mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
add actual components folder
This commit is contained in:
56
components/assets/icons.html
Normal file
56
components/assets/icons.html
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
<svg><defs>
|
||||
<g id="apps"><path d="M4,8h4V4H4V8z M10,20h4v-4h-4V20z M4,20h4v-4H4V20z M4,14h4v-4H4V14z M10,14h4v-4h-4V14z M16,4v4h4V4H16z M10,8h4V4h-4V8z M16,14h4v-4h-4V14z M16,20h4v-4h-4V20z"/></g>
|
||||
<g id="archive"><path d="M20.5,5.2l-1.4-1.7C18.9,3.2,18.5,3,18,3H6C5.5,3,5.1,3.2,4.8,3.5L3.5,5.2C3.2,5.6,3,6,3,6.5V19c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V6.5C21,6,20.8,5.6,20.5,5.2z M12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5z M5.1,5l0.8-1h12l0.9,1H5.1z"/></g>
|
||||
<g id="check-box-outline-blank"><path d="M19,5v14L5,19V5H19 M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3L19,3z"/></g>
|
||||
<g id="check-box-outline"><path d="M7.9,10.1l-1.4,1.4L11,16L21,6l-1.4-1.4L11,13.2L7.9,10.1z M19,19L5,19V5h10V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2v-8h-2V19z"/></g>
|
||||
<g id="arrow-drop-down"><polygon points="7,10 12,15 17,10 "/></g>
|
||||
<g id="info-outline"><path d="M11,17h2v-6h-2V17z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z M11,9h2V7h-2V9z"/></g>
|
||||
<g id="search"><path d="M15.5,14h-0.8l-0.3-0.3c1-1.1,1.6-2.6,1.6-4.2C16,5.9,13.1,3,9.5,3C5.9,3,3,5.9,3,9.5S5.9,16,9.5,16c1.6,0,3.1-0.6,4.2-1.6l0.3,0.3v0.8l5,5l1.5-1.5L15.5,14z M9.5,14C7,14,5,12,5,9.5S7,5,9.5,5C12,5,14,7,14,9.5S12,14,9.5,14z"/></g>
|
||||
<g id="arrow-back"><path d="M20,11H7.8l5.6-5.6L12,4l-8,8l8,8l1.4-1.4L7.8,13H20V11z"/></g>
|
||||
<g id="schedule"><path fill-opacity="0.9" d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/><polygon fill-opacity="0.9" points="12.5,7 11,7 11,13 16.2,16.2 17,14.9 12.5,12.2 "/></g>
|
||||
<g id="close"><polygon points="19,6.4 17.6,5 12,10.6 6.4,5 5,6.4 10.6,12 5,17.6 6.4,19 12,13.4 17.6,19 19,17.6 13.4,12 "/></g>
|
||||
<g id="create"><path d="M3,17.2V21h3.8L17.8,9.9l-3.8-3.8L3,17.2z M20.7,7c0.4-0.4,0.4-1,0-1.4l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0l-1.8,1.8l3.8,3.8L20.7,7z"/></g>
|
||||
<g id="settings"><path d="M19.4,13c0-0.3,0.1-0.6,0.1-1s0-0.7-0.1-1l2.1-1.7c0.2-0.2,0.2-0.4,0.1-0.6l-2-3.5C19.5,5.1,19.3,5,19,5.1l-2.5,1c-0.5-0.4-1.1-0.7-1.7-1l-0.4-2.6C14.5,2.2,14.2,2,14,2h-4C9.8,2,9.5,2.2,9.5,2.4L9.1,5.1C8.5,5.3,8,5.7,7.4,6.1L5,5.1C4.7,5,4.5,5.1,4.3,5.3l-2,3.5C2.2,8.9,2.3,9.2,2.5,9.4L4.6,11c0,0.3-0.1,0.6-0.1,1s0,0.7,0.1,1l-2.1,1.7c-0.2,0.2-0.2,0.4-0.1,0.6l2,3.5C4.5,18.9,4.7,19,5,18.9l2.5-1c0.5,0.4,1.1,0.7,1.7,1l0.4,2.6c0,0.2,0.2,0.4,0.5,0.4h4c0.2,0,0.5-0.2,0.5-0.4l0.4-2.6c0.6-0.3,1.2-0.6,1.7-1l2.5,1c0.2,0.1,0.5,0,0.6-0.2l2-3.5c0.1-0.2,0.1-0.5-0.1-0.6L19.4,13z M12,15.5c-1.9,0-3.5-1.6-3.5-3.5s1.6-3.5,3.5-3.5s3.5,1.6,3.5,3.5S13.9,15.5,12,15.5z"/></g>
|
||||
<g id="delete"><path d="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M19,4h-3.5l-1-1h-5l-1,1H5v2h14V4z"/></g>
|
||||
<g id="menu"><path d="M3,18h18v-2H3V18z M3,13h18v-2H3V13z M3,6v2h18V6H3z"/></g>
|
||||
</defs></svg>
|
||||
|
||||
<script>
|
||||
|
||||
var doc = window.import;
|
||||
|
||||
var cloneIcon = function(id, size) {
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
var icon = doc.querySelector(id);
|
||||
if (icon) {
|
||||
svg.style.pointerEvents = 'none';
|
||||
svg.style.display = 'block';
|
||||
svg.setAttribute('height', '100%');
|
||||
svg.setAttribute('width', '100%');
|
||||
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
||||
svg.setAttribute('viewBox', '0 0 24 24');
|
||||
svg.appendChild(icon.cloneNode(true));
|
||||
}
|
||||
return svg;
|
||||
};
|
||||
|
||||
var icons = {
|
||||
apps: cloneIcon('#apps'),
|
||||
archive: cloneIcon('#archive'),
|
||||
box: cloneIcon('#check-box-outline-blank'),
|
||||
checked: cloneIcon('#check-box-outline'),
|
||||
drop: cloneIcon('#arrow-drop-down'),
|
||||
'info-outline': cloneIcon('#info-outline'),
|
||||
search: cloneIcon('#search'),
|
||||
back: cloneIcon('#arrow-back'),
|
||||
schedule: cloneIcon('#schedule'),
|
||||
create: cloneIcon('#create'),
|
||||
close: cloneIcon('#close'),
|
||||
delete: cloneIcon('#delete'),
|
||||
schedule: cloneIcon('#schedule'),
|
||||
settings: cloneIcon('#settings'),
|
||||
menu: cloneIcon('#menu')
|
||||
};
|
||||
|
||||
</script>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
252
components/perf-lib/frame-tester.html
Normal file
252
components/perf-lib/frame-tester.html
Normal file
@@ -0,0 +1,252 @@
|
||||
<link rel="import" href="polymer/polymer.html">
|
||||
|
||||
<template>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
iframe {
|
||||
position: absolute;
|
||||
border: 0;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
visibility: hidden;
|
||||
xdisplay: none;
|
||||
}
|
||||
|
||||
o, n {
|
||||
display: inline-block;
|
||||
xwidth: 24px;
|
||||
margin: 2px;
|
||||
text-align: right;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
o {
|
||||
color: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
n {
|
||||
display: inline-block;
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div></div>
|
||||
|
||||
<iframe id="frame"></iframe>
|
||||
|
||||
</template><script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'frame-tester',
|
||||
base: '',
|
||||
runs: 25,
|
||||
|
||||
published: [
|
||||
'tests'
|
||||
],
|
||||
|
||||
created: function() {
|
||||
this.frame = this.$('#frame');
|
||||
this.strategy = this.strategies.minimum;
|
||||
this.attributeChangedCallback('runs');
|
||||
this.attributeChangedCallback('base');
|
||||
addEventListener('message', this.scoreMessage.bind(this));
|
||||
},
|
||||
|
||||
attributeChangedCallback: function(name) {
|
||||
var value = this.getAttribute(name);
|
||||
switch(name) {
|
||||
case 'runs':
|
||||
this.runs = value || this.runs;
|
||||
break;
|
||||
case 'base':
|
||||
this.base = value || this.base;
|
||||
break;
|
||||
case 'strategy':
|
||||
this.strategy = this.strategies[strategy] || this.strategy;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
testsChanged: function() {
|
||||
this.go();
|
||||
},
|
||||
|
||||
shuffle: function(tests) {
|
||||
var shuffled = [];
|
||||
var ordered = tests.slice(0);
|
||||
var count = ordered.length;
|
||||
for (var i=0, j; i<count; i++) {
|
||||
j = Math.floor(Math.random()*count);
|
||||
// TODO(sjmiles): this is an easy but poorly randomized distribution
|
||||
for (; !ordered[j]; j = (j + 1) % count);
|
||||
shuffled.push(j);
|
||||
ordered[j] = null;
|
||||
}
|
||||
return shuffled;
|
||||
},
|
||||
|
||||
go: function() {
|
||||
this.count = 0;
|
||||
this.total = [];
|
||||
this.times = [];
|
||||
for (var i=0; i<this.tests.length; i++) {
|
||||
this.total[i] = 0;
|
||||
this.times[i] = [];
|
||||
}
|
||||
this.startRun();
|
||||
},
|
||||
|
||||
startRun: function() {
|
||||
this.shuffled = this.shuffle(this.tests);
|
||||
this.index = -1;
|
||||
//console.group('run', this.count);
|
||||
this.nextTest();
|
||||
},
|
||||
|
||||
nextTest: function() {
|
||||
// last test in this run?
|
||||
if (++this.index === this.tests.length) {
|
||||
//console.groupEnd();
|
||||
// report results
|
||||
++this.count;
|
||||
this.report();
|
||||
// more runs?
|
||||
if (this.count < this.runs) {
|
||||
this.startRun();
|
||||
} else {
|
||||
// all done!
|
||||
this.fire('done');
|
||||
}
|
||||
return;
|
||||
}
|
||||
// test order is randomized
|
||||
this.test = this.shuffled[this.index];
|
||||
this.frame.src = this.base + this.tests[this.test];
|
||||
// it's possible for a test to end before the load event fires,
|
||||
// so assume the frame loads immediately and start waiting
|
||||
// for a result.
|
||||
this.load();
|
||||
},
|
||||
|
||||
load: function() {
|
||||
// frame is loaded, measure the time, then proceed
|
||||
this.measure(function(time) {
|
||||
this.record(time);
|
||||
this.nextTest();
|
||||
});
|
||||
},
|
||||
|
||||
measure: function(next) {
|
||||
this.afterScore = next;
|
||||
},
|
||||
|
||||
scoreMessage: function(e) {
|
||||
if (this.afterScore) {
|
||||
var time = e.data;
|
||||
if (time.slice(-2) === 'ms') {
|
||||
time = Number(time.slice(0, -2));
|
||||
this.afterScore(time);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
record: function(time) {
|
||||
//console.log('index [%d], test [%d] time [%d]', this.index, this.test, time);
|
||||
this.times[this.test].push(time);
|
||||
this.total[this.test] += time;
|
||||
},
|
||||
|
||||
report: function() {
|
||||
var info = '<br>Runs: ' + this.count + '/' + this.runs
|
||||
+ '<br><br>';
|
||||
//
|
||||
for (var i=0; i<this.tests.length; i++) {
|
||||
var url = this.tests[i],
|
||||
total = this.total[i],
|
||||
times = this.times[i],
|
||||
stats = this.stats(times);
|
||||
//
|
||||
var time = this.strategy.score(times, stats);
|
||||
//
|
||||
info += ''
|
||||
+ ' <b>' + (time).toFixed(1) + 'ms' + '</b>'
|
||||
//+ ' [stddev: ' + stats.deviation.toFixed(2) + ']'
|
||||
+ ' <a href="' + this.base + url + '" target="_blank">' + url + '</a>'
|
||||
+ '<br>'
|
||||
;
|
||||
//
|
||||
info += '<span style="font-size: 8px">';
|
||||
for (var j=0, v; v=times[j]; j++) {
|
||||
var o = stats.outlier(v);
|
||||
info += (o ? '<o>' : '<n>') + v.toFixed(0) + (o ? '</o>' : '</n>') + '|';
|
||||
}
|
||||
info += '</span>';
|
||||
info += '<hr>';
|
||||
}
|
||||
//
|
||||
this.$('div').innerHTML = info;
|
||||
},
|
||||
|
||||
stats: function(a) {
|
||||
var r = {mean: 0, variance: 0, deviation: 0}, t = a.length;
|
||||
for (var m, s = 0, l = t; l--; s += a[l]);
|
||||
for (m = r.mean = s / t, l = t, s = 0; l--; s += Math.pow(a[l] - m, 2));
|
||||
r.outlier = this.strategy.outlier;
|
||||
return r.deviation = Math.sqrt(r.variance = s / t), r;
|
||||
},
|
||||
|
||||
//
|
||||
// selectable statical strategies
|
||||
//
|
||||
|
||||
strategies: {
|
||||
|
||||
// This strategy selects the minimum timing for score.
|
||||
minimum: {
|
||||
score: function(times, stats) {
|
||||
var min = Number.MAX_VALUE;
|
||||
for (var j=0, v; v=times[j]; j++) {
|
||||
min = Math.min(v, min);
|
||||
}
|
||||
stats.score = min;
|
||||
return min;
|
||||
},
|
||||
// called in stats context
|
||||
outlier: function(value) {
|
||||
return value > this.score;
|
||||
}
|
||||
},
|
||||
|
||||
// This strategy selects the mean of all times not more than one stddev
|
||||
// away from the total sampling mean.
|
||||
onedev: {
|
||||
score: function(times, stats) {
|
||||
var cleaned = [];
|
||||
for (var j=0, v; v=times[j]; j++) {
|
||||
if (!stats.outlier(v)) {
|
||||
cleaned.push(v);
|
||||
}
|
||||
}
|
||||
return this.stats(cleaned).mean;
|
||||
},
|
||||
// called in stats context
|
||||
outlier: function(value) {
|
||||
return Math.abs(value - this.mean) > (1 * this.deviation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
40
components/perf-lib/perf.js
Normal file
40
components/perf-lib/perf.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// x-browser compat.
|
||||
if (!window.performance) {
|
||||
var start = Date.now();
|
||||
// only at millisecond precision
|
||||
window.performance = {now: function(){ return Date.now() - start }};
|
||||
}
|
||||
|
||||
console.perf = function() {
|
||||
if (console.timeline) {
|
||||
console.timeline();
|
||||
}
|
||||
console.profile();
|
||||
console.perf.time = performance.now();
|
||||
};
|
||||
|
||||
console.perfEnd = function() {
|
||||
if (window.WebComponents) {
|
||||
// TODO(sjmiles): we need some kind of 'whenReady' or other signal
|
||||
// that will work if this function is called after the event has fired
|
||||
addEventListener('WebComponentsReady', function() {
|
||||
console._perfEnd();
|
||||
});
|
||||
} else {
|
||||
console._perfEnd();
|
||||
}
|
||||
};
|
||||
|
||||
console._perfEnd = function() {
|
||||
// force layout
|
||||
document.body.offsetWidth;
|
||||
var time = performance.now() - console.perf.time;
|
||||
console.profileEnd();
|
||||
if (console.timeline) {
|
||||
console.timelineEnd();
|
||||
}
|
||||
document.title = time.toFixed(1) + 'ms: ' + document.title;
|
||||
if (window.top !== window) {
|
||||
window.top.postMessage(time + 'ms', '*');
|
||||
}
|
||||
};
|
||||
126
components/perf-lib/polymer/annotations.html
Normal file
126
components/perf-lib/polymer/annotations.html
Normal file
@@ -0,0 +1,126 @@
|
||||
<script>
|
||||
|
||||
/*
|
||||
|
||||
Scans a template to produce an annotation map that stores expression metadata
|
||||
and information that can be used to associate that metadata with the
|
||||
corresponding nodes in a template instance.
|
||||
|
||||
Supported annotations are:
|
||||
|
||||
* binding annotations in text nodes
|
||||
* double-mustache expressions: {{expression}}
|
||||
* double-bracket expressions: [[expression]]
|
||||
* binding annotations in attributes
|
||||
* attribute-bind expressions: name={{expression}} || [[expression]]
|
||||
* property-bind expressions: name*={{expression}} || [[expression]]
|
||||
* event annotations
|
||||
* event delegation directives: on-<eventName>="expression"
|
||||
|
||||
Generated data-structure:
|
||||
|
||||
[
|
||||
{
|
||||
bindings: [
|
||||
{
|
||||
kind: ['event'|'text'|'attribute'|'property'],
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}
|
||||
],
|
||||
// TODO(sjmiles): confusingly, this is annotation-parent, not node-parent
|
||||
parent: <reference to parent annotation>,
|
||||
index: <integer index in parent's childNodes collection>
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
TODO(sjmiles): this module should produce either syntactic metadata
|
||||
(e.g. double-mustache, double-bracket, star-attr), or semantic metadata
|
||||
(e.g. manual-bind, auto-bind, property-bind). Right now it's half and half.
|
||||
|
||||
*/
|
||||
|
||||
Base.features.push({
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
return !annote.parent ? root :
|
||||
this.findAnnotatedNode(root, annote.parent).childNodes[annote.index];
|
||||
},
|
||||
parseTemplateAnnotations: function(template) {
|
||||
// TODO(sjmiles): it's not a map, per se
|
||||
var map = [];
|
||||
this._parseTemplateNode(template.content, map);
|
||||
if (map.length) {
|
||||
template.map = map;
|
||||
}
|
||||
},
|
||||
_parseTemplateNode: function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ?
|
||||
this._parseTemplateTextNode(node, map) :
|
||||
this._parseTemplateElement(node, map);
|
||||
},
|
||||
_parseTemplateTextNode: function(node, map) {
|
||||
var v = node.textContent, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var annotation = {
|
||||
bindings: [{
|
||||
kind: 'text',
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
value: v.slice(2, -2)
|
||||
}]
|
||||
};
|
||||
map.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
},
|
||||
_parseTemplateElement: function(node, map) {
|
||||
var annotations = {
|
||||
bindings: []
|
||||
};
|
||||
this._parseTemplateNodeAnnotations(node, annotations, map);
|
||||
this._parseTemplateChildNodes(node, annotations, map);
|
||||
if (annotations.bindings.length) {
|
||||
map.push(annotations);
|
||||
}
|
||||
return annotations;
|
||||
},
|
||||
_parseTemplateNodeAnnotations: function(node, annotation) {
|
||||
if (node.attributes) {
|
||||
for (var i=0, a; (a=node.attributes[i]); i++) {
|
||||
var n = a.name, v = a.value, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var kind = (n[n.length-1]) === '*' ? 'property' : 'attribute';
|
||||
if (kind === 'property') {
|
||||
n = n.slice(0, -1);
|
||||
}
|
||||
annotation.bindings.push({
|
||||
kind: kind,
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
name: n,
|
||||
value: v.slice(2, -2)
|
||||
});
|
||||
} else if (n.slice(0, 3) === 'on-') {
|
||||
annotation.bindings.push({
|
||||
kind: 'event',
|
||||
name: n.slice(3),
|
||||
value: v
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_parseTemplateChildNodes: function(root, annotations, map) {
|
||||
if (root.firstChild) {
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var annotation = this._parseTemplateNode(node, map);
|
||||
if (annotation) {
|
||||
annotation.parent = annotations;
|
||||
annotation.index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
82
components/perf-lib/polymer/base.html
Normal file
82
components/perf-lib/polymer/base.html
Normal file
@@ -0,0 +1,82 @@
|
||||
<script>
|
||||
|
||||
var attributes = function(node, attr$) {
|
||||
attr$.split(' ').forEach(function(a) {
|
||||
node.setAttribute(a, '');
|
||||
});
|
||||
};
|
||||
|
||||
Base = {
|
||||
// (semi-)pluggable features for Base
|
||||
features: [],
|
||||
installFeatures: function() {
|
||||
// simple engine to modularize features
|
||||
Base.features.forEach(function(f) {
|
||||
extend(Base, f);
|
||||
});
|
||||
delete Base.init;
|
||||
delete Base.installFeatures;
|
||||
},
|
||||
registerCallback: function() {
|
||||
// context is a prototype, not an instance
|
||||
var prototype = this;
|
||||
prototype._template = window.import.querySelector('template');
|
||||
if (prototype._template) {
|
||||
// requires annotations feature
|
||||
prototype.parseTemplateAnnotations(prototype._template);
|
||||
}
|
||||
// make a list of names that have properties of the form <name>Changed.
|
||||
// DataClient can use this list to efficiently associate *Changed
|
||||
// methods with published properties.
|
||||
// TODO(sjmiles): maybe remove this feature completely in favor of
|
||||
// explicit annotations in the prototype.
|
||||
//prototype.precomputeNotifierCandidates();
|
||||
},
|
||||
createdCallback: function() {
|
||||
this.initFeatures();
|
||||
this.createRoot();
|
||||
//if (!this.deferStamp) {
|
||||
this.stampTemplate();
|
||||
//}
|
||||
this.installAttributes();
|
||||
this.created();
|
||||
// requires bindings feature
|
||||
this.updateBindings();
|
||||
},
|
||||
created: function() {
|
||||
},
|
||||
initFeatures: function() {
|
||||
Base.features.forEach(function(f) {
|
||||
f.init && f.init.call(this);
|
||||
}, this);
|
||||
},
|
||||
//
|
||||
// TODO(sjmiles): all the following should be 'features'?
|
||||
//
|
||||
createRoot: function(){
|
||||
// TODO(sjmiles): ad hoc to switch on _template existence here
|
||||
this.root = this._template ? this.createShadowRoot() : this;
|
||||
},
|
||||
stampTemplate: function() {
|
||||
if (this._template && !this._stamped) {
|
||||
this.root.appendChild(this.instanceTemplate(this._template));
|
||||
this._stamped = true;
|
||||
// requires bindings feature
|
||||
this.marshalBindings();
|
||||
// TODO(sjmiles): we don't necessarily want to do this here
|
||||
//if (this.deferBindings) {
|
||||
// this.updateBindings();
|
||||
//}
|
||||
}
|
||||
},
|
||||
installAttributes: function() {
|
||||
if (this._attributes) {
|
||||
attributes(this, this._attributes);
|
||||
}
|
||||
},
|
||||
instanceTemplate: function(template) {
|
||||
return document.importNode(template.content, true);
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
147
components/perf-lib/polymer/bindings.html
Normal file
147
components/perf-lib/polymer/bindings.html
Normal file
@@ -0,0 +1,147 @@
|
||||
<script>
|
||||
|
||||
//
|
||||
// `marshalBindings` consumes template annotations to:
|
||||
//
|
||||
// 1. set up event delegation machinery
|
||||
// 2. set up binding machinery and instance data
|
||||
//
|
||||
// `updateBindings` can be called to perform propagation of model data to DOM
|
||||
// on demand
|
||||
|
||||
Base.features.push({
|
||||
init: function() {
|
||||
this._model = this;
|
||||
this._bindings = [];
|
||||
},
|
||||
// instance binding data
|
||||
marshalBindings: function() {
|
||||
if (this._template.map) {
|
||||
this._template.map.forEach(function(annotation) {
|
||||
// locate instance node
|
||||
var node = this.findAnnotatedNode(this.root, annotation);
|
||||
// implement data-binding on the instance node
|
||||
this.marshalNodeBindings(node, annotation);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
// TODO(sjmiles): most of these methods should probably be prefixed with _
|
||||
marshalNodeBindings: function(node, annotation) {
|
||||
for (var i=0, fn, b; b=annotation.bindings[i]; i++) {
|
||||
if (b.kind === 'event') {
|
||||
this.implementListener(node, b.name, b.value);
|
||||
} else {
|
||||
this._bindings.push({
|
||||
node: node,
|
||||
property: b.value,
|
||||
fn: this.implementBinding(node, b.kind, b.name, b.value, b.mode)
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
implementListener: function(node, name, method) {
|
||||
// TODO(sjmiles): perhaps implementListener should
|
||||
// be factored even further (i.e. into a separate module/feature?)
|
||||
// because it's conceptually separable from the details of template
|
||||
// annotation processing.
|
||||
var ctrlr = this;
|
||||
node.addEventListener(name, function(e) {
|
||||
if (ctrlr[method]) {
|
||||
Polymer.log.events && console.log('[event-binding]: %s -> %s.%s', name, ctrlr.localName, method)
|
||||
ctrlr[method](e, e.detail);
|
||||
}
|
||||
});
|
||||
},
|
||||
implementBinding: function(node, kind, name, property, mode) {
|
||||
// TODO(sjmiles): perhaps implementBinding should
|
||||
// be factored even further (i.e. into a separate module/feature?)
|
||||
// because it's conceptually separable from the details of template
|
||||
// annotation processing.
|
||||
switch(kind) {
|
||||
case 'text':
|
||||
// TODO(sjmiles): baby-step toward path support
|
||||
// - only text bindings
|
||||
// - support exactly one dot (foo.bar)
|
||||
// - generated fn binds to this, derefs path from model
|
||||
// (ignores argument)
|
||||
/*
|
||||
if (property.indexOf('.') >= 0) {
|
||||
var path = property.split('.');
|
||||
var object = path[0];
|
||||
var property = path[1];
|
||||
// TODO(sjmiles): tortured attempt to avoid nerfing performance
|
||||
// - binder does nothing until target object becomes available
|
||||
// then memoizes it for all time
|
||||
var work = function() {
|
||||
var obj = this[object];
|
||||
if (obj) {
|
||||
object = obj;
|
||||
work = postWork;
|
||||
work();
|
||||
}
|
||||
}.bind(this);
|
||||
var postWork = function() {
|
||||
node.data = object[property];
|
||||
};
|
||||
var fn = function() {
|
||||
work();
|
||||
};
|
||||
//console.log('[implementBinding]: path detected: [%s]', property);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
var fn = function(value) {
|
||||
node.data = value;
|
||||
};
|
||||
break;
|
||||
case 'attribute':
|
||||
var fn = function(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
node[value ? 'setAttribute' : 'removeAttribute'](name, '');
|
||||
} else {
|
||||
node.setAttribute(name, value);
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'property':
|
||||
// this is a fourth type of binding that utilizes shared storage to
|
||||
// avoid the need to observe and propagate changes
|
||||
// TODO(sjmiles): ad-hoc DataClient detection
|
||||
if (node._data && (name in node._data)) {
|
||||
this.publishProperty(property);
|
||||
this.bindProperty(property, name, node);
|
||||
return null;
|
||||
}
|
||||
// generic property binding
|
||||
var fn = function(value) {
|
||||
node[name] = value;
|
||||
};
|
||||
break;
|
||||
}
|
||||
if (mode === 'auto') {
|
||||
// create an observable property
|
||||
this.publishProperty(property);
|
||||
// update the binding when changes are observed
|
||||
this.watch(property, fn);
|
||||
}
|
||||
return fn;
|
||||
},
|
||||
updateBindings: function() {
|
||||
var model = this._model;
|
||||
this._bindings.forEach(function(b) {
|
||||
// TODO(sjmiles): the `fn` are structured so as to be called directly
|
||||
// from `watch` which supplies a `value` argument. We would instead
|
||||
// compose the `fn` to include the model dereference instead of taking
|
||||
// an argument.
|
||||
// The conceptual propriety is unclear to me at the moment.
|
||||
// Needing to encode `property` in `b` is a cost to the current method.
|
||||
// Doing an extra lookup in `fn` when called from watch (or having a
|
||||
// conditional to avoid doing that) would be a cost the other way.
|
||||
if (b.fn) {
|
||||
b.fn(model[b.property]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
322
components/perf-lib/polymer/data.html
Normal file
322
components/perf-lib/polymer/data.html
Normal file
@@ -0,0 +1,322 @@
|
||||
<script>
|
||||
|
||||
// TODO(sjmiles): when binding properties that are themselves bound,
|
||||
// we need to do a merge, which increases the complexity considerably.
|
||||
// Is there a different way to frame this problem?
|
||||
|
||||
// TODO(sjmiles): implement dispose
|
||||
|
||||
/*
|
||||
* Datum wraps a concrete data value and provides:
|
||||
*
|
||||
* - persistent shareable reference
|
||||
* - synchronous notifications on mutations (via method-traps)
|
||||
*
|
||||
* Multiple DataClients can refer to a single Datum instance. If any of the
|
||||
* clients change the concrete value, the new value is immediately available
|
||||
* to all the others (single source of truth).
|
||||
*
|
||||
* e.g. given
|
||||
* foo.datum = datum1
|
||||
* bar.datum = datum1
|
||||
* then obviously:
|
||||
* foo.datum.value == bar.datum.value
|
||||
*
|
||||
* But now foo and bar can construct accessors such that:
|
||||
* foo.baz => get() { return this.datum.value };
|
||||
* bar.zot => get() { return this.datum.value };
|
||||
* now, less obviously:
|
||||
* foo.baz === bar.zot
|
||||
*
|
||||
* This is the data-binding gambit.
|
||||
*
|
||||
* The `watch` method registers a callback function to be invoked
|
||||
* synchronously on data mutations.
|
||||
*
|
||||
* Watch callbacks are triggered when:
|
||||
* - data is changed via setValue
|
||||
* - any of various Array methods are called value (when value is Array)
|
||||
* - `notify` method is called directly
|
||||
*
|
||||
* `merge` method collapses a DataClient into this one, combining
|
||||
* all watchers and referers.
|
||||
*/
|
||||
function Datum(value) {
|
||||
this.value = value;
|
||||
// we require back references so we can merge datums
|
||||
// back references will require dispose pattern for GC,
|
||||
// but then we already have this problem wrt watchers
|
||||
// a 'referer' must be a DataClient
|
||||
this.referers = [];
|
||||
}
|
||||
|
||||
Datum.prototype = {
|
||||
addReferer: function(client, name) {
|
||||
// TODO(sjmiles): `client` has to be a DataClient right now, could be
|
||||
// generalized.
|
||||
// TODO(sjmiles): storing this data seems wrong, the Datum doesn't need
|
||||
// to know these details except for merging. Perhaps we should take
|
||||
// a function argument, and let the caller supply a closure with the
|
||||
// necessary bookkeeping.
|
||||
this.referers.push({client: client, name: name});
|
||||
},
|
||||
watch: function(notifier) {
|
||||
if (!this.watchers) {
|
||||
this.watchers = [];
|
||||
}
|
||||
this.watchers.push(notifier);
|
||||
},
|
||||
// TODO(sjmiles): use accessors?
|
||||
// TODO(sjmiles): factor out Array decoration
|
||||
setValue: function(value) {
|
||||
var old = this.value;
|
||||
if (old !== value) {
|
||||
// TODO(sjmiles): experimental
|
||||
if (value instanceof Array) {
|
||||
//console.log('decorating Array');
|
||||
// TODO(sjmiles): how costly is this?
|
||||
extend(value, ArrayTrap);
|
||||
// TODO(sjmiles): binding client this way is problematic
|
||||
value.client = this;
|
||||
}
|
||||
this.value = value;
|
||||
this.notify(old);
|
||||
}
|
||||
},
|
||||
notify: function(old) {
|
||||
if (this.watchers) {
|
||||
var value = this.value;
|
||||
this.watchers.forEach(function(w) {
|
||||
w(value, old);
|
||||
});
|
||||
}
|
||||
},
|
||||
merge: function(other) {
|
||||
// transfer all watchers of `other` to `this`
|
||||
if (other.watchers) {
|
||||
this.watchers = (this.watchers || []).concat(other.watchers);
|
||||
other.watchers = null;
|
||||
}
|
||||
// redirect all referers of `other` to `this`
|
||||
var referers = other.referers;
|
||||
if (referers.length) {
|
||||
referers.forEach(function(r) {
|
||||
r.client._refer(r.name, this);
|
||||
}, this);
|
||||
other.referers = null;
|
||||
}
|
||||
// `other` should be free to GC at this point
|
||||
}
|
||||
};
|
||||
|
||||
ArrayTrap = Object.create(null);
|
||||
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(function(n) {
|
||||
ArrayTrap[n] = function() {
|
||||
console.log('Array.%s notify', n);
|
||||
Array.prototype[n].apply(this, arguments);
|
||||
this.client.notify(this);
|
||||
};
|
||||
});
|
||||
|
||||
/*
|
||||
* DataClient implements a data-store whose properties can be:
|
||||
*
|
||||
* - shared among DataClients (aka _bound_)
|
||||
* - observed for mutations
|
||||
* - computed from dependent properties
|
||||
*
|
||||
* `Publishing` a property on a DataClient:
|
||||
*
|
||||
* - creates a Datum instance mapped to the property name
|
||||
* - installs (as needed) get/set accessors (on the prototype) for the
|
||||
* property name that reference the mapped Datum instance.
|
||||
* - if a method called `<name>Changed` exists, use this
|
||||
* method as a `watch` callback.
|
||||
*
|
||||
* A sugaring system is implemented to set up relationships at
|
||||
* init-time from meta-data declarations on the instance.
|
||||
*
|
||||
* // `published` identifies property names to publish at init-time.
|
||||
* // TODO(sjmiles): use a map to support initial values
|
||||
* published: [
|
||||
* 'foo',
|
||||
* 'bar'
|
||||
* ],
|
||||
* // `watched` object maps properties to names of methods to use for `watch`
|
||||
* // callbacks. Compound watches are supported.
|
||||
* published: [
|
||||
* foo: 'doUpdates',
|
||||
* 'foo bar baz': 'otherUpdates'
|
||||
* ],
|
||||
* // `computed` object maps properties to faux method-invocations. The
|
||||
* // right-side is parsed to construct a CompoundWatch whose notify callback
|
||||
* // computes the value of the left-side.
|
||||
* computed: [
|
||||
* foo: 'computeFoo(bar)',
|
||||
* baz: 'computeBaz(foo, zot, narg)'
|
||||
* ]
|
||||
*
|
||||
*/
|
||||
DataClient = {
|
||||
published: [],
|
||||
watched: {},
|
||||
computed: {},
|
||||
init: function() {
|
||||
this._data = {};
|
||||
this.publishProperties(this.published);
|
||||
this.watchProperties(this.watched);
|
||||
this.defineComputedProperties(this.computed);
|
||||
},
|
||||
publishProperties: function(properties) {
|
||||
properties.forEach(this.publishProperty, this)
|
||||
},
|
||||
publishProperty: function(property) {
|
||||
this._defineProperty(property);
|
||||
// auto-watch feature
|
||||
// TODO(sjmiles): consider eliminating this feature in favor
|
||||
// of explicit annotation
|
||||
var watchName = property + 'Changed';
|
||||
/*
|
||||
if (this._hasNotifier) {
|
||||
if (!this._hasNotifier[property]) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
*/
|
||||
// TODO(sjmiles): either test is extremely slow
|
||||
// TODO(sjmiles): slowness of this test was exaggerated by red-herring:
|
||||
// some last-gen elements (x-repeater) binding *Changed methods manually
|
||||
// causing double updates.
|
||||
//if (!(watchName in this)) {
|
||||
if (!this[watchName]) {
|
||||
return;
|
||||
}
|
||||
//}
|
||||
this.watch(property, this[watchName]);
|
||||
},
|
||||
precomputeNotifierCandidates: function() {
|
||||
// TODO(sjmiles): implemented due to bogus results as described above
|
||||
/*
|
||||
this._hasNotifier = {};
|
||||
// TODO(sjmiles): seems simple, fraught with performance danger
|
||||
for (var n in this) {
|
||||
if (n.slice(-7) === 'Changed') {
|
||||
this._hasNotifier[n.slice(0, -7)] = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
},
|
||||
notifyChange: function(name) {
|
||||
var datum = this._data[name];
|
||||
datum.notify(datum.value);
|
||||
},
|
||||
watch: function(name, notify) {
|
||||
this._data[name].watch(notify.bind(this));
|
||||
},
|
||||
watchProperties: function(properties) {
|
||||
for (var n in properties) {
|
||||
this.compoundWatch(n.split(' '), this[properties[n]]);
|
||||
}
|
||||
},
|
||||
defineComputedProperties: function(computed) {
|
||||
for (var n in computed) {
|
||||
this.defineComputedProperty(n, computed[n]);
|
||||
}
|
||||
},
|
||||
defineComputedProperty: function(name, expression) {
|
||||
this.publishProperty(name);
|
||||
var parts = expression.match(/(.*)\((.*)\)/);
|
||||
var method = this[parts[1]];
|
||||
var args = parts[2].replace(/ /g, '').split(',');
|
||||
this.compoundWatch(args, function() {
|
||||
Polymer.log.watches && console.log('[defineComputedProperty]: computing [%s]', name, arguments);
|
||||
this[name] = method.apply(this, arguments);
|
||||
});
|
||||
},
|
||||
compoundWatch: function(names, notify) {
|
||||
// TODO(sjmiles): needs factoring
|
||||
//
|
||||
// fallback to normal watch if we are not truly compound
|
||||
if (names.length === 1) {
|
||||
this.watch(names[0], notify);
|
||||
return;
|
||||
}
|
||||
var debouncing = false,
|
||||
data = this._data,
|
||||
async = this.async.bind(this),
|
||||
client = this
|
||||
;
|
||||
names.forEach(function(n) {
|
||||
data[n].watch(function(value) {
|
||||
Polymer.log.watches && console.log('[compoundWatch]: debounce [%s]', n);
|
||||
if (!debouncing) {
|
||||
debouncing = true;
|
||||
// TODO(sjmiles): if a property in `name` is itself computed
|
||||
// we fail to debounce properly.
|
||||
// I don't see how we can debounce properly unless we study
|
||||
// the dependency graph. :(
|
||||
async(function() {
|
||||
debouncing = false;
|
||||
var args = [];
|
||||
names.forEach(function(n) {
|
||||
args.push(client[n])
|
||||
});
|
||||
Polymer.log.watches && console.log('[compoundWatch]: async-notify ', names, args);
|
||||
notify.apply(client, args);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// cause this[name] and target[targetName] to refer to the same datum
|
||||
// TODO(sjmiles): change call signature to (name, target[, targetName])
|
||||
bindProperty: function(name, targetName, target) {
|
||||
//
|
||||
// TODO(sjmiles): which datum is 'old' and which is 'new' is arbitrary atm
|
||||
// (one value is discarded)
|
||||
//
|
||||
// merge old into new
|
||||
target._data[targetName].merge(this._data[name], name);
|
||||
},
|
||||
// TODO(sjmiles): general-purpose utility method should be implemented
|
||||
// elsewhere?
|
||||
async: function(method) {
|
||||
var handled = false;
|
||||
var handle = function() {
|
||||
if (!handled) {
|
||||
handled = true;
|
||||
method.call(this);
|
||||
}
|
||||
}.bind(this);
|
||||
// minimize latency by racing requests
|
||||
setTimeout(handle);
|
||||
requestAnimationFrame(handle);
|
||||
},
|
||||
_defineProperty: function(name) {
|
||||
if (!(name in this._data)) {
|
||||
this._refer(name, new Datum(null));
|
||||
// install accessors on our prototype (if needed)
|
||||
Object.getPrototypeOf(this)._defineAccessors(name);
|
||||
}
|
||||
},
|
||||
_defineAccessors: function(name) {
|
||||
if (!this.hasOwnProperty(name)) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function() {
|
||||
return this._data[name].value;
|
||||
},
|
||||
set: function(value) {
|
||||
this._data[name].setValue(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
_refer: function(name, datum) {
|
||||
this._data[name] = datum;
|
||||
datum.addReferer(this, name);
|
||||
}
|
||||
};
|
||||
|
||||
Base.features.push(DataClient);
|
||||
|
||||
</script>
|
||||
38
components/perf-lib/polymer/events.html
Normal file
38
components/perf-lib/polymer/events.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<script>
|
||||
|
||||
Base.features.push({
|
||||
listeners: {},
|
||||
init: function() {
|
||||
this.listenListeners();
|
||||
},
|
||||
listenListeners: function() {
|
||||
for (var n in this.listeners) {
|
||||
this.listen(n, this.listeners[n]);
|
||||
}
|
||||
},
|
||||
listen: function(eventName, methodName) {
|
||||
this.addEventListener(eventName, function(e) {
|
||||
this[methodName](e, e.detail);
|
||||
}.bind(this));
|
||||
},
|
||||
fire: function(type, detail, onNode, bubbles, cancelable) {
|
||||
var node = onNode || this;
|
||||
var detail = (detail === null || detail === undefined) ? {} : detail;
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles !== undefined ? bubbles : true,
|
||||
cancelable: cancelable !== undefined ? cancelable : true,
|
||||
detail: detail
|
||||
});
|
||||
node.dispatchEvent(event);
|
||||
return event;
|
||||
},
|
||||
eventBind: function(eventName, node, targetValue, model, property) {
|
||||
node.addEventListener(eventName, function(e) {
|
||||
model[property] = e.target[targetValue];
|
||||
Polymer.log.events && console.log('[eventBind]: %s.%s = %s',
|
||||
e.target.localName, targetValue, e.target[targetValue]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
4
components/perf-lib/polymer/experimental.html
Normal file
4
components/perf-lib/polymer/experimental.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<script>
|
||||
|
||||
|
||||
</script>
|
||||
41
components/perf-lib/polymer/lang.html
Normal file
41
components/perf-lib/polymer/lang.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<script>
|
||||
|
||||
// a tiny bit of sugar for `document.currentScript.ownerDocument`
|
||||
// sadly `import` is reserved, so we need another name or
|
||||
// you have to refer to this value `window.import`
|
||||
Object.defineProperty(window, 'import', {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
var script = document._currentScript || document.currentScript;
|
||||
return script.ownerDocument;
|
||||
}
|
||||
});
|
||||
|
||||
// copy own properties from 'api' to 'prototype, with name hinting for 'super'
|
||||
function extend(prototype, api) {
|
||||
if (prototype && api) {
|
||||
// use only own properties of 'api'
|
||||
Object.getOwnPropertyNames(api).forEach(function(n) {
|
||||
// acquire property descriptor
|
||||
var pd = Object.getOwnPropertyDescriptor(api, n);
|
||||
if (pd) {
|
||||
// clone property via descriptor
|
||||
Object.defineProperty(prototype, n, pd);
|
||||
// cache name-of-method for 'super' engine
|
||||
if (typeof pd.value == 'function') {
|
||||
// hint the 'super' engine
|
||||
pd.value.nom = n;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return prototype;
|
||||
};
|
||||
|
||||
Event.prototype.keys = {
|
||||
ESC_KEY: 27,
|
||||
ENTER_KEY: 13
|
||||
};
|
||||
|
||||
</script>
|
||||
278
components/perf-lib/polymer/layout.html
Normal file
278
components/perf-lib/polymer/layout.html
Normal file
@@ -0,0 +1,278 @@
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<style shim-shadowdom>
|
||||
/*******************************
|
||||
Flex Layout
|
||||
*******************************/
|
||||
|
||||
html /deep/ [layout][horizontal], html /deep/ [layout][vertical] {
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
html /deep/ [layout][horizontal][inline], html /deep/ [layout][vertical][inline] {
|
||||
display: -ms-inline-flexbox;
|
||||
display: -webkit-inline-flex;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
html /deep/ [layout][horizontal] {
|
||||
-ms-flex-direction: row;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html /deep/ [layout][horizontal][reverse] {
|
||||
-ms-flex-direction: row-reverse;
|
||||
-webkit-flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
html /deep/ [layout][vertical] {
|
||||
-ms-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
html /deep/ [layout][vertical][reverse] {
|
||||
-ms-flex-direction: column-reverse;
|
||||
-webkit-flex-direction: column-reverse;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
html /deep/ [layout][wrap] {
|
||||
-ms-flex-wrap: wrap;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
html /deep/ [layout][wrap-reverse] {
|
||||
-ms-flex-wrap: wrap-reverse;
|
||||
-webkit-flex-wrap: wrap-reverse;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
|
||||
html /deep/ [flex] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
html /deep/ [flex][auto] {
|
||||
-ms-flex: 1 1 auto;
|
||||
-webkit-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
html /deep/ [flex][none] {
|
||||
-ms-flex: none;
|
||||
-webkit-flex: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
html /deep/ [flex][one] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
html /deep/ [flex][two] {
|
||||
-ms-flex: 2;
|
||||
-webkit-flex: 2;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
html /deep/ [flex][three] {
|
||||
-ms-flex: 3;
|
||||
-webkit-flex: 3;
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
html /deep/ [flex][four] {
|
||||
-ms-flex: 4;
|
||||
-webkit-flex: 4;
|
||||
flex: 4;
|
||||
}
|
||||
|
||||
html /deep/ [flex][five] {
|
||||
-ms-flex: 5;
|
||||
-webkit-flex: 5;
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
html /deep/ [flex][six] {
|
||||
-ms-flex: 6;
|
||||
-webkit-flex: 6;
|
||||
flex: 6;
|
||||
}
|
||||
|
||||
html /deep/ [flex][seven] {
|
||||
-ms-flex: 7;
|
||||
-webkit-flex: 7;
|
||||
flex: 7;
|
||||
}
|
||||
|
||||
html /deep/ [flex][eight] {
|
||||
-ms-flex: 8;
|
||||
-webkit-flex: 8;
|
||||
flex: 8;
|
||||
}
|
||||
|
||||
html /deep/ [flex][nine] {
|
||||
-ms-flex: 9;
|
||||
-webkit-flex: 9;
|
||||
flex: 9;
|
||||
}
|
||||
|
||||
html /deep/ [flex][ten] {
|
||||
-ms-flex: 10;
|
||||
-webkit-flex: 10;
|
||||
flex: 10;
|
||||
}
|
||||
|
||||
html /deep/ [flex][eleven] {
|
||||
-ms-flex: 11;
|
||||
-webkit-flex: 11;
|
||||
flex: 11;
|
||||
}
|
||||
|
||||
html /deep/ [flex][twelve] {
|
||||
-ms-flex: 12;
|
||||
-webkit-flex: 12;
|
||||
flex: 12;
|
||||
}
|
||||
|
||||
/* alignment in cross axis */
|
||||
|
||||
html /deep/ [layout][start] {
|
||||
-ms-flex-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
html /deep/ [layout][center], html /deep/ [layout][center-center] {
|
||||
-ms-flex-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
html /deep/ [layout][end] {
|
||||
-ms-flex-align: end;
|
||||
-webkit-align-items: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* alignment in main axis */
|
||||
|
||||
html /deep/ [layout][start-justified] {
|
||||
-ms-flex-pack: start;
|
||||
-webkit-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
html /deep/ [layout][center-justified], html /deep/ [layout][center-center] {
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
html /deep/ [layout][end-justified] {
|
||||
-ms-flex-pack: end;
|
||||
-webkit-justify-content: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
html /deep/ [layout][around-justified] {
|
||||
-ms-flex-pack: around;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
html /deep/ [layout][justified] {
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* self alignment */
|
||||
|
||||
html /deep/ [self-start] {
|
||||
-ms-align-self: flex-start;
|
||||
-webkit-align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
html /deep/ [self-center] {
|
||||
-ms-align-self: center;
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
html /deep/ [self-end] {
|
||||
-ms-align-self: flex-end;
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
html /deep/ [self-stretch] {
|
||||
-ms-align-self: stretch;
|
||||
-webkit-align-self: stretch;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other Layout
|
||||
*******************************/
|
||||
|
||||
html /deep/ [block] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ie support for hidden */
|
||||
html /deep/ [hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
html /deep/ [relative] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html /deep/ [fit] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[fullbleed] {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other
|
||||
*******************************/
|
||||
|
||||
html /deep/ [segment], html /deep/ segment {
|
||||
display: block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 1em 0.5em;
|
||||
padding: 1em;
|
||||
background-color: white;
|
||||
-webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
26
components/perf-lib/polymer/polymer.html
Normal file
26
components/perf-lib/polymer/polymer.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<link rel="import" href="lang.html">
|
||||
<link rel="import" href="layout.html">
|
||||
|
||||
<link rel="import" href="base.html">
|
||||
|
||||
<!-- pluggable features for Base-->
|
||||
<link rel="import" href="utils.html">
|
||||
<link rel="import" href="data.html">
|
||||
<link rel="import" href="annotations.html">
|
||||
<link rel="import" href="bindings.html">
|
||||
<link rel="import" href="events.html">
|
||||
<link rel="import" href="experimental.html">
|
||||
|
||||
<script>
|
||||
Base.installFeatures();
|
||||
Base.__proto__ = HTMLElement.prototype;
|
||||
|
||||
var Polymer = function(prototype) {
|
||||
prototype.__proto__ = Base;
|
||||
prototype.registerCallback();
|
||||
document.registerElement(prototype.name, {prototype: prototype});
|
||||
}
|
||||
|
||||
Polymer.log = {
|
||||
};
|
||||
</script>
|
||||
83
components/perf-lib/polymer/template.html
Normal file
83
components/perf-lib/polymer/template.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<script>
|
||||
var instanceTemplate = function(template) {
|
||||
return document.importNode(template.content, true);
|
||||
};
|
||||
|
||||
var preprocessTemplate = function(template) {
|
||||
var map = [];
|
||||
var a = _preprocess(template.content, map);
|
||||
if (a) {
|
||||
a.map = map;
|
||||
//console.log(a.map);
|
||||
}
|
||||
template.map = map;
|
||||
};
|
||||
|
||||
var _preprocess = function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ? preprocessTextNode(node, map) : preprocessNode(node, map);
|
||||
};
|
||||
|
||||
var preprocessTextNode = function(node, map) {
|
||||
var t = node.textContent;
|
||||
if (t.slice(0, 2) === '{{' || t.slice(0, 2) === '[[') {
|
||||
var annotations = Object.create(null);
|
||||
annotations.name = 'text';
|
||||
annotations.value = t;
|
||||
map.push(annotations);
|
||||
return annotations;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var preprocessNode = function(node, map) {
|
||||
var annotations = Object.create(null);
|
||||
parseAnnotations(node, annotations, map);
|
||||
preprocessChildNodes(node, annotations, map);
|
||||
return annotations;
|
||||
};
|
||||
|
||||
var parseAnnotations = function(node, annotations, map) {
|
||||
var bound = false;
|
||||
if (node.attributes) {
|
||||
var b$ = annotations.bindings = Object.create(null);
|
||||
var e$ = annotations.events = Object.create(null);
|
||||
for (var i=0, a; a=node.attributes[i]; i++) {
|
||||
var n = a.name, v = a.value;
|
||||
if (v.slice(0, 2) === '{{') {
|
||||
b$[n] = {
|
||||
raw: v,
|
||||
prop: v.slice(2, -2)
|
||||
};
|
||||
bound = true;
|
||||
}
|
||||
if (n.slice(0, 3) === 'on-') {
|
||||
e$[n.slice(3)] = v;
|
||||
bound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bound) {
|
||||
map.push(annotations);
|
||||
}
|
||||
annotations.name = node.localName || 'template';
|
||||
};
|
||||
|
||||
var preprocessChildNodes = function(root, annotations, map) {
|
||||
if (root.firstChild) {
|
||||
var c$ = annotations.children = [];
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var ann = _preprocess(node, map);
|
||||
if (ann) {
|
||||
ann.parent = annotations;
|
||||
ann.index = i;
|
||||
}
|
||||
c$.push(ann);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var mapFind = function(root, n) {
|
||||
return n.parent ? mapFind(root, n.parent).childNodes[n.index] : root;
|
||||
};
|
||||
|
||||
</script>
|
||||
36
components/perf-lib/polymer/util.html
Normal file
36
components/perf-lib/polymer/util.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<script>
|
||||
|
||||
// a tiny bit of sugar for `document.currentScript.ownerDocument`
|
||||
// sadly `import` is reserved, so we need another name or
|
||||
// you have to refer to this value `window.import`
|
||||
Object.defineProperty(window, 'import', {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
var script = document._currentScript || document.currentScript;
|
||||
return script.ownerDocument;
|
||||
}
|
||||
});
|
||||
|
||||
// copy own properties from 'api' to 'prototype, with name hinting for 'super'
|
||||
function extend(prototype, api) {
|
||||
if (prototype && api) {
|
||||
// use only own properties of 'api'
|
||||
Object.getOwnPropertyNames(api).forEach(function(n) {
|
||||
// acquire property descriptor
|
||||
var pd = Object.getOwnPropertyDescriptor(api, n);
|
||||
if (pd) {
|
||||
// clone property via descriptor
|
||||
Object.defineProperty(prototype, n, pd);
|
||||
// cache name-of-method for 'super' engine
|
||||
if (typeof pd.value == 'function') {
|
||||
// hint the 'super' engine
|
||||
pd.value.nom = n;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return prototype;
|
||||
};
|
||||
|
||||
</script>
|
||||
14
components/perf-lib/polymer/utils.html
Normal file
14
components/perf-lib/polymer/utils.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<script>
|
||||
|
||||
Base.features.push({
|
||||
$: function(slctr) {
|
||||
return this.root.querySelector(slctr);
|
||||
},
|
||||
bindModel: function(model, properties) {
|
||||
properties.forEach(function(p) {
|
||||
this.bindProperty(p, p, model);
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
1
components/polymer/README.md
Normal file
1
components/polymer/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Mutation-2: 01: As with 00, but eschew normative ShadowDOM.
|
||||
56
components/polymer/assets/icons.html
Normal file
56
components/polymer/assets/icons.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<svg><defs>
|
||||
<g id="apps"><path d="M4,8h4V4H4V8z M10,20h4v-4h-4V20z M4,20h4v-4H4V20z M4,14h4v-4H4V14z M10,14h4v-4h-4V14z M16,4v4h4V4H16z M10,8h4V4h-4V8z M16,14h4v-4h-4V14z M16,20h4v-4h-4V20z"/></g>
|
||||
<g id="archive"><path d="M20.5,5.2l-1.4-1.7C18.9,3.2,18.5,3,18,3H6C5.5,3,5.1,3.2,4.8,3.5L3.5,5.2C3.2,5.6,3,6,3,6.5V19c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V6.5C21,6,20.8,5.6,20.5,5.2z M12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5z M5.1,5l0.8-1h12l0.9,1H5.1z"/></g>
|
||||
<g id="check-box-outline-blank"><path d="M19,5v14L5,19V5H19 M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3L19,3z"/></g>
|
||||
<g id="check-box-outline"><path d="M7.9,10.1l-1.4,1.4L11,16L21,6l-1.4-1.4L11,13.2L7.9,10.1z M19,19L5,19V5h10V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2v-8h-2V19z"/></g>
|
||||
<g id="arrow-drop-down"><polygon points="7,10 12,15 17,10 "/></g>
|
||||
<g id="info-outline"><path d="M11,17h2v-6h-2V17z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z M11,9h2V7h-2V9z"/></g>
|
||||
<g id="search"><path d="M15.5,14h-0.8l-0.3-0.3c1-1.1,1.6-2.6,1.6-4.2C16,5.9,13.1,3,9.5,3C5.9,3,3,5.9,3,9.5S5.9,16,9.5,16c1.6,0,3.1-0.6,4.2-1.6l0.3,0.3v0.8l5,5l1.5-1.5L15.5,14z M9.5,14C7,14,5,12,5,9.5S7,5,9.5,5C12,5,14,7,14,9.5S12,14,9.5,14z"/></g>
|
||||
<g id="arrow-back"><path d="M20,11H7.8l5.6-5.6L12,4l-8,8l8,8l1.4-1.4L7.8,13H20V11z"/></g>
|
||||
<g id="schedule"><path fill-opacity="0.9" d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/><polygon fill-opacity="0.9" points="12.5,7 11,7 11,13 16.2,16.2 17,14.9 12.5,12.2 "/></g>
|
||||
<g id="close"><polygon points="19,6.4 17.6,5 12,10.6 6.4,5 5,6.4 10.6,12 5,17.6 6.4,19 12,13.4 17.6,19 19,17.6 13.4,12 "/></g>
|
||||
<g id="create"><path d="M3,17.2V21h3.8L17.8,9.9l-3.8-3.8L3,17.2z M20.7,7c0.4-0.4,0.4-1,0-1.4l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0l-1.8,1.8l3.8,3.8L20.7,7z"/></g>
|
||||
<g id="settings"><path d="M19.4,13c0-0.3,0.1-0.6,0.1-1s0-0.7-0.1-1l2.1-1.7c0.2-0.2,0.2-0.4,0.1-0.6l-2-3.5C19.5,5.1,19.3,5,19,5.1l-2.5,1c-0.5-0.4-1.1-0.7-1.7-1l-0.4-2.6C14.5,2.2,14.2,2,14,2h-4C9.8,2,9.5,2.2,9.5,2.4L9.1,5.1C8.5,5.3,8,5.7,7.4,6.1L5,5.1C4.7,5,4.5,5.1,4.3,5.3l-2,3.5C2.2,8.9,2.3,9.2,2.5,9.4L4.6,11c0,0.3-0.1,0.6-0.1,1s0,0.7,0.1,1l-2.1,1.7c-0.2,0.2-0.2,0.4-0.1,0.6l2,3.5C4.5,18.9,4.7,19,5,18.9l2.5-1c0.5,0.4,1.1,0.7,1.7,1l0.4,2.6c0,0.2,0.2,0.4,0.5,0.4h4c0.2,0,0.5-0.2,0.5-0.4l0.4-2.6c0.6-0.3,1.2-0.6,1.7-1l2.5,1c0.2,0.1,0.5,0,0.6-0.2l2-3.5c0.1-0.2,0.1-0.5-0.1-0.6L19.4,13z M12,15.5c-1.9,0-3.5-1.6-3.5-3.5s1.6-3.5,3.5-3.5s3.5,1.6,3.5,3.5S13.9,15.5,12,15.5z"/></g>
|
||||
<g id="delete"><path d="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M19,4h-3.5l-1-1h-5l-1,1H5v2h14V4z"/></g>
|
||||
<g id="schedule"><path fill-opacity="0.9" d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/><polygon fill-opacity="0.9" points="12.5,7 11,7 11,13 16.2,16.2 17,14.9 12.5,12.2 "/></g>
|
||||
<g id="menu"><path d="M3,18h18v-2H3V18z M3,13h18v-2H3V13z M3,6v2h18V6H3z"/></g>
|
||||
</defs></svg>
|
||||
|
||||
<script>
|
||||
|
||||
var doc = window.import;
|
||||
|
||||
var cloneIcon = function(id, size) {
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
var icon = doc.querySelector(id);
|
||||
if (icon) {
|
||||
svg.style.pointerEvents = 'none';
|
||||
svg.style.display = 'block';
|
||||
svg.setAttribute('height', '100%');
|
||||
svg.setAttribute('width', '100%');
|
||||
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
||||
svg.setAttribute('viewBox', '0 0 24 24');
|
||||
svg.appendChild(icon.cloneNode(true));
|
||||
}
|
||||
return svg;
|
||||
};
|
||||
|
||||
var icons = {
|
||||
apps: cloneIcon('#apps'),
|
||||
archive: cloneIcon('#archive'),
|
||||
box: cloneIcon('#check-box-outline-blank'),
|
||||
checked: cloneIcon('#check-box-outline'),
|
||||
drop: cloneIcon('#arrow-drop-down'),
|
||||
'info-outline': cloneIcon('#info-outline'),
|
||||
search: cloneIcon('#search'),
|
||||
back: cloneIcon('#arrow-back'),
|
||||
schedule: cloneIcon('#schedule'),
|
||||
create: cloneIcon('#create'),
|
||||
close: cloneIcon('#close'),
|
||||
delete: cloneIcon('#delete'),
|
||||
schedule: cloneIcon('#schedule'),
|
||||
settings: cloneIcon('#settings'),
|
||||
menu: cloneIcon('#menu')
|
||||
};
|
||||
|
||||
</script>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
0
components/polymer/bower.json
Normal file
0
components/polymer/bower.json
Normal file
2
components/polymer/build.bat
Normal file
2
components/polymer/build.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
START /B vulcanize polymer.html --inline -output dist/polymer.html
|
||||
START /B vulcanize data.html --inline -output dist/data.html
|
||||
4
components/polymer/data.html
Normal file
4
components/polymer/data.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<link rel="import" href="src/features/bind.html">
|
||||
<link rel="import" href="src/features/annotations-bind.html">
|
||||
<link rel="import" href="src/features/computed.html">
|
||||
<link rel="import" href="src/features/bind-effects.html">
|
||||
592
components/polymer/dist/data.html
vendored
Normal file
592
components/polymer/dist/data.html
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
<script>
|
||||
|
||||
/*
|
||||
* Needs new name.
|
||||
*
|
||||
* Provides a data-binding API, by which a getter/setter pair
|
||||
* can be constructed in one of two imperative modes:
|
||||
*
|
||||
* bindMethod(property, methodName): constructs a getter/setter pair and a
|
||||
* backing store for the given property; calls the method when the setter
|
||||
* is invoked and the value has changed from what is in the backing store.
|
||||
*
|
||||
* bindProperty(property, path): constructs a getter/setter pair that
|
||||
* forwards data access to a property on another object.
|
||||
*
|
||||
* This feature also supports a `bind` object, which contains expressions
|
||||
* that are deconstructed into `bindMethod` or `bindProperty` calls, or
|
||||
* into a `multiBinding` construct. `multiBinding` constructs support
|
||||
* multiple side-effects.
|
||||
*
|
||||
* bind {
|
||||
* // if `method` is the name of a method on the current object, a
|
||||
* // `bindMethod` call is made to define `property` as described above.
|
||||
* property: 'method'
|
||||
* // if the value is not the name of a method, it's assumed to be the
|
||||
* // name in the `$` hash that maps to an element.
|
||||
* // If no target property is specified, `textContent` is assumed to
|
||||
* // be the backing-store for `property2` accessors.
|
||||
* property2: 'elementId'
|
||||
* // If a path is provided, that element is dereferenced from $ as before,
|
||||
* // but the full path is used for the backing-store.
|
||||
* // This declaration binds property3 to $.elementId.value
|
||||
* property3: 'elementId.value'
|
||||
* // If the specified property is also `published`, a multi-binding
|
||||
* // construct is created which sends a change notification in addition
|
||||
* // to whatever user side-effect is specified.
|
||||
* publishedProperty: <method name or element property>
|
||||
* // Specify multiple side-effects directly as an array. Only one
|
||||
* // callback method is allowed.
|
||||
* property4: [
|
||||
* 'nameOfMethod',
|
||||
* 'elementId',
|
||||
* 'elementId.property',
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* Methods bound into multi-bind contexts support a validation feature. If
|
||||
* the method returns a value that does not === undefined side-effects are
|
||||
* prevented, and the triggering property is set to the returned value, and
|
||||
* a new round of side-effects is initiated.
|
||||
*
|
||||
* Multi-bind = multiple side-effects for one signal.
|
||||
* Note: `signal` today is `set-trap`, should we generalize?
|
||||
* Side-effects can be registered by multiple subsystems:
|
||||
* - bind feature
|
||||
* - bind-annotations feature
|
||||
* - computed feature
|
||||
* - published feature
|
||||
* We need to accumulate all side-effects for a particular property
|
||||
* before constructing the handler.
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// per prototype
|
||||
|
||||
// TODO(sjmiles): initialization of `_propertyEffects` and the
|
||||
// `addPropertyEffect` itself are really the domain of bind-effects
|
||||
// but these things needs to happen before bind-effects itself initializes.
|
||||
// We need to factor bind-effects into before and after features instead
|
||||
// and let this feature be for dealing with `bind` object.
|
||||
|
||||
register: function(prototype) {
|
||||
prototype._addPropertyBindEffects();
|
||||
},
|
||||
|
||||
// TODO(sjmiles): really ad hoc self-modifying code
|
||||
// to resolve initialization ordering around optional
|
||||
// module
|
||||
addPropertyEffect: function(property, kind, effect) {
|
||||
// prepare storage on first invocation
|
||||
this._propertyEffects = {};
|
||||
// add the effect
|
||||
this._addPropertyEffect(property, kind, effect);
|
||||
// subsequent invocations skip preparation step implementation
|
||||
this.addPropertyEffect = this._addPropertyEffect;
|
||||
},
|
||||
|
||||
_addPropertyEffect: function(property, kind, effect) {
|
||||
var fx = this._propertyEffects[property];
|
||||
if (!fx) {
|
||||
fx = this._propertyEffects[property] = [];
|
||||
}
|
||||
fx.push({
|
||||
kind: kind,
|
||||
effect: effect
|
||||
});
|
||||
},
|
||||
|
||||
_addPropertyBindEffects: function() {
|
||||
for (var n in this.bind) {
|
||||
var bind = this.bind[n];
|
||||
if (typeof bind === 'object') {
|
||||
// multiplexed definition
|
||||
for (var nn in bind) {
|
||||
this._addPropertyBindEffect(n, bind[nn]);
|
||||
}
|
||||
} else {
|
||||
// single definition
|
||||
this._addPropertyBindEffect(n, bind);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addPropertyBindEffect: function(property, bindEffect) {
|
||||
this.addPropertyEffect(property, 'bind', bindEffect);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// TODO(sjmiles): this code was ported from an earlier mutation and needs
|
||||
// a cleanup to cleave closer to neoprene MO
|
||||
|
||||
/*
|
||||
|
||||
Scans a template to produce an annotation map that stores expression metadata
|
||||
and information that can be used to associate that metadata with the
|
||||
corresponding nodes in a template instance.
|
||||
|
||||
Supported annotations are:
|
||||
|
||||
* id attributes
|
||||
* binding annotations in text nodes
|
||||
* double-mustache expressions: {{expression}}
|
||||
* double-bracket expressions: [[expression]]
|
||||
* binding annotations in attributes
|
||||
* attribute-bind expressions: name="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name*="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name:="expression"
|
||||
* event annotations
|
||||
* event delegation directives: on-<eventName>="expression"
|
||||
|
||||
Generated data-structure:
|
||||
|
||||
[
|
||||
{
|
||||
id: '<id>',
|
||||
events: [
|
||||
{
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
bindings: [
|
||||
{
|
||||
kind: ['text'|'attribute'|'property'],
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
// TODO(sjmiles): confusingly, this is annotation-parent, not node-parent
|
||||
parent: <reference to parent annotation>,
|
||||
index: <integer index in parent's childNodes collection>
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
TODO(sjmiles): this module should produce either syntactic metadata
|
||||
(e.g. double-mustache, double-bracket, star-attr), or semantic metadata
|
||||
(e.g. manual-bind, auto-bind, property-bind). Right now it's half and half.
|
||||
|
||||
*/
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// instance-time
|
||||
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
if (!annote.parent) {
|
||||
return root;
|
||||
}
|
||||
var parent = this.findAnnotatedNode(root, annote.parent);
|
||||
// enforce locality.
|
||||
var nodes = (parent === this) ? parent.childNodes :
|
||||
(parent.lightChildren || parent.childNodes);
|
||||
return nodes[annote.index];
|
||||
},
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template) {
|
||||
prototype.parseAnnotations(prototype._template)
|
||||
}
|
||||
},
|
||||
|
||||
parseAnnotations: function(template) {
|
||||
// TODO(sjmiles): it's not a map, per se
|
||||
var map = [];
|
||||
this._parseNodeAnnotations(template.content, map);
|
||||
if (map.length) {
|
||||
template.map = map;
|
||||
}
|
||||
return template.map;
|
||||
},
|
||||
|
||||
_parseNodeAnnotations: function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ?
|
||||
this._parseTextNodeAnnotation(node, map) :
|
||||
this._parseElementAnnotations(node, map);
|
||||
},
|
||||
|
||||
_parseTextNodeAnnotation: function(node, map) {
|
||||
var v = node.textContent, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var annotation = {
|
||||
bindings: [{
|
||||
kind: 'text',
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
value: v.slice(2, -2)
|
||||
}]
|
||||
};
|
||||
map.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
},
|
||||
|
||||
_parseElementAnnotations: function(node, map) {
|
||||
var annote = {
|
||||
bindings: [],
|
||||
events: []
|
||||
};
|
||||
this._parseChildNodesAnnotations(node, annote, map);
|
||||
if (node.attributes) {
|
||||
this._parseNodeAttributeAnnotations(node, annote, map);
|
||||
}
|
||||
if (annote.bindings.length || annote.events.length || annote.id) {
|
||||
map.push(annote);
|
||||
}
|
||||
return annote;
|
||||
},
|
||||
|
||||
_parseChildNodesAnnotations: function(root, annotation, map) {
|
||||
if (root.firstChild) {
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var childAnnotation = this._parseNodeAnnotations(node, map);
|
||||
if (childAnnotation) {
|
||||
childAnnotation.parent = annotation;
|
||||
childAnnotation.index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotations: function(node, annotation) {
|
||||
for (var i=0, a; (a=node.attributes[i]); i++) {
|
||||
var n = a.name, v = a.value;
|
||||
// id
|
||||
if (n === 'id') {
|
||||
annotation.id = v;
|
||||
}
|
||||
// on-* (event)
|
||||
else if (n.slice(0, 3) === 'on-') {
|
||||
annotation.events.push({
|
||||
name: n.slice(3),
|
||||
value: v
|
||||
});
|
||||
}
|
||||
// other attribute
|
||||
else {
|
||||
var b = this._parseNodeAttributeAnnotation(node, n, v);
|
||||
if (b) {
|
||||
annotation.bindings.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotation: function(node, n, v) {
|
||||
var escape = v.slice(0, 2), lastChar = n[n.length-1];
|
||||
var kind = 'attribute', mode = '';
|
||||
if (lastChar === '*' || lastChar === ':') {
|
||||
n = n.slice(0, -1);
|
||||
kind = 'property';
|
||||
mode = 'auto';
|
||||
}
|
||||
if (escape === '{{') {
|
||||
mode = 'auto';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (escape === '[[') {
|
||||
mode = 'manual';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (mode) {
|
||||
if (n === 'style') {
|
||||
kind = 'style';
|
||||
}
|
||||
return {
|
||||
kind: kind,
|
||||
mode: mode,
|
||||
name: n,
|
||||
value: v
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Parses the annotations map created by `annotations` features to perform
|
||||
* declarative desugaring.
|
||||
*
|
||||
* Depends on `annotations` feature and `bind` feature.
|
||||
*
|
||||
* Two tasks are supported:
|
||||
*
|
||||
* - nodes with 'id' are described in a virtual annotation map at
|
||||
* registration time. This map is then concretized per instance.
|
||||
*
|
||||
* - Simple mustache expressions consisting of a single property name
|
||||
* in a `textContent` context are bound using `bind` features
|
||||
* `bindMethod`. In this mode, the bound method is constructed at
|
||||
* registration time, so marshaling is done done via the concretized
|
||||
* `_nodes` at every access.
|
||||
*
|
||||
* TODO(sjmiles): ph3ar general confusion between registration and
|
||||
* instance time tasks. Is there a cleaner way to disambiguate?
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template && prototype._template.map) {
|
||||
this._preprocessBindAnnotations(prototype, prototype._template.map);
|
||||
}
|
||||
},
|
||||
|
||||
// construct binding meta-data at *registration* time
|
||||
_preprocessBindAnnotations: function(prototype, map) {
|
||||
// create a virtual annotation map, must be concretized at instance time
|
||||
prototype._nodes = [];
|
||||
// process annotations that have been parsed from template
|
||||
map.forEach(function(annotation) {
|
||||
// where to find the node in the concretized map
|
||||
var index = prototype._nodes.push(annotation) - 1;
|
||||
// TODO(sjmiles): we need to support multi-bind, right now you only get
|
||||
// one (not including kind === `id`)
|
||||
annotation.bindings.forEach(function(binding) {
|
||||
prototype._bindAnnotationBinding(binding, index);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_bindAnnotationBinding: function(binding, index) {
|
||||
// add to the list of property side-effects
|
||||
binding.index = index;
|
||||
this.addPropertyEffect(binding.value, 'annotation', binding);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
/* computed property feature */
|
||||
|
||||
computed: {
|
||||
},
|
||||
|
||||
register: function(prototype) {
|
||||
prototype.defineComputedProperties(prototype.computed);
|
||||
},
|
||||
|
||||
defineComputedProperties: function(computed) {
|
||||
for (var n in computed) {
|
||||
this.defineComputedProperty(n, computed[n]);
|
||||
}
|
||||
},
|
||||
|
||||
defineComputedProperty: function(name, expression) {
|
||||
var index = expression.indexOf('(');
|
||||
var method = expression.slice(0, index);
|
||||
var args = expression.slice(index + 1, -1).replace(/ /g, '').split(',');
|
||||
console.log('%c on [%s] compute [%s] via [%s]', 'color: green', args[0], name, method);
|
||||
this.addPropertyEffect(args[0], 'compute', {
|
||||
property: name,
|
||||
method: method
|
||||
});
|
||||
/*
|
||||
this.compoundWatch(args, function() {
|
||||
Polymer.log.watches && console.log('[compute] [%s]', name, arguments);
|
||||
this[name] = method.apply(this, arguments);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// per instance
|
||||
|
||||
init: function() {
|
||||
this._data = Object.create(null);
|
||||
},
|
||||
|
||||
_setupBindListeners: function() {
|
||||
var bl = this._bindListeners;
|
||||
for (var n in bl) {
|
||||
bl[n].targets.forEach(function(target) {
|
||||
this._setupBindListener(n, target);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
_setupBindListener: function(property, target) {
|
||||
//console.log('[bind]: [%s][%s] listening for [%s][%s-changed]', this.localName, property, target.id || target.index, target.property);
|
||||
var host = this, property;
|
||||
var node = target.id ? this.$[target.id] : this._nodes[target.index];
|
||||
node.addEventListener(target.property + '-changed', function(e) {
|
||||
//console.log('[bind]:[%s] heard [%s-changed] this.[%s] = [%s]', host.localName, source, property, e.detail);
|
||||
host[property] = e.detail;
|
||||
});
|
||||
},
|
||||
|
||||
_notifyChange: function(property) {
|
||||
this.fire(property + '-changed', this[property], null, false);
|
||||
},
|
||||
|
||||
_setData: function(property, value) {
|
||||
var old = this._data[property];
|
||||
if (old !== value) {
|
||||
this._data[property] = value;
|
||||
}
|
||||
return old;
|
||||
},
|
||||
|
||||
// per prototype
|
||||
|
||||
register: function(prototype) {
|
||||
prototype._bindListeners = {};
|
||||
prototype._createBindings();
|
||||
},
|
||||
|
||||
_createBindings: function() {
|
||||
//console.group(this.name);
|
||||
var fx = this._propertyEffects;
|
||||
for (var n in fx) {
|
||||
//console.group(n);
|
||||
var compiledEffects = fx[n].map(function(x) {
|
||||
return this._buildEffect(n, x);
|
||||
}, this);
|
||||
this._bindPropertyEffects(n, compiledEffects);
|
||||
//console.log(fxt.join('\n'));
|
||||
//console.groupEnd();
|
||||
}
|
||||
//console.groupEnd();
|
||||
},
|
||||
|
||||
_buildEffect: function(property, fx) {
|
||||
return this['_' + fx.kind + 'EffectBuilder'](property, fx.effect);
|
||||
},
|
||||
|
||||
_bindEffectBuilder: function(source, effect) {
|
||||
// TODO(sjmiles): validation system requires a blessed
|
||||
// validator effect which needs to be processed first.
|
||||
/*
|
||||
if (typeof this[effect] === 'function') {
|
||||
return [
|
||||
'var validated = this.' + effect + '(value, old)',
|
||||
'if (validated !== undefined) {',
|
||||
' // recurse',
|
||||
' this[property] = validated;',
|
||||
' return;',
|
||||
'}'
|
||||
].join('\n');
|
||||
}
|
||||
*/
|
||||
//
|
||||
// TODO(sjmiles): try/catch is temporary
|
||||
//try {
|
||||
if (typeof this[effect] === 'function') {
|
||||
return 'this.' + effect + '(this._data.' + source + ', old);'
|
||||
}
|
||||
//} catch(x) {}
|
||||
//
|
||||
var paths = effect.split('.');
|
||||
var id = paths.shift();
|
||||
var property = paths.join('.');
|
||||
//
|
||||
if (property) {
|
||||
// TODO(sjmiles): awkward: store data for instance-time listeners.
|
||||
// _addBindListener is in bind.html, if we did the path processing
|
||||
// in that module we could contain all the listener logic there too.
|
||||
this._addBindListener(source, id, property);
|
||||
} else {
|
||||
property = 'textContent';
|
||||
}
|
||||
//
|
||||
return 'this.$.' + id + '.' + property + ' = '
|
||||
+ 'this._data.' + source + ';'
|
||||
},
|
||||
|
||||
_bindPropertyEffects: function(property, effects) {
|
||||
var defun = {
|
||||
get: function() {
|
||||
return this._data[property];
|
||||
}
|
||||
}
|
||||
if (effects.length) {
|
||||
// combine effects
|
||||
effects = effects.join('\n\t\t');
|
||||
// construct effector
|
||||
var effector = '_' + property + 'Effector';
|
||||
this[effector] = new Function('old', effects);
|
||||
// construct setter body
|
||||
var body = '\tvar old = this._setData(\'' + property + '\', value);\n'
|
||||
+ '\tif (value !== old) {\n'
|
||||
+ '\t\tthis.' + effector + '(old);\n'
|
||||
+ '\t}';
|
||||
var setter = new Function('value', body);
|
||||
// ReadOnly properties have a private setter only
|
||||
if (this.isReadOnlyProperty(property)) {
|
||||
this['_set_' + property] = setter;
|
||||
}
|
||||
// other properties have a proper setter
|
||||
else {
|
||||
defun.set = setter;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(this, property, defun);
|
||||
//var prop = Object.getOwnPropertyDescriptor(this, property);
|
||||
//console.log(prop.set ? prop.set.toString() : '(read-only)');
|
||||
},
|
||||
|
||||
_notifyEffectBuilder: function(source) {
|
||||
return 'this._notifyChange(\'' + source + '\')';
|
||||
},
|
||||
|
||||
_computeEffectBuilder: function(source, effect) {
|
||||
return 'this.' + effect.property
|
||||
+ ' = this.' + effect.method + '(this._data.' + source + ');';
|
||||
},
|
||||
|
||||
_annotationEffectBuilder: function(source, binding) {
|
||||
var target = binding.name || 'textContent';
|
||||
if (binding.kind !== 'text' && binding.kind !== 'attribute') {
|
||||
console.warn(binding.kind);
|
||||
return;
|
||||
}
|
||||
if (target !== 'textContent') {
|
||||
this._addAnnotatedListener(source, binding.index, target);
|
||||
}
|
||||
return this._bindAnnotationProperty(source, target, binding.index);
|
||||
},
|
||||
|
||||
_bindAnnotationProperty: function(source, target, index) {
|
||||
return 'this._nodes[' + index + '].' + target
|
||||
+ ' = this._data.' + source + ';';
|
||||
},
|
||||
|
||||
_addBindListener: function(source, id, property) {
|
||||
var bl = this._requireBindListeners(source);
|
||||
bl.targets.push({
|
||||
id: id,
|
||||
property: property
|
||||
});
|
||||
},
|
||||
|
||||
_addAnnotatedListener: function(source, index, property) {
|
||||
var bl = this._requireBindListeners(source);
|
||||
bl.targets.push({
|
||||
index: index,
|
||||
property: property
|
||||
});
|
||||
},
|
||||
|
||||
_requireBindListeners: function(source) {
|
||||
var bl = this._bindListeners[source];
|
||||
if (!bl) {
|
||||
bl = this._bindListeners[source] = {targets: []};
|
||||
}
|
||||
return bl;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
BIN
components/polymer/dist/data.html.gz
vendored
Normal file
BIN
components/polymer/dist/data.html.gz
vendored
Normal file
Binary file not shown.
589
components/polymer/dist/data.js
vendored
Normal file
589
components/polymer/dist/data.js
vendored
Normal file
@@ -0,0 +1,589 @@
|
||||
|
||||
/*
|
||||
* Needs new name.
|
||||
*
|
||||
* Provides a data-binding API, by which a getter/setter pair
|
||||
* can be constructed in one of two imperative modes:
|
||||
*
|
||||
* bindMethod(property, methodName): constructs a getter/setter pair and a
|
||||
* backing store for the given property; calls the method when the setter
|
||||
* is invoked and the value has changed from what is in the backing store.
|
||||
*
|
||||
* bindProperty(property, path): constructs a getter/setter pair that
|
||||
* forwards data access to a property on another object.
|
||||
*
|
||||
* This feature also supports a `bind` object, which contains expressions
|
||||
* that are deconstructed into `bindMethod` or `bindProperty` calls, or
|
||||
* into a `multiBinding` construct. `multiBinding` constructs support
|
||||
* multiple side-effects.
|
||||
*
|
||||
* bind {
|
||||
* // if `method` is the name of a method on the current object, a
|
||||
* // `bindMethod` call is made to define `property` as described above.
|
||||
* property: 'method'
|
||||
* // if the value is not the name of a method, it's assumed to be the
|
||||
* // name in the `$` hash that maps to an element.
|
||||
* // If no target property is specified, `textContent` is assumed to
|
||||
* // be the backing-store for `property2` accessors.
|
||||
* property2: 'elementId'
|
||||
* // If a path is provided, that element is dereferenced from $ as before,
|
||||
* // but the full path is used for the backing-store.
|
||||
* // This declaration binds property3 to $.elementId.value
|
||||
* property3: 'elementId.value'
|
||||
* // If the specified property is also `published`, a multi-binding
|
||||
* // construct is created which sends a change notification in addition
|
||||
* // to whatever user side-effect is specified.
|
||||
* publishedProperty: <method name or element property>
|
||||
* // Specify multiple side-effects directly as an array. Only one
|
||||
* // callback method is allowed.
|
||||
* property4: [
|
||||
* 'nameOfMethod',
|
||||
* 'elementId',
|
||||
* 'elementId.property',
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* Methods bound into multi-bind contexts support a validation feature. If
|
||||
* the method returns a value that does not === undefined side-effects are
|
||||
* prevented, and the triggering property is set to the returned value, and
|
||||
* a new round of side-effects is initiated.
|
||||
*
|
||||
* Multi-bind = multiple side-effects for one signal.
|
||||
* Note: `signal` today is `set-trap`, should we generalize?
|
||||
* Side-effects can be registered by multiple subsystems:
|
||||
* - bind feature
|
||||
* - bind-annotations feature
|
||||
* - computed feature
|
||||
* - published feature
|
||||
* We need to accumulate all side-effects for a particular property
|
||||
* before constructing the handler.
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// per prototype
|
||||
|
||||
// TODO(sjmiles): initialization of `_propertyEffects` and the
|
||||
// `addPropertyEffect` itself are really the domain of bind-effects
|
||||
// but these things needs to happen before bind-effects itself initializes.
|
||||
// We need to factor bind-effects into before and after features instead
|
||||
// and let this feature be for dealing with `bind` object.
|
||||
|
||||
register: function(prototype) {
|
||||
prototype._addPropertyBindEffects();
|
||||
},
|
||||
|
||||
// TODO(sjmiles): really ad hoc self-modifying code
|
||||
// to resolve initialization ordering around optional
|
||||
// module
|
||||
addPropertyEffect: function(property, kind, effect) {
|
||||
// prepare storage on first invocation
|
||||
this._propertyEffects = {};
|
||||
// add the effect
|
||||
this._addPropertyEffect(property, kind, effect);
|
||||
// subsequent invocations skip preparation step implementation
|
||||
this.addPropertyEffect = this._addPropertyEffect;
|
||||
},
|
||||
|
||||
_addPropertyEffect: function(property, kind, effect) {
|
||||
var fx = this._propertyEffects[property];
|
||||
if (!fx) {
|
||||
fx = this._propertyEffects[property] = [];
|
||||
}
|
||||
fx.push({
|
||||
kind: kind,
|
||||
effect: effect
|
||||
});
|
||||
},
|
||||
|
||||
_addPropertyBindEffects: function() {
|
||||
for (var n in this.bind) {
|
||||
var bind = this.bind[n];
|
||||
if (typeof bind === 'object') {
|
||||
// multiplexed definition
|
||||
for (var nn in bind) {
|
||||
this._addPropertyBindEffect(n, bind[nn]);
|
||||
}
|
||||
} else {
|
||||
// single definition
|
||||
this._addPropertyBindEffect(n, bind);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addPropertyBindEffect: function(property, bindEffect) {
|
||||
this.addPropertyEffect(property, 'bind', bindEffect);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// TODO(sjmiles): this code was ported from an earlier mutation and needs
|
||||
// a cleanup to cleave closer to neoprene MO
|
||||
|
||||
/*
|
||||
|
||||
Scans a template to produce an annotation map that stores expression metadata
|
||||
and information that can be used to associate that metadata with the
|
||||
corresponding nodes in a template instance.
|
||||
|
||||
Supported annotations are:
|
||||
|
||||
* id attributes
|
||||
* binding annotations in text nodes
|
||||
* double-mustache expressions: {{expression}}
|
||||
* double-bracket expressions: [[expression]]
|
||||
* binding annotations in attributes
|
||||
* attribute-bind expressions: name="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name*="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name:="expression"
|
||||
* event annotations
|
||||
* event delegation directives: on-<eventName>="expression"
|
||||
|
||||
Generated data-structure:
|
||||
|
||||
[
|
||||
{
|
||||
id: '<id>',
|
||||
events: [
|
||||
{
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
bindings: [
|
||||
{
|
||||
kind: ['text'|'attribute'|'property'],
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
// TODO(sjmiles): confusingly, this is annotation-parent, not node-parent
|
||||
parent: <reference to parent annotation>,
|
||||
index: <integer index in parent's childNodes collection>
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
TODO(sjmiles): this module should produce either syntactic metadata
|
||||
(e.g. double-mustache, double-bracket, star-attr), or semantic metadata
|
||||
(e.g. manual-bind, auto-bind, property-bind). Right now it's half and half.
|
||||
|
||||
*/
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// instance-time
|
||||
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
if (!annote.parent) {
|
||||
return root;
|
||||
}
|
||||
var parent = this.findAnnotatedNode(root, annote.parent);
|
||||
// enforce locality.
|
||||
var nodes = (parent === this) ? parent.childNodes :
|
||||
(parent.lightChildren || parent.childNodes);
|
||||
return nodes[annote.index];
|
||||
},
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template) {
|
||||
prototype.parseAnnotations(prototype._template)
|
||||
}
|
||||
},
|
||||
|
||||
parseAnnotations: function(template) {
|
||||
// TODO(sjmiles): it's not a map, per se
|
||||
var map = [];
|
||||
this._parseNodeAnnotations(template.content, map);
|
||||
if (map.length) {
|
||||
template.map = map;
|
||||
}
|
||||
return template.map;
|
||||
},
|
||||
|
||||
_parseNodeAnnotations: function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ?
|
||||
this._parseTextNodeAnnotation(node, map) :
|
||||
this._parseElementAnnotations(node, map);
|
||||
},
|
||||
|
||||
_parseTextNodeAnnotation: function(node, map) {
|
||||
var v = node.textContent, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var annotation = {
|
||||
bindings: [{
|
||||
kind: 'text',
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
value: v.slice(2, -2)
|
||||
}]
|
||||
};
|
||||
map.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
},
|
||||
|
||||
_parseElementAnnotations: function(node, map) {
|
||||
var annote = {
|
||||
bindings: [],
|
||||
events: []
|
||||
};
|
||||
this._parseChildNodesAnnotations(node, annote, map);
|
||||
if (node.attributes) {
|
||||
this._parseNodeAttributeAnnotations(node, annote, map);
|
||||
}
|
||||
if (annote.bindings.length || annote.events.length || annote.id) {
|
||||
map.push(annote);
|
||||
}
|
||||
return annote;
|
||||
},
|
||||
|
||||
_parseChildNodesAnnotations: function(root, annotation, map) {
|
||||
if (root.firstChild) {
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var childAnnotation = this._parseNodeAnnotations(node, map);
|
||||
if (childAnnotation) {
|
||||
childAnnotation.parent = annotation;
|
||||
childAnnotation.index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotations: function(node, annotation) {
|
||||
for (var i=0, a; (a=node.attributes[i]); i++) {
|
||||
var n = a.name, v = a.value;
|
||||
// id
|
||||
if (n === 'id') {
|
||||
annotation.id = v;
|
||||
}
|
||||
// on-* (event)
|
||||
else if (n.slice(0, 3) === 'on-') {
|
||||
annotation.events.push({
|
||||
name: n.slice(3),
|
||||
value: v
|
||||
});
|
||||
}
|
||||
// other attribute
|
||||
else {
|
||||
var b = this._parseNodeAttributeAnnotation(node, n, v);
|
||||
if (b) {
|
||||
annotation.bindings.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotation: function(node, n, v) {
|
||||
var escape = v.slice(0, 2), lastChar = n[n.length-1];
|
||||
var kind = 'attribute', mode = '';
|
||||
if (lastChar === '*' || lastChar === ':') {
|
||||
n = n.slice(0, -1);
|
||||
kind = 'property';
|
||||
mode = 'auto';
|
||||
}
|
||||
if (escape === '{{') {
|
||||
mode = 'auto';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (escape === '[[') {
|
||||
mode = 'manual';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (mode) {
|
||||
if (n === 'style') {
|
||||
kind = 'style';
|
||||
}
|
||||
return {
|
||||
kind: kind,
|
||||
mode: mode,
|
||||
name: n,
|
||||
value: v
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Parses the annotations map created by `annotations` features to perform
|
||||
* declarative desugaring.
|
||||
*
|
||||
* Depends on `annotations` feature and `bind` feature.
|
||||
*
|
||||
* Two tasks are supported:
|
||||
*
|
||||
* - nodes with 'id' are described in a virtual annotation map at
|
||||
* registration time. This map is then concretized per instance.
|
||||
*
|
||||
* - Simple mustache expressions consisting of a single property name
|
||||
* in a `textContent` context are bound using `bind` features
|
||||
* `bindMethod`. In this mode, the bound method is constructed at
|
||||
* registration time, so marshaling is done done via the concretized
|
||||
* `_nodes` at every access.
|
||||
*
|
||||
* TODO(sjmiles): ph3ar general confusion between registration and
|
||||
* instance time tasks. Is there a cleaner way to disambiguate?
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template && prototype._template.map) {
|
||||
this._preprocessBindAnnotations(prototype, prototype._template.map);
|
||||
}
|
||||
},
|
||||
|
||||
// construct binding meta-data at *registration* time
|
||||
_preprocessBindAnnotations: function(prototype, map) {
|
||||
// create a virtual annotation map, must be concretized at instance time
|
||||
prototype._nodes = [];
|
||||
// process annotations that have been parsed from template
|
||||
map.forEach(function(annotation) {
|
||||
// where to find the node in the concretized map
|
||||
var index = prototype._nodes.push(annotation) - 1;
|
||||
// TODO(sjmiles): we need to support multi-bind, right now you only get
|
||||
// one (not including kind === `id`)
|
||||
annotation.bindings.forEach(function(binding) {
|
||||
prototype._bindAnnotationBinding(binding, index);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_bindAnnotationBinding: function(binding, index) {
|
||||
// add to the list of property side-effects
|
||||
binding.index = index;
|
||||
this.addPropertyEffect(binding.value, 'annotation', binding);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
/* computed property feature */
|
||||
|
||||
computed: {
|
||||
},
|
||||
|
||||
register: function(prototype) {
|
||||
prototype.defineComputedProperties(prototype.computed);
|
||||
},
|
||||
|
||||
defineComputedProperties: function(computed) {
|
||||
for (var n in computed) {
|
||||
this.defineComputedProperty(n, computed[n]);
|
||||
}
|
||||
},
|
||||
|
||||
defineComputedProperty: function(name, expression) {
|
||||
var index = expression.indexOf('(');
|
||||
var method = expression.slice(0, index);
|
||||
var args = expression.slice(index + 1, -1).replace(/ /g, '').split(',');
|
||||
console.log('%c on [%s] compute [%s] via [%s]', 'color: green', args[0], name, method);
|
||||
this.addPropertyEffect(args[0], 'compute', {
|
||||
property: name,
|
||||
method: method
|
||||
});
|
||||
/*
|
||||
this.compoundWatch(args, function() {
|
||||
Polymer.log.watches && console.log('[compute] [%s]', name, arguments);
|
||||
this[name] = method.apply(this, arguments);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// per instance
|
||||
|
||||
init: function() {
|
||||
this._data = Object.create(null);
|
||||
},
|
||||
|
||||
_setupBindListeners: function() {
|
||||
var bl = this._bindListeners;
|
||||
for (var n in bl) {
|
||||
bl[n].targets.forEach(function(target) {
|
||||
this._setupBindListener(n, target);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
_setupBindListener: function(property, target) {
|
||||
//console.log('[bind]: [%s][%s] listening for [%s][%s-changed]', this.localName, property, target.id || target.index, target.property);
|
||||
var host = this, property;
|
||||
var node = target.id ? this.$[target.id] : this._nodes[target.index];
|
||||
node.addEventListener(target.property + '-changed', function(e) {
|
||||
//console.log('[bind]:[%s] heard [%s-changed] this.[%s] = [%s]', host.localName, source, property, e.detail);
|
||||
host[property] = e.detail;
|
||||
});
|
||||
},
|
||||
|
||||
_notifyChange: function(property) {
|
||||
this.fire(property + '-changed', this[property], null, false);
|
||||
},
|
||||
|
||||
_setData: function(property, value) {
|
||||
var old = this._data[property];
|
||||
if (old !== value) {
|
||||
this._data[property] = value;
|
||||
}
|
||||
return old;
|
||||
},
|
||||
|
||||
// per prototype
|
||||
|
||||
register: function(prototype) {
|
||||
prototype._bindListeners = {};
|
||||
prototype._createBindings();
|
||||
},
|
||||
|
||||
_createBindings: function() {
|
||||
//console.group(this.name);
|
||||
var fx = this._propertyEffects;
|
||||
for (var n in fx) {
|
||||
//console.group(n);
|
||||
var compiledEffects = fx[n].map(function(x) {
|
||||
return this._buildEffect(n, x);
|
||||
}, this);
|
||||
this._bindPropertyEffects(n, compiledEffects);
|
||||
//console.log(fxt.join('\n'));
|
||||
//console.groupEnd();
|
||||
}
|
||||
//console.groupEnd();
|
||||
},
|
||||
|
||||
_buildEffect: function(property, fx) {
|
||||
return this['_' + fx.kind + 'EffectBuilder'](property, fx.effect);
|
||||
},
|
||||
|
||||
_bindEffectBuilder: function(source, effect) {
|
||||
// TODO(sjmiles): validation system requires a blessed
|
||||
// validator effect which needs to be processed first.
|
||||
/*
|
||||
if (typeof this[effect] === 'function') {
|
||||
return [
|
||||
'var validated = this.' + effect + '(value, old)',
|
||||
'if (validated !== undefined) {',
|
||||
' // recurse',
|
||||
' this[property] = validated;',
|
||||
' return;',
|
||||
'}'
|
||||
].join('\n');
|
||||
}
|
||||
*/
|
||||
//
|
||||
// TODO(sjmiles): try/catch is temporary
|
||||
//try {
|
||||
if (typeof this[effect] === 'function') {
|
||||
return 'this.' + effect + '(this._data.' + source + ', old);'
|
||||
}
|
||||
//} catch(x) {}
|
||||
//
|
||||
var paths = effect.split('.');
|
||||
var id = paths.shift();
|
||||
var property = paths.join('.');
|
||||
//
|
||||
if (property) {
|
||||
// TODO(sjmiles): awkward: store data for instance-time listeners.
|
||||
// _addBindListener is in bind.html, if we did the path processing
|
||||
// in that module we could contain all the listener logic there too.
|
||||
this._addBindListener(source, id, property);
|
||||
} else {
|
||||
property = 'textContent';
|
||||
}
|
||||
//
|
||||
return 'this.$.' + id + '.' + property + ' = '
|
||||
+ 'this._data.' + source + ';'
|
||||
},
|
||||
|
||||
_bindPropertyEffects: function(property, effects) {
|
||||
var defun = {
|
||||
get: function() {
|
||||
return this._data[property];
|
||||
}
|
||||
}
|
||||
if (effects.length) {
|
||||
// combine effects
|
||||
effects = effects.join('\n\t\t');
|
||||
// construct effector
|
||||
var effector = '_' + property + 'Effector';
|
||||
this[effector] = new Function('old', effects);
|
||||
// construct setter body
|
||||
var body = '\tvar old = this._setData(\'' + property + '\', value);\n'
|
||||
+ '\tif (value !== old) {\n'
|
||||
+ '\t\tthis.' + effector + '(old);\n'
|
||||
+ '\t}';
|
||||
var setter = new Function('value', body);
|
||||
// ReadOnly properties have a private setter only
|
||||
if (this.isReadOnlyProperty(property)) {
|
||||
this['_set_' + property] = setter;
|
||||
}
|
||||
// other properties have a proper setter
|
||||
else {
|
||||
defun.set = setter;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(this, property, defun);
|
||||
//var prop = Object.getOwnPropertyDescriptor(this, property);
|
||||
//console.log(prop.set ? prop.set.toString() : '(read-only)');
|
||||
},
|
||||
|
||||
_notifyEffectBuilder: function(source) {
|
||||
return 'this._notifyChange(\'' + source + '\')';
|
||||
},
|
||||
|
||||
_computeEffectBuilder: function(source, effect) {
|
||||
return 'this.' + effect.property
|
||||
+ ' = this.' + effect.method + '(this._data.' + source + ');';
|
||||
},
|
||||
|
||||
_annotationEffectBuilder: function(source, binding) {
|
||||
var target = binding.name || 'textContent';
|
||||
if (binding.kind !== 'text' && binding.kind !== 'attribute') {
|
||||
console.warn(binding.kind);
|
||||
return;
|
||||
}
|
||||
if (target !== 'textContent') {
|
||||
this._addAnnotatedListener(source, binding.index, target);
|
||||
}
|
||||
return this._bindAnnotationProperty(source, target, binding.index);
|
||||
},
|
||||
|
||||
_bindAnnotationProperty: function(source, target, index) {
|
||||
return 'this._nodes[' + index + '].' + target
|
||||
+ ' = this._data.' + source + ';';
|
||||
},
|
||||
|
||||
_addBindListener: function(source, id, property) {
|
||||
var bl = this._requireBindListeners(source);
|
||||
bl.targets.push({
|
||||
id: id,
|
||||
property: property
|
||||
});
|
||||
},
|
||||
|
||||
_addAnnotatedListener: function(source, index, property) {
|
||||
var bl = this._requireBindListeners(source);
|
||||
bl.targets.push({
|
||||
index: index,
|
||||
property: property
|
||||
});
|
||||
},
|
||||
|
||||
_requireBindListeners: function(source) {
|
||||
var bl = this._bindListeners[source];
|
||||
if (!bl) {
|
||||
bl = this._bindListeners[source] = {targets: []};
|
||||
}
|
||||
return bl;
|
||||
}
|
||||
|
||||
});
|
||||
1
components/polymer/dist/data.min.html
vendored
Normal file
1
components/polymer/dist/data.min.html
vendored
Normal file
File diff suppressed because one or more lines are too long
1
components/polymer/dist/data.min.js
vendored
Normal file
1
components/polymer/dist/data.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
components/polymer/dist/data.min.js.gz
vendored
Normal file
BIN
components/polymer/dist/data.min.js.gz
vendored
Normal file
Binary file not shown.
275
components/polymer/dist/layout.css
vendored
Normal file
275
components/polymer/dist/layout.css
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
/*******************************
|
||||
Flex Layout
|
||||
*******************************/
|
||||
|
||||
[layout][horizontal], [layout][vertical] {
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[layout][horizontal][inline], [layout][vertical][inline] {
|
||||
display: -ms-inline-flexbox;
|
||||
display: -webkit-inline-flex;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
[layout][horizontal] {
|
||||
-ms-flex-direction: row;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
[layout][horizontal][reverse] {
|
||||
-ms-flex-direction: row-reverse;
|
||||
-webkit-flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
[layout][vertical] {
|
||||
-ms-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
[layout][vertical][reverse] {
|
||||
-ms-flex-direction: column-reverse;
|
||||
-webkit-flex-direction: column-reverse;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
[layout][wrap] {
|
||||
-ms-flex-wrap: wrap;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
[layout][wrap-reverse] {
|
||||
-ms-flex-wrap: wrap-reverse;
|
||||
-webkit-flex-wrap: wrap-reverse;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
|
||||
[flex] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[flex][auto] {
|
||||
-ms-flex: 1 1 auto;
|
||||
-webkit-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
[flex][none] {
|
||||
-ms-flex: none;
|
||||
-webkit-flex: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
[flex][one] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[flex][two] {
|
||||
-ms-flex: 2;
|
||||
-webkit-flex: 2;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
[flex][three] {
|
||||
-ms-flex: 3;
|
||||
-webkit-flex: 3;
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
[flex][four] {
|
||||
-ms-flex: 4;
|
||||
-webkit-flex: 4;
|
||||
flex: 4;
|
||||
}
|
||||
|
||||
[flex][five] {
|
||||
-ms-flex: 5;
|
||||
-webkit-flex: 5;
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
[flex][six] {
|
||||
-ms-flex: 6;
|
||||
-webkit-flex: 6;
|
||||
flex: 6;
|
||||
}
|
||||
|
||||
[flex][seven] {
|
||||
-ms-flex: 7;
|
||||
-webkit-flex: 7;
|
||||
flex: 7;
|
||||
}
|
||||
|
||||
[flex][eight] {
|
||||
-ms-flex: 8;
|
||||
-webkit-flex: 8;
|
||||
flex: 8;
|
||||
}
|
||||
|
||||
[flex][nine] {
|
||||
-ms-flex: 9;
|
||||
-webkit-flex: 9;
|
||||
flex: 9;
|
||||
}
|
||||
|
||||
[flex][ten] {
|
||||
-ms-flex: 10;
|
||||
-webkit-flex: 10;
|
||||
flex: 10;
|
||||
}
|
||||
|
||||
[flex][eleven] {
|
||||
-ms-flex: 11;
|
||||
-webkit-flex: 11;
|
||||
flex: 11;
|
||||
}
|
||||
|
||||
[flex][twelve] {
|
||||
-ms-flex: 12;
|
||||
-webkit-flex: 12;
|
||||
flex: 12;
|
||||
}
|
||||
|
||||
/* alignment in cross axis */
|
||||
|
||||
[layout][start] {
|
||||
-ms-flex-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
[layout][center], [layout][center-center] {
|
||||
-ms-flex-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[layout][end] {
|
||||
-ms-flex-align: end;
|
||||
-webkit-align-items: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* alignment in main axis */
|
||||
|
||||
[layout][start-justified] {
|
||||
-ms-flex-pack: start;
|
||||
-webkit-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[layout][center-justified], [layout][center-center] {
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[layout][end-justified] {
|
||||
-ms-flex-pack: end;
|
||||
-webkit-justify-content: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
[layout][around-justified] {
|
||||
-ms-flex-pack: around;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
[layout][justified] {
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* self alignment */
|
||||
|
||||
[self-start] {
|
||||
-ms-align-self: flex-start;
|
||||
-webkit-align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
[self-center] {
|
||||
-ms-align-self: center;
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
[self-end] {
|
||||
-ms-align-self: flex-end;
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
[self-stretch] {
|
||||
-ms-align-self: stretch;
|
||||
-webkit-align-self: stretch;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other Layout
|
||||
*******************************/
|
||||
|
||||
[block] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ie support for hidden */
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
[invisible] {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
[relative] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[fit] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[fullbleed] {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
[scroll] {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other
|
||||
*******************************/
|
||||
|
||||
[segment], segment {
|
||||
display: block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 1em 0.5em;
|
||||
padding: 1em;
|
||||
background-color: white;
|
||||
-webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
}
|
||||
BIN
components/polymer/dist/layout.css.gz
vendored
Normal file
BIN
components/polymer/dist/layout.css.gz
vendored
Normal file
Binary file not shown.
3
components/polymer/dist/polymer-data.min.html
vendored
Normal file
3
components/polymer/dist/polymer-data.min.html
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<link rel="stylesheet" href="layout.css">
|
||||
<script src="polymer.min.js><script>
|
||||
<script src="data.min.js><script>
|
||||
631
components/polymer/dist/polymer.html
vendored
Normal file
631
components/polymer/dist/polymer.html
vendored
Normal file
@@ -0,0 +1,631 @@
|
||||
<script>
|
||||
|
||||
Object.defineProperty(window, 'import', {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return (document._currentScript || document.currentScript).ownerDocument;
|
||||
}
|
||||
});
|
||||
|
||||
function extend(prototype, api) {
|
||||
if (prototype && api) {
|
||||
Object.getOwnPropertyNames(api).forEach(function(n) {
|
||||
var pd = Object.getOwnPropertyDescriptor(api, n);
|
||||
if (pd) {
|
||||
Object.defineProperty(prototype, n, pd);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
return prototype;
|
||||
};
|
||||
|
||||
Event.prototype.keys = {
|
||||
ESC_KEY: 27,
|
||||
ENTER_KEY: 13
|
||||
};
|
||||
|
||||
Base = {
|
||||
|
||||
|
||||
_features: [],
|
||||
|
||||
addFeature: function(feature) {
|
||||
this._features.push(feature);
|
||||
extend(Base, feature);
|
||||
delete Base.init;
|
||||
delete Base.register;
|
||||
},
|
||||
|
||||
registerCallback: function() {
|
||||
|
||||
var prototype = this;
|
||||
this.registerFeatures(prototype);
|
||||
this.registered(prototype);
|
||||
},
|
||||
|
||||
registered: function(prototype) {
|
||||
|
||||
},
|
||||
|
||||
registerFeatures: function(prototype) {
|
||||
var f$ = this._features;
|
||||
for (var i=0, n=f$.length; i<n && (f=f$[i]); i++) {
|
||||
f.register && f.register(prototype);
|
||||
}
|
||||
},
|
||||
|
||||
createdCallback: function() {
|
||||
this.root = this;
|
||||
this.beforeCreated();
|
||||
this.initFeatures();
|
||||
this.created();
|
||||
this.afterCreated();
|
||||
},
|
||||
|
||||
beforeCreated: function() {
|
||||
|
||||
},
|
||||
|
||||
initFeatures: function() {
|
||||
var f$ = this._features;
|
||||
for (var i=0, n=f$.length; i<n && (f=f$[i]); i++) {
|
||||
f.init && f.init.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
created: function() {
|
||||
|
||||
},
|
||||
|
||||
afterCreated: function() {
|
||||
|
||||
},
|
||||
|
||||
attachedCallback: function() {
|
||||
|
||||
this.attached();
|
||||
},
|
||||
|
||||
attached: function() {
|
||||
|
||||
},
|
||||
|
||||
detachedCallback: function() {
|
||||
|
||||
this.detached();
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
|
||||
},
|
||||
|
||||
attributeChangedCallback: function() {
|
||||
|
||||
this.attributeChanged.apply(this, arguments);
|
||||
},
|
||||
|
||||
attributeChanged: function() {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Base.__proto__ = HTMLElement.prototype;
|
||||
|
||||
Polymer = function(prototype) {
|
||||
prototype.__proto__ = Base;
|
||||
prototype.registerCallback();
|
||||
document.registerElement(prototype.name, {prototype: prototype});
|
||||
};
|
||||
|
||||
Polymer.log = {
|
||||
};
|
||||
|
||||
Base.addFeature({
|
||||
log: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = '[%s]: ' + args[0];
|
||||
args.splice(1, 0, this.localName);
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
published: {
|
||||
},
|
||||
|
||||
nob: Object.create(null),
|
||||
|
||||
register: function(prototype) {
|
||||
|
||||
if (prototype.addPropertyEffect) {
|
||||
for (var n in prototype.published) {
|
||||
if (prototype.isNotifyProperty(n)) {
|
||||
prototype.addPropertyEffect(n, 'notify');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getPublishInfo: function(property) {
|
||||
var p = this.published[property];
|
||||
if (typeof(p) === 'function') {
|
||||
p = this.published[property] = {
|
||||
type: p
|
||||
};
|
||||
}
|
||||
return p || Base.nob;
|
||||
},
|
||||
|
||||
getPublishedPropertyType: function(property) {
|
||||
return this.getPublishInfo(property).type;
|
||||
},
|
||||
|
||||
isReadOnlyProperty: function(property) {
|
||||
return this.getPublishInfo(property).readOnly;
|
||||
},
|
||||
|
||||
isNotifyProperty: function(property) {
|
||||
return this.getPublishInfo(property).notify;
|
||||
},
|
||||
|
||||
isReflectedProperty: function(property) {
|
||||
return this.getPublishInfo(property).reflect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
init: function() {
|
||||
if (this.hostAttributes) {
|
||||
this.cloneAttributes(this, this.hostAttributes);
|
||||
}
|
||||
},
|
||||
|
||||
cloneAttributes: function(node, attr$) {
|
||||
attr$.split(' ').forEach(function(a) {
|
||||
node.setAttribute(a, '');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
|
||||
|
||||
takeAttributes: function() {
|
||||
for (var n in this.published) {
|
||||
this.attributeChanged(n);
|
||||
}
|
||||
},
|
||||
|
||||
attributeChanged: function(name) {
|
||||
var type = this.getPublishedPropertyType(name);
|
||||
if (type) {
|
||||
this.deserialize(name, type);
|
||||
}
|
||||
},
|
||||
|
||||
deserialize: function(name, type) {
|
||||
var value = this.getAttribute(name);
|
||||
switch(type) {
|
||||
|
||||
case Number:
|
||||
value = Number(value) || this[name];
|
||||
break;
|
||||
|
||||
case Boolean:
|
||||
value = this.hasAttribute(name);
|
||||
break;
|
||||
|
||||
case Object:
|
||||
case Array:
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch(x) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case Date:
|
||||
value = Date.parse(value);
|
||||
break;
|
||||
|
||||
case String:
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
this[name] = value;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
register: function(prototype) {
|
||||
var script = (document._currentScript || document.currentScript);
|
||||
var prev = script.previousElementSibling;
|
||||
if (prev && prev.localName === 'template') {
|
||||
prototype._template = prev;
|
||||
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
stampTemplate: function(template) {
|
||||
this._stampTemplate(template || this._template, this.root);
|
||||
|
||||
if (window.CustomElements && CustomElements.upgradeSubtree) {
|
||||
CustomElements.upgradeSubtree(this.root);
|
||||
}
|
||||
},
|
||||
|
||||
_stampTemplate: function(template, target) {
|
||||
|
||||
target.insertBefore(this.instanceTemplate(template),
|
||||
target.firstElementChild);
|
||||
},
|
||||
|
||||
instanceTemplate: function(template) {
|
||||
return document.importNode(template.content, true);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
|
||||
isHost: true,
|
||||
|
||||
register: function(prototype) {
|
||||
var t = prototype._template;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
prototype._useContent = Boolean(t && t.content.querySelector('content'));
|
||||
},
|
||||
|
||||
poolContent: function() {
|
||||
|
||||
var pool = document.createDocumentFragment();
|
||||
while (this.firstChild) {
|
||||
pool.appendChild(this.firstChild);
|
||||
}
|
||||
this.contentPool = pool;
|
||||
|
||||
this.lightChildren =
|
||||
Array.prototype.slice.call(this.contentPool.childNodes, 0);
|
||||
},
|
||||
|
||||
distributeContent: function() {
|
||||
var content, pool = this.contentPool;
|
||||
|
||||
while (content = this.querySelector('content')) {
|
||||
var select = content.getAttribute('select');
|
||||
var frag = pool;
|
||||
if (select) {
|
||||
frag = document.createDocumentFragment();
|
||||
|
||||
|
||||
|
||||
var nodes = pool.querySelectorAll(select);
|
||||
for (var i=0, l=nodes.length; i<l; i++) {
|
||||
frag.appendChild(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
content.parentNode.replaceChild(frag, content);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
|
||||
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
if (!annote.parent) {
|
||||
return root;
|
||||
}
|
||||
var parent = this.findAnnotatedNode(root, annote.parent);
|
||||
|
||||
var nodes = (parent === this) ? parent.childNodes :
|
||||
(parent.lightChildren || parent.childNodes);
|
||||
return nodes[annote.index];
|
||||
},
|
||||
|
||||
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template) {
|
||||
prototype.parseAnnotations(prototype._template)
|
||||
}
|
||||
},
|
||||
|
||||
parseAnnotations: function(template) {
|
||||
|
||||
var map = [];
|
||||
this._parseNodeAnnotations(template.content, map);
|
||||
if (map.length) {
|
||||
template.map = map;
|
||||
}
|
||||
return template.map;
|
||||
},
|
||||
|
||||
_parseNodeAnnotations: function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ?
|
||||
this._parseTextNodeAnnotation(node, map) :
|
||||
this._parseElementAnnotations(node, map);
|
||||
},
|
||||
|
||||
_parseTextNodeAnnotation: function(node, map) {
|
||||
var v = node.textContent, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var annotation = {
|
||||
bindings: [{
|
||||
kind: 'text',
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
value: v.slice(2, -2)
|
||||
}]
|
||||
};
|
||||
map.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
},
|
||||
|
||||
_parseElementAnnotations: function(node, map) {
|
||||
var annote = {
|
||||
bindings: [],
|
||||
events: []
|
||||
};
|
||||
this._parseChildNodesAnnotations(node, annote, map);
|
||||
if (node.attributes) {
|
||||
this._parseNodeAttributeAnnotations(node, annote, map);
|
||||
}
|
||||
if (annote.bindings.length || annote.events.length || annote.id) {
|
||||
map.push(annote);
|
||||
}
|
||||
return annote;
|
||||
},
|
||||
|
||||
_parseChildNodesAnnotations: function(root, annotation, map) {
|
||||
if (root.firstChild) {
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var childAnnotation = this._parseNodeAnnotations(node, map);
|
||||
if (childAnnotation) {
|
||||
childAnnotation.parent = annotation;
|
||||
childAnnotation.index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotations: function(node, annotation) {
|
||||
for (var i=0, a; (a=node.attributes[i]); i++) {
|
||||
var n = a.name, v = a.value;
|
||||
|
||||
if (n === 'id') {
|
||||
annotation.id = v;
|
||||
}
|
||||
|
||||
else if (n.slice(0, 3) === 'on-') {
|
||||
annotation.events.push({
|
||||
name: n.slice(3),
|
||||
value: v
|
||||
});
|
||||
}
|
||||
|
||||
else {
|
||||
var b = this._parseNodeAttributeAnnotation(node, n, v);
|
||||
if (b) {
|
||||
annotation.bindings.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotation: function(node, n, v) {
|
||||
var escape = v.slice(0, 2), lastChar = n[n.length-1];
|
||||
var kind = 'attribute', mode = '';
|
||||
if (lastChar === '*' || lastChar === ':') {
|
||||
n = n.slice(0, -1);
|
||||
kind = 'property';
|
||||
mode = 'auto';
|
||||
}
|
||||
if (escape === '{{') {
|
||||
mode = 'auto';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (escape === '[[') {
|
||||
mode = 'manual';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (mode) {
|
||||
if (n === 'style') {
|
||||
kind = 'style';
|
||||
}
|
||||
return {
|
||||
kind: kind,
|
||||
mode: mode,
|
||||
name: n,
|
||||
value: v
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
$$: function(slctr) {
|
||||
return this.root.querySelector(slctr);
|
||||
},
|
||||
|
||||
|
||||
_marshalNodeReferences: function() {
|
||||
this.$ = {};
|
||||
var map = this._template && this._template.map;
|
||||
if (map) {
|
||||
map.forEach(function(annotation) {
|
||||
var id = annotation.id;
|
||||
if (id) {
|
||||
this.$[id] = this.findAnnotatedNode(this.root, annotation);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
_marshalAnnotatedNodes: function() {
|
||||
if (this._nodes) {
|
||||
this._nodes = this._nodes.map(function(a) {
|
||||
return this.findAnnotatedNode(this.root, a);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
listeners: {},
|
||||
|
||||
init: function() {
|
||||
},
|
||||
|
||||
|
||||
listenListeners: function() {
|
||||
for (var key in this.listeners) {
|
||||
var node = this, name = key;
|
||||
if (name.indexOf('.') >= 0) {
|
||||
name = name.split('.');
|
||||
node = this.$[name[0]];
|
||||
name = name[1];
|
||||
}
|
||||
this.listen(node, name, this.listeners[key]);
|
||||
}
|
||||
},
|
||||
|
||||
listen: function(node, eventName, methodName) {
|
||||
node.addEventListener(eventName, function(e) {
|
||||
this[methodName](e, e.detail);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
fire: function(type, detail, onNode, bubbles, cancelable) {
|
||||
var node = onNode || this;
|
||||
var detail = (detail === null || detail === undefined) ? {} : detail;
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles !== undefined ? bubbles : true,
|
||||
cancelable: cancelable !== undefined ? cancelable : true,
|
||||
detail: detail
|
||||
});
|
||||
node.dispatchEvent(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
keyPresses: {},
|
||||
|
||||
listenKeyPresses: function() {
|
||||
|
||||
for (var n in this.keyPresses) {
|
||||
|
||||
this.addEventListener('keypress', this.keyPressesFeatureHandler);
|
||||
|
||||
for (n in this.keyPresses) {
|
||||
if (typeof n === 'string') {
|
||||
this.keyPresses[Event.prototype.keys[n]] = this.keyPresses[n];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
keyPressesFeatureHandler: function(e) {
|
||||
var method = this.keyPresses[e.keyCode];
|
||||
if (method && this[method]) {
|
||||
return this[method](e.keyCode, e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
|
||||
|
||||
_setupAnnotatedListeners: function() {
|
||||
var map = this._template.map;
|
||||
if (map) {
|
||||
map.forEach(function(annotation) {
|
||||
var events = annotation.events;
|
||||
if (events && events.length) {
|
||||
var node = this.findAnnotatedNode(this.root, annotation);
|
||||
events.forEach(function(e) {
|
||||
|
||||
this.listen(node, e.name, e.value);
|
||||
}, this)
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
async: function(method) {
|
||||
var handled = false;
|
||||
var handle = function() {
|
||||
if (!handled) {
|
||||
handled = true;
|
||||
method.call(this);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
setTimeout(handle);
|
||||
requestAnimationFrame(handle);
|
||||
},
|
||||
|
||||
toggleAttribute: function(name, value) {
|
||||
this[value ? 'setAttribute' : 'removeAttribute'](name, '');
|
||||
},
|
||||
|
||||
attributeFollows: function(name, neo, old) {
|
||||
if (old) {
|
||||
old.removeAttribute(name);
|
||||
}
|
||||
if (neo) {
|
||||
neo.setAttribute(name, '');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
BIN
components/polymer/dist/polymer.html.gz
vendored
Normal file
BIN
components/polymer/dist/polymer.html.gz
vendored
Normal file
Binary file not shown.
812
components/polymer/dist/polymer.js
vendored
Normal file
812
components/polymer/dist/polymer.js
vendored
Normal file
@@ -0,0 +1,812 @@
|
||||
// a tiny bit of sugar for `document.currentScript.ownerDocument`
|
||||
// sadly `import` is reserved, so we need another name or
|
||||
// you have to refer to this value `window.import`
|
||||
Object.defineProperty(window, 'import', {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return (document._currentScript || document.currentScript).ownerDocument;
|
||||
}
|
||||
});
|
||||
|
||||
// copy own properties from 'api' to 'prototype, with name hinting for 'super'
|
||||
function extend(prototype, api) {
|
||||
if (prototype && api) {
|
||||
// use only own properties of 'api'
|
||||
Object.getOwnPropertyNames(api).forEach(function(n) {
|
||||
// acquire property descriptor
|
||||
var pd = Object.getOwnPropertyDescriptor(api, n);
|
||||
if (pd) {
|
||||
// clone property via descriptor
|
||||
Object.defineProperty(prototype, n, pd);
|
||||
// cache name-of-method for 'super' engine
|
||||
/*
|
||||
if (typeof pd.value == 'function') {
|
||||
// hint the 'super' engine
|
||||
pd.value.nom = n;
|
||||
}
|
||||
*/
|
||||
}
|
||||
});
|
||||
}
|
||||
return prototype;
|
||||
};
|
||||
|
||||
Event.prototype.keys = {
|
||||
ESC_KEY: 27,
|
||||
ENTER_KEY: 13
|
||||
};
|
||||
|
||||
Base = {
|
||||
|
||||
// (semi-)pluggable features for Base
|
||||
_features: [],
|
||||
|
||||
addFeature: function(feature) {
|
||||
this._features.push(feature);
|
||||
extend(Base, feature);
|
||||
delete Base.init;
|
||||
delete Base.register;
|
||||
},
|
||||
|
||||
registerCallback: function() {
|
||||
// `this` context is a prototype, not an instance
|
||||
var prototype = this;
|
||||
this.registerFeatures(prototype);
|
||||
this.registered(prototype);
|
||||
},
|
||||
|
||||
registered: function(prototype) {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
registerFeatures: function(prototype) {
|
||||
var f$ = this._features;
|
||||
for (var i=0, n=f$.length; i<n && (f=f$[i]); i++) {
|
||||
f.register && f.register(prototype);
|
||||
}
|
||||
},
|
||||
|
||||
createdCallback: function() {
|
||||
this.root = this;
|
||||
this.beforeCreated();
|
||||
this.initFeatures();
|
||||
this.created();
|
||||
this.afterCreated();
|
||||
},
|
||||
|
||||
beforeCreated: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
initFeatures: function() {
|
||||
var f$ = this._features;
|
||||
for (var i=0, n=f$.length; i<n && (f=f$[i]); i++) {
|
||||
f.init && f.init.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
afterCreated: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
attachedCallback: function() {
|
||||
// reserved for canonical behavior
|
||||
this.attached();
|
||||
},
|
||||
|
||||
attached: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
detachedCallback: function() {
|
||||
// reserved for canonical behavior
|
||||
this.detached();
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
attributeChangedCallback: function() {
|
||||
// reserved for canonical behavior
|
||||
this.attributeChanged.apply(this, arguments);
|
||||
},
|
||||
|
||||
attributeChanged: function() {
|
||||
// for overriding
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Base.__proto__ = HTMLElement.prototype;
|
||||
|
||||
Polymer = function(prototype) {
|
||||
prototype.__proto__ = Base;
|
||||
prototype.registerCallback();
|
||||
document.registerElement(prototype.name, {prototype: prototype});
|
||||
};
|
||||
|
||||
Polymer.log = {
|
||||
};
|
||||
|
||||
Base.addFeature({
|
||||
log: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = '[%s]: ' + args[0];
|
||||
args.splice(1, 0, this.localName);
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Define public property API.
|
||||
*
|
||||
* published: {
|
||||
* <property>: <Type || Object>,
|
||||
* ...
|
||||
*
|
||||
* // `foo` property can be assigned via attribute, will be deserialized to
|
||||
* // the specified data-type. All `published` properties have this behavior.
|
||||
* foo: String,
|
||||
*
|
||||
* // `bar` property has additional behavior specifiers.
|
||||
* // type: type for (de-)serialization
|
||||
* // notify: true to send a signal when a value is set to this property
|
||||
* // reflect: true to serialize the property to an attribute
|
||||
* // readOnly: if true, the property has no setter
|
||||
* bar: {
|
||||
* type: Boolean,
|
||||
* notify: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
published: {
|
||||
},
|
||||
|
||||
nob: Object.create(null),
|
||||
|
||||
register: function(prototype) {
|
||||
// TODO(sjmiles): improve layering
|
||||
if (prototype.addPropertyEffect) {
|
||||
for (var n in prototype.published) {
|
||||
if (prototype.isNotifyProperty(n)) {
|
||||
prototype.addPropertyEffect(n, 'notify');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getPublishInfo: function(property) {
|
||||
var p = this.published[property];
|
||||
if (typeof(p) === 'function') {
|
||||
p = this.published[property] = {
|
||||
type: p
|
||||
};
|
||||
}
|
||||
return p || Base.nob;
|
||||
},
|
||||
|
||||
getPublishedPropertyType: function(property) {
|
||||
return this.getPublishInfo(property).type;
|
||||
},
|
||||
|
||||
isReadOnlyProperty: function(property) {
|
||||
return this.getPublishInfo(property).readOnly;
|
||||
},
|
||||
|
||||
isNotifyProperty: function(property) {
|
||||
return this.getPublishInfo(property).notify;
|
||||
},
|
||||
|
||||
isReflectedProperty: function(property) {
|
||||
return this.getPublishInfo(property).reflect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Support for `hostAttributes` property.
|
||||
*
|
||||
* `hostAttributes` is a space separated string of attributes to
|
||||
* install on every instance.
|
||||
*
|
||||
* There is room for addition `attributes` features, namely:
|
||||
*
|
||||
* - potentially automatic handling of attributeChanged
|
||||
* - capturing initial configuration values from attributes
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
init: function() {
|
||||
if (this.hostAttributes) {
|
||||
this.cloneAttributes(this, this.hostAttributes);
|
||||
}
|
||||
},
|
||||
|
||||
cloneAttributes: function(node, attr$) {
|
||||
attr$.split(' ').forEach(function(a) {
|
||||
node.setAttribute(a, '');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Support for `published` property.
|
||||
*
|
||||
* `published` object maps the names of attributes that the user
|
||||
* wants mapped as inputs to properties to the data-type of that property.
|
||||
*
|
||||
* This feature overwrites `attributeChanged` to support automatic
|
||||
* propagation of attribute values at run-time.
|
||||
*
|
||||
* Static values in attributes at creation time can be captured by
|
||||
* `takeAttributes`.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* published: {
|
||||
* // values set to index attribute are converted to Number and propagated
|
||||
* // to index property
|
||||
* index: Number,
|
||||
* // values set to label attribute are propagated to index property
|
||||
* label: String
|
||||
* }
|
||||
*
|
||||
* Supported types:
|
||||
*
|
||||
* - Number
|
||||
* - Boolean
|
||||
* - String
|
||||
* - Object (JSON)
|
||||
* - Array (JSON)
|
||||
* - Date
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
/* attribute publishing feature, requires `published` feature */
|
||||
|
||||
takeAttributes: function() {
|
||||
for (var n in this.published) {
|
||||
this.attributeChanged(n);
|
||||
}
|
||||
},
|
||||
|
||||
attributeChanged: function(name) {
|
||||
var type = this.getPublishedPropertyType(name);
|
||||
if (type) {
|
||||
this.deserialize(name, type);
|
||||
}
|
||||
},
|
||||
|
||||
deserialize: function(name, type) {
|
||||
var value = this.getAttribute(name);
|
||||
switch(type) {
|
||||
|
||||
case Number:
|
||||
value = Number(value) || this[name];
|
||||
break;
|
||||
|
||||
case Boolean:
|
||||
value = this.hasAttribute(name);
|
||||
break;
|
||||
|
||||
case Object:
|
||||
case Array:
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch(x) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case Date:
|
||||
value = Date.parse(value);
|
||||
break;
|
||||
|
||||
case String:
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
this[name] = value;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
register: function(prototype) {
|
||||
var script = (document._currentScript || document.currentScript);
|
||||
var prev = script.previousElementSibling;
|
||||
if (prev && prev.localName === 'template') {
|
||||
prototype._template = prev;
|
||||
// TODO(sjmiles): probably should be it's own feature
|
||||
//this.decorateTemplateNodes(prototype._template.content,
|
||||
//prototype.name);
|
||||
}
|
||||
},
|
||||
|
||||
/*decorateTemplateNodes: function(root, name) {
|
||||
for (var node = root.firstElementChild; node;
|
||||
node = node.nextElementSibling) {
|
||||
node.setAttribute(name, '');
|
||||
this.decorateTemplateNodes(node, name);
|
||||
}
|
||||
},*/
|
||||
|
||||
stampTemplate: function(template) {
|
||||
this._stampTemplate(template || this._template, this.root);
|
||||
// TODO(sjmiles): hello prollyfill
|
||||
if (window.CustomElements && CustomElements.upgradeSubtree) {
|
||||
CustomElements.upgradeSubtree(this.root);
|
||||
}
|
||||
},
|
||||
|
||||
_stampTemplate: function(template, target) {
|
||||
// TODO(sorvell): light dom children will invalidate annotations.
|
||||
target.insertBefore(this.instanceTemplate(template),
|
||||
target.firstElementChild);
|
||||
},
|
||||
|
||||
instanceTemplate: function(template) {
|
||||
return document.importNode(template.content, true);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// TODO(sjmiles): ad-hoc signal for `ShadowDOM-lite-enhanced` nodes
|
||||
isHost: true,
|
||||
|
||||
register: function(prototype) {
|
||||
var t = prototype._template;
|
||||
// TODO(sorvell): is qsa is wrong here due to distribution?
|
||||
// TODO(sjmiles): No element should ever actually stamp a <content> node
|
||||
// into it's composed tree, so I believe this is actually correct.
|
||||
// However, I wonder if it's more efficient to capture during annotation
|
||||
// parsing, since the parse step does a tree walk in any case, and the
|
||||
// tree is smaller before element expansion.
|
||||
prototype._useContent = Boolean(t && t.content.querySelector('content'));
|
||||
},
|
||||
|
||||
poolContent: function() {
|
||||
// pool the light dom
|
||||
var pool = document.createDocumentFragment();
|
||||
while (this.firstChild) {
|
||||
pool.appendChild(this.firstChild);
|
||||
}
|
||||
this.contentPool = pool;
|
||||
// capture lightChildren to help reify dom scoping
|
||||
this.lightChildren =
|
||||
Array.prototype.slice.call(this.contentPool.childNodes, 0);
|
||||
},
|
||||
|
||||
distributeContent: function() {
|
||||
var content, pool = this.contentPool;
|
||||
// replace <content> with nodes teleported from pool
|
||||
while (content = this.querySelector('content')) {
|
||||
var select = content.getAttribute('select');
|
||||
var frag = pool;
|
||||
if (select) {
|
||||
frag = document.createDocumentFragment();
|
||||
// TODO(sjmiles): diverges from ShadowDOM spec behavior: ShadowDOM
|
||||
// only selects top level nodes from pool. Iterate children and match
|
||||
// manually instead.
|
||||
var nodes = pool.querySelectorAll(select);
|
||||
for (var i=0, l=nodes.length; i<l; i++) {
|
||||
frag.appendChild(nodes[i]);
|
||||
}
|
||||
}
|
||||
// content self-destructs
|
||||
content.parentNode.replaceChild(frag, content);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// TODO(sjmiles): this code was ported from an earlier mutation and needs
|
||||
// a cleanup to cleave closer to neoprene MO
|
||||
|
||||
/*
|
||||
|
||||
Scans a template to produce an annotation map that stores expression metadata
|
||||
and information that can be used to associate that metadata with the
|
||||
corresponding nodes in a template instance.
|
||||
|
||||
Supported annotations are:
|
||||
|
||||
* id attributes
|
||||
* binding annotations in text nodes
|
||||
* double-mustache expressions: {{expression}}
|
||||
* double-bracket expressions: [[expression]]
|
||||
* binding annotations in attributes
|
||||
* attribute-bind expressions: name="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name*="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name:="expression"
|
||||
* event annotations
|
||||
* event delegation directives: on-<eventName>="expression"
|
||||
|
||||
Generated data-structure:
|
||||
|
||||
[
|
||||
{
|
||||
id: '<id>',
|
||||
events: [
|
||||
{
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
bindings: [
|
||||
{
|
||||
kind: ['text'|'attribute'|'property'],
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
// TODO(sjmiles): confusingly, this is annotation-parent, not node-parent
|
||||
parent: <reference to parent annotation>,
|
||||
index: <integer index in parent's childNodes collection>
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
TODO(sjmiles): this module should produce either syntactic metadata
|
||||
(e.g. double-mustache, double-bracket, star-attr), or semantic metadata
|
||||
(e.g. manual-bind, auto-bind, property-bind). Right now it's half and half.
|
||||
|
||||
*/
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// instance-time
|
||||
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
if (!annote.parent) {
|
||||
return root;
|
||||
}
|
||||
var parent = this.findAnnotatedNode(root, annote.parent);
|
||||
// enforce locality.
|
||||
var nodes = (parent === this) ? parent.childNodes :
|
||||
(parent.lightChildren || parent.childNodes);
|
||||
return nodes[annote.index];
|
||||
},
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template) {
|
||||
prototype.parseAnnotations(prototype._template)
|
||||
}
|
||||
},
|
||||
|
||||
parseAnnotations: function(template) {
|
||||
// TODO(sjmiles): it's not a map, per se
|
||||
var map = [];
|
||||
this._parseNodeAnnotations(template.content, map);
|
||||
if (map.length) {
|
||||
template.map = map;
|
||||
}
|
||||
return template.map;
|
||||
},
|
||||
|
||||
_parseNodeAnnotations: function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ?
|
||||
this._parseTextNodeAnnotation(node, map) :
|
||||
this._parseElementAnnotations(node, map);
|
||||
},
|
||||
|
||||
_parseTextNodeAnnotation: function(node, map) {
|
||||
var v = node.textContent, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var annotation = {
|
||||
bindings: [{
|
||||
kind: 'text',
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
value: v.slice(2, -2)
|
||||
}]
|
||||
};
|
||||
map.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
},
|
||||
|
||||
_parseElementAnnotations: function(node, map) {
|
||||
var annote = {
|
||||
bindings: [],
|
||||
events: []
|
||||
};
|
||||
this._parseChildNodesAnnotations(node, annote, map);
|
||||
if (node.attributes) {
|
||||
this._parseNodeAttributeAnnotations(node, annote, map);
|
||||
}
|
||||
if (annote.bindings.length || annote.events.length || annote.id) {
|
||||
map.push(annote);
|
||||
}
|
||||
return annote;
|
||||
},
|
||||
|
||||
_parseChildNodesAnnotations: function(root, annotation, map) {
|
||||
if (root.firstChild) {
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var childAnnotation = this._parseNodeAnnotations(node, map);
|
||||
if (childAnnotation) {
|
||||
childAnnotation.parent = annotation;
|
||||
childAnnotation.index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotations: function(node, annotation) {
|
||||
for (var i=0, a; (a=node.attributes[i]); i++) {
|
||||
var n = a.name, v = a.value;
|
||||
// id
|
||||
if (n === 'id') {
|
||||
annotation.id = v;
|
||||
}
|
||||
// on-* (event)
|
||||
else if (n.slice(0, 3) === 'on-') {
|
||||
annotation.events.push({
|
||||
name: n.slice(3),
|
||||
value: v
|
||||
});
|
||||
}
|
||||
// other attribute
|
||||
else {
|
||||
var b = this._parseNodeAttributeAnnotation(node, n, v);
|
||||
if (b) {
|
||||
annotation.bindings.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotation: function(node, n, v) {
|
||||
var escape = v.slice(0, 2), lastChar = n[n.length-1];
|
||||
var kind = 'attribute', mode = '';
|
||||
if (lastChar === '*' || lastChar === ':') {
|
||||
n = n.slice(0, -1);
|
||||
kind = 'property';
|
||||
mode = 'auto';
|
||||
}
|
||||
if (escape === '{{') {
|
||||
mode = 'auto';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (escape === '[[') {
|
||||
mode = 'manual';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (mode) {
|
||||
if (n === 'style') {
|
||||
kind = 'style';
|
||||
}
|
||||
return {
|
||||
kind: kind,
|
||||
mode: mode,
|
||||
name: n,
|
||||
value: v
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// depends on `annotations` feature
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
$$: function(slctr) {
|
||||
return this.root.querySelector(slctr);
|
||||
},
|
||||
|
||||
// construct $ map (id based)
|
||||
_marshalNodeReferences: function() {
|
||||
this.$ = {};
|
||||
var map = this._template && this._template.map;
|
||||
if (map) {
|
||||
map.forEach(function(annotation) {
|
||||
var id = annotation.id;
|
||||
if (id) {
|
||||
this.$[id] = this.findAnnotatedNode(this.root, annotation);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
// concretize `_nodes` map (annotation based)
|
||||
_marshalAnnotatedNodes: function() {
|
||||
if (this._nodes) {
|
||||
this._nodes = this._nodes.map(function(a) {
|
||||
return this.findAnnotatedNode(this.root, a);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
listeners: {},
|
||||
|
||||
init: function() {
|
||||
},
|
||||
|
||||
// TODO(sjmiles): support for '.' notation requires 'nodes' feature
|
||||
listenListeners: function() {
|
||||
for (var key in this.listeners) {
|
||||
var node = this, name = key;
|
||||
if (name.indexOf('.') >= 0) {
|
||||
name = name.split('.');
|
||||
node = this.$[name[0]];
|
||||
name = name[1];
|
||||
}
|
||||
this.listen(node, name, this.listeners[key]);
|
||||
}
|
||||
},
|
||||
|
||||
listen: function(node, eventName, methodName) {
|
||||
node.addEventListener(eventName, function(e) {
|
||||
this[methodName](e, e.detail);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// TODO(sjmiles): use a dictionary for options after `detail`
|
||||
fire: function(type, detail, onNode, bubbles, cancelable) {
|
||||
var node = onNode || this;
|
||||
var detail = (detail === null || detail === undefined) ? {} : detail;
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles !== undefined ? bubbles : true,
|
||||
cancelable: cancelable !== undefined ? cancelable : true,
|
||||
detail: detail
|
||||
});
|
||||
node.dispatchEvent(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
keyPresses: {},
|
||||
|
||||
listenKeyPresses: function() {
|
||||
// for..in here to gate empty keyPresses object (iterates once or never)
|
||||
for (var n in this.keyPresses) {
|
||||
// only get here if there is something in keyPresses
|
||||
this.addEventListener('keypress', this.keyPressesFeatureHandler);
|
||||
// map string keys to numeric codes
|
||||
for (n in this.keyPresses) {
|
||||
if (typeof n === 'string') {
|
||||
this.keyPresses[Event.prototype.keys[n]] = this.keyPresses[n];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
keyPressesFeatureHandler: function(e) {
|
||||
var method = this.keyPresses[e.keyCode];
|
||||
if (method && this[method]) {
|
||||
return this[method](e.keyCode, e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Parses the annotations map created by `annotations` features to support
|
||||
* declarative events.
|
||||
*
|
||||
* Depends on `annotations` and `events` features.
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// instance-time
|
||||
|
||||
_setupAnnotatedListeners: function() {
|
||||
var map = this._template.map;
|
||||
if (map) {
|
||||
map.forEach(function(annotation) {
|
||||
var events = annotation.events;
|
||||
if (events && events.length) {
|
||||
var node = this.findAnnotatedNode(this.root, annotation);
|
||||
events.forEach(function(e) {
|
||||
//console.log('[%s] listening for [%s] on [%s]', e.value, e.name, node.localName);
|
||||
this.listen(node, e.name, e.value);
|
||||
}, this)
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
async: function(method) {
|
||||
var handled = false;
|
||||
var handle = function() {
|
||||
if (!handled) {
|
||||
handled = true;
|
||||
method.call(this);
|
||||
}
|
||||
}.bind(this);
|
||||
// minimize latency by racing requests
|
||||
setTimeout(handle);
|
||||
requestAnimationFrame(handle);
|
||||
},
|
||||
|
||||
toggleAttribute: function(name, value) {
|
||||
this[value ? 'setAttribute' : 'removeAttribute'](name, '');
|
||||
},
|
||||
|
||||
attributeFollows: function(name, neo, old) {
|
||||
if (old) {
|
||||
old.removeAttribute(name);
|
||||
}
|
||||
if (neo) {
|
||||
neo.setAttribute(name, '');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// TODO(sjmiles): hack
|
||||
Base.originalInitFeatures = Base.initFeatures;
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
initFeatures: function() {
|
||||
this.originalInitFeatures(this);
|
||||
this.features();
|
||||
},
|
||||
|
||||
features: function() {
|
||||
this.defaultFeatures();
|
||||
},
|
||||
|
||||
defaultFeatures: function() {
|
||||
if (this._useContent) {
|
||||
this.poolContent();
|
||||
}
|
||||
if (this._template) {
|
||||
this.stampTemplate();
|
||||
this._marshalNodeReferences();
|
||||
this._marshalAnnotatedNodes();
|
||||
this._setupAnnotatedListeners();
|
||||
if (this._setupBindListeners) {
|
||||
this._setupBindListeners();
|
||||
}
|
||||
}
|
||||
this.listenListeners();
|
||||
this.listenKeyPresses();
|
||||
if (this._useContent) {
|
||||
this.distributeContent();
|
||||
}
|
||||
this.takeAttributes();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Polymer.noFeatures = function() {
|
||||
};
|
||||
|
||||
Polymer.defaultFeatures = Base.defaultFeatures;
|
||||
|
||||
278
components/polymer/dist/polymer.min.html
vendored
Normal file
278
components/polymer/dist/polymer.min.html
vendored
Normal file
File diff suppressed because one or more lines are too long
1
components/polymer/dist/polymer.min.js
vendored
Normal file
1
components/polymer/dist/polymer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
components/polymer/dist/polymer.min.js.gz
vendored
Normal file
BIN
components/polymer/dist/polymer.min.js.gz
vendored
Normal file
Binary file not shown.
47
components/polymer/docs.html
Normal file
47
components/polymer/docs.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>x-doc-viewer</title>
|
||||
|
||||
<script src="../perf-lib/perf.js"></script>
|
||||
|
||||
<link rel="import" href="../elements/x-doc-viewer/x-doc-viewer.html">
|
||||
<link rel="import" href="../assets/icons.html">
|
||||
|
||||
<style>
|
||||
|
||||
html, body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body fullbleed vertical layout>
|
||||
|
||||
<script>console.perf();</script>
|
||||
|
||||
<x-doc-viewer flex sources='[
|
||||
"src/features/bind.html",
|
||||
"src/features/published.html",
|
||||
"src/features/annotations.html",
|
||||
"../elements/x-doc-viewer/x-doc-viewer.html"
|
||||
]'></x-doc-viewer>
|
||||
|
||||
<script>console.perfEnd();</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
323
components/polymer/explainer/Explaner.md
Normal file
323
components/polymer/explainer/Explaner.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# Polymer (Neoprene)
|
||||
|
||||
## Raw Custom Elements
|
||||
|
||||
Custom Elements are a powerful emerging web standard that allows developers to create their own elements by attaching a class to a tag-name.
|
||||
|
||||
### document.registerElement
|
||||
|
||||
The native API is very simple, it looks something like this:
|
||||
|
||||
```js
|
||||
document.registerElement(<name String>, {prototype: Object[, extends: String]});
|
||||
```
|
||||
|
||||
### Typical Boilerplate
|
||||
|
||||
There is a little bit of work one has to do to set up the class with the right prototypes and so on to construct a Custom Element. Here is an typical example (using ES5 syntax):
|
||||
|
||||
```js
|
||||
var ctor = function() {
|
||||
return document.createElement('x-custom');
|
||||
};
|
||||
ctor.prototype = Object.create(HTMLElement.prototype);
|
||||
ctor.prototype.constructor = ctor;
|
||||
ctor.prototype.createdCallback = function() {
|
||||
this.innerHTML = 'Hello World, I am a <b>Custom Element!</b>';
|
||||
}
|
||||
document.registerElement('x-custom', ctor);
|
||||
```
|
||||
|
||||
### Reluctant Polymer() Abstraction
|
||||
|
||||
By principle, Polymer team tries to avoid abstracting DOM APIs, especially new ones. But in this case we finally decided the ergonomic benefit was worth it. By wrapping `registerElement` in our own function, we can reduce the above boilerplate to:
|
||||
|
||||
```js
|
||||
var ctor = Polymer({
|
||||
name: 'x-custom',
|
||||
created: function() {
|
||||
this.innerHTML = 'Hello World, I am a <b>Custom Element!</b>';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Polymer() Does a Bit More
|
||||
|
||||
You might notice the `Polymer()` invocation defines `created` instead of `createdCallback`. This is a feature of `Polymer.Base`, a tiny prototype that `Polymer()` adds to your prototype chain as it's handling the boilerplate above. `Polymer.Base` hooks the standard Custom Element lifecycle callbacks to provide helper implementations. The hooks in turn call shorter-named lifecycle methods on your prototype.
|
||||
|
||||
- `created` instead of `createdCallback`
|
||||
- `attached` instead of `attachedCallback`
|
||||
- `detached` instead of `detachedCallback`
|
||||
- `attributeChanged` instead of `attributeChangedCallback`
|
||||
|
||||
You can always fallback to using the low-level methods if you wish (iow, you could simply implement `createdCallback` in your prototype).
|
||||
|
||||
`Polymer.Base` also implements `registerCallback` on your prototype. `Polymer()` calls `registerCallback` which allows `Polymer.Base` to supply a layering system for Polymer abstractions so that no element needs to pay for features it doesn't use.
|
||||
|
||||
## Features
|
||||
|
||||
By default, the default Polymer distribution include several features. Although `Polymer.Base` itself is tiny, if you examine `Polymer.Base` you will probably see several methods that have been plugged-in to that prototype by feature definitions. The next few sections will explain these features and why we include them in the default set. Keep in mind that it's entirely possible to construct custom feature sets, or even use a trivial, featureless form of `Polymer()`.
|
||||
|
||||
### Feature: _published_
|
||||
|
||||
The first feature implements support for the `published` property. By placing a object-valued `published` property on your prototype, let's you define various aspects of your custom-elements public API.
|
||||
|
||||
By itself, the `published` feature **doesn't do anything**. It only provides API for asking questions about these special properties (see [link to docs] for details).
|
||||
|
||||
```js
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
published: {
|
||||
user: String,
|
||||
isHappy: Boolean,
|
||||
count: {
|
||||
type: Number,
|
||||
readOnly: true,
|
||||
notify: true
|
||||
}
|
||||
},
|
||||
|
||||
created: function() {
|
||||
this.innerHTML = 'Hello World, I am a <b>Custom Element!</b>';
|
||||
}
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
Remember that the fields assigned to `count`, such as `readOnly` and `notify` don't do anything by themselves, it requires other features to give them life.
|
||||
|
||||
### Feature: _attributes_
|
||||
|
||||
Many custom elements want to support configuration using HTML attributes. Custom Elements provides the `attributeChanged` callback gives us the raw API for this ability, but then we have to deal with initialization and type conversion (attributes are always strings). Here is an example of a custom element that supports a `user` attribute using the raw API.
|
||||
|
||||
```js
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
created: function() {
|
||||
// handle any initial value
|
||||
this.attributeChanged('user');
|
||||
// render
|
||||
this.innerHTML = 'Hello World, my user is ' + (this.user || 'nobody') + '.';
|
||||
},
|
||||
|
||||
attributeChanged: function(name) {
|
||||
switch(name) {
|
||||
case 'user':
|
||||
// pretty easy since user is a String, for other types
|
||||
// we have to do more work
|
||||
if (this.hasAttribute('user')) {
|
||||
this.user = this.getAttribute('user');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
Although it's relatively simple, having to write this code becomes annoying when working with multiple attributes or non-String types. It's also not very DRY.
|
||||
|
||||
Instead, Polymer's `attributes` feature handles this work for you (using the `published` feature data). If an attribute is set that matches a property listed in the `published` object, the value is captured into the matching property. Strings are automatically converted to the published type.
|
||||
|
||||
The type system includes support for Object values expressed as JSON, or Date objects expressed as any Date-parsable string representation. Boolean properties are mapped to Boolean attributes, in other words, if the attribute exists at all, it's value is true, regardless of it's string-value (and the value is only false if the attribute does not exist).
|
||||
|
||||
Here is the equivalent of the above code, taking advantage of the `attributes` feature.
|
||||
|
||||
```html
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
published: {
|
||||
user: String
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// render
|
||||
this.innerHTML = 'Hello World, my user is ' + (this.user || 'nobody') + '.';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<x-custom user="Scott"></x-custom>
|
||||
```
|
||||
|
||||
### [ToDoc] attributes:hostAttributes
|
||||
|
||||
### Feature: _template_
|
||||
|
||||
HTML templates are an emerging web standard that we like to consider part of the Web Components family. Templates are a great way to provide archetypal DOM content for your custom element, and this is where the `template` feature comes in.
|
||||
|
||||
As usual, we started by writing basic template support by hand. It generally looks something like this:
|
||||
|
||||
```html
|
||||
<template>
|
||||
|
||||
Hello World from x-custom!
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
created: function() {
|
||||
var template = <find the template somehow>;
|
||||
var instance = document.importNode(template.content, true);
|
||||
this.appendChild(instance);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
Again, it's simple, but it's a common pattern, so the `template` feature does it automatically. By default it looks for a template as the first element before the script, so our code can look like this:
|
||||
|
||||
```html
|
||||
<template>
|
||||
|
||||
Hello World from x-custom!
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom'
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
### Feature: _annotations_
|
||||
|
||||
Most elements need to customize the DOM instanced from a template. For this reason, it's handy to encode markers into your template to indicate special nodes, attributes, or text. Polymer calls these markers _annotations_. The `annotations` feature scans the template (once per element, at registration time) and builds a data-structure into the prototype that identifies markers it finds in the DOM (see [link to docs] for details). Normally you do not need to work with this data directly, Polymer does it for you.
|
||||
|
||||
### Feature: _annotations-nodes_
|
||||
|
||||
Traditionally, modifying DOM is done by querying for elements to manipulate. Here is an example:
|
||||
|
||||
```html
|
||||
<template>
|
||||
|
||||
Hello World from <span id="name"></span>!
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
created: function() {
|
||||
this.querySelector("#name").textContent = this.name;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
This example is very simple. But in real projects, repeating queries is inefficient, so query results are often stored (memoized). Also, as DOM composition becomes more tricky, crafting correct queries can be difficult. For these reasons, automatically capturing nodes makes a good feature.
|
||||
|
||||
The `annotations-nodes` feature builds a map of instance nodes by `id` in `this.$` (using the `annotations` feature data). Here is how the `annotations-nodes` feature simplifies the above example.
|
||||
|
||||
```html
|
||||
<template>
|
||||
|
||||
Hello World from <span id="name"></span>!
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
created: function() {
|
||||
this.$.name.textContent = this.name;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
### Feature: _annotations-events_
|
||||
|
||||
Most elements also need to listen for events. The standard DOM method `addEventListener` provides the low-level support:
|
||||
|
||||
```html
|
||||
<template>
|
||||
|
||||
<button id="button">Kick Me</button>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
created: function() {
|
||||
this.$.button.addEventListener('click', function() {
|
||||
alert('Ow!');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
Again, this is pretty simple, but it's so common that it's worth making even simpler. The `annotations-events` feature supports declaring event listeners directly in our template.
|
||||
|
||||
Declaring listeners in the template is convenient, and also helps us decouple view from behavior.
|
||||
|
||||
```html
|
||||
<template>
|
||||
|
||||
<button on-click="kickAction">Kick Me</button>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
kickAction: function() {
|
||||
alert('Ow!');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
Notice that the `kickAction` method doesn't know anything about `button`. If we decided that kicking should be performed by a key-press, or a menu-item, the element code doesn't need to know. We can change the UI however we want. Also notice that by attaching the event declaratively, we have removed the need to give the button an id.
|
||||
|
||||
### [ToDoc] events feature
|
||||
|
||||
### [ToDoc] keys feature
|
||||
|
||||
### [ToDoc] content feature
|
||||
|
||||
|
||||
BIN
components/polymer/explainer/data-bind.png
Normal file
BIN
components/polymer/explainer/data-bind.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
components/polymer/explainer/data-bind.vsdx
Normal file
BIN
components/polymer/explainer/data-bind.vsdx
Normal file
Binary file not shown.
41
components/polymer/explainer/samples.html
Normal file
41
components/polymer/explainer/samples.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>Explainer Samples</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="import" href="../polymer.html">
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
|
||||
name: 'x-custom',
|
||||
|
||||
published: {
|
||||
user: String
|
||||
},
|
||||
|
||||
created: function() {
|
||||
this.innerHTML = 'Hello World, my user is ' + (this.user || 'nobody') + '.';
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<x-custom user="Scott"></x-custom>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
61
components/polymer/polymer.html
Normal file
61
components/polymer/polymer.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<link rel="import" href="src/polymer.html">
|
||||
<link rel="import" href="src/features/log.html">
|
||||
<link rel="import" href="src/features/published.html">
|
||||
<link rel="import" href="src/features/attributes.html">
|
||||
<link rel="import" href="src/features/template.html">
|
||||
<link rel="import" href="src/features/content.html">
|
||||
<link rel="import" href="src/features/annotations.html">
|
||||
<link rel="import" href="src/features/annotations-nodes.html">
|
||||
<link rel="import" href="src/features/events.html">
|
||||
<link rel="import" href="src/features/keys.html">
|
||||
<link rel="import" href="src/features/annotations-events.html">
|
||||
<link rel="import" href="src/features/utils.html">
|
||||
<link rel="import" href="src/features/layout.html">
|
||||
|
||||
<script>
|
||||
|
||||
// TODO(sjmiles): hack
|
||||
Base.originalInitFeatures = Base.initFeatures;
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
initFeatures: function() {
|
||||
// TODO(sjmiles): hack to make sure this feature goes last
|
||||
this.originalInitFeatures(this);
|
||||
this.features();
|
||||
},
|
||||
|
||||
features: function() {
|
||||
this.defaultFeatures();
|
||||
},
|
||||
|
||||
defaultFeatures: function() {
|
||||
if (this._useContent) {
|
||||
this.poolContent();
|
||||
}
|
||||
if (this._template) {
|
||||
this.stampTemplate();
|
||||
this._marshalNodeReferences();
|
||||
this._marshalAnnotatedNodes();
|
||||
this._setupAnnotatedListeners();
|
||||
if (this._setupBindListeners) {
|
||||
this._setupBindListeners();
|
||||
}
|
||||
}
|
||||
this.listenListeners();
|
||||
this.listenKeyPresses();
|
||||
if (this._useContent) {
|
||||
this.distributeContent();
|
||||
}
|
||||
this.takeAttributes();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Polymer.noFeatures = function() {
|
||||
};
|
||||
|
||||
Polymer.defaultFeatures = Base.defaultFeatures;
|
||||
|
||||
</script>
|
||||
|
||||
89
components/polymer/src/base.html
Normal file
89
components/polymer/src/base.html
Normal file
@@ -0,0 +1,89 @@
|
||||
<script>
|
||||
|
||||
Base = {
|
||||
|
||||
// (semi-)pluggable features for Base
|
||||
_features: [],
|
||||
|
||||
addFeature: function(feature) {
|
||||
this._features.push(feature);
|
||||
extend(Base, feature);
|
||||
delete Base.init;
|
||||
delete Base.register;
|
||||
},
|
||||
|
||||
registerCallback: function() {
|
||||
// `this` context is a prototype, not an instance
|
||||
var prototype = this;
|
||||
this.registerFeatures(prototype);
|
||||
this.registered(prototype);
|
||||
},
|
||||
|
||||
registered: function(prototype) {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
registerFeatures: function(prototype) {
|
||||
var f$ = this._features;
|
||||
for (var i=0, n=f$.length; i<n && (f=f$[i]); i++) {
|
||||
f.register && f.register(prototype);
|
||||
}
|
||||
},
|
||||
|
||||
createdCallback: function() {
|
||||
this.root = this;
|
||||
this.beforeCreated();
|
||||
this.initFeatures();
|
||||
this.created();
|
||||
this.afterCreated();
|
||||
},
|
||||
|
||||
beforeCreated: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
initFeatures: function() {
|
||||
var f$ = this._features;
|
||||
for (var i=0, n=f$.length; i<n && (f=f$[i]); i++) {
|
||||
f.init && f.init.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
afterCreated: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
attachedCallback: function() {
|
||||
// reserved for canonical behavior
|
||||
this.attached();
|
||||
},
|
||||
|
||||
attached: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
detachedCallback: function() {
|
||||
// reserved for canonical behavior
|
||||
this.detached();
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
// for overriding
|
||||
},
|
||||
|
||||
attributeChangedCallback: function() {
|
||||
// reserved for canonical behavior
|
||||
this.attributeChanged.apply(this, arguments);
|
||||
},
|
||||
|
||||
attributeChanged: function() {
|
||||
// for overriding
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
</script>
|
||||
66
components/polymer/src/features/annotations-bind.html
Normal file
66
components/polymer/src/features/annotations-bind.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<link rel="import" href="annotations.html">
|
||||
|
||||
<script>
|
||||
|
||||
/*
|
||||
* Parses the annotations map created by `annotations` features to perform
|
||||
* declarative desugaring.
|
||||
*
|
||||
* Depends on `annotations` feature and `bind` feature.
|
||||
*
|
||||
* Two tasks are supported:
|
||||
*
|
||||
* - nodes with 'id' are described in a virtual annotation map at
|
||||
* registration time. This map is then concretized per instance.
|
||||
*
|
||||
* - Simple mustache expressions consisting of a single property name
|
||||
* in a `textContent` context are bound using `bind` features
|
||||
* `bindMethod`. In this mode, the bound method is constructed at
|
||||
* registration time, so marshaling is done done via the concretized
|
||||
* `_nodes` at every access.
|
||||
*
|
||||
* TODO(sjmiles): ph3ar general confusion between registration and
|
||||
* instance time tasks. Is there a cleaner way to disambiguate?
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template && prototype._template.map) {
|
||||
this._preprocessBindAnnotations(prototype, prototype._template.map);
|
||||
}
|
||||
},
|
||||
|
||||
// construct binding meta-data at *registration* time
|
||||
_preprocessBindAnnotations: function(prototype, map) {
|
||||
// create a virtual annotation map, must be concretized at instance time
|
||||
prototype._nodes = [];
|
||||
// process annotations that have been parsed from template
|
||||
map.forEach(function(annotation) {
|
||||
// where to find the node in the concretized map
|
||||
var index = prototype._nodes.push(annotation) - 1;
|
||||
// TODO(sjmiles): we need to support multi-bind, right now you only get
|
||||
// one (not including kind === `id`)
|
||||
annotation.bindings.forEach(function(binding) {
|
||||
prototype._bindAnnotationBinding(binding, index);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// _nodes[index][<binding.name=>]{{binding.value}}
|
||||
_bindAnnotationBinding: function(binding, index) {
|
||||
// capture the node index
|
||||
binding.index = index;
|
||||
// discover top-level property (model) from path
|
||||
var path = binding.value;
|
||||
var i = path.indexOf('.');
|
||||
// [name=]{{model[.subpath]}}
|
||||
var model = (i >= 0) ? path.slice(0, i) : path;
|
||||
// add 'annotation' binding effect for property 'model'
|
||||
this.addPropertyEffect(model, 'annotation', binding);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
34
components/polymer/src/features/annotations-events.html
Normal file
34
components/polymer/src/features/annotations-events.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<link rel="import" href="annotations.html">
|
||||
|
||||
<script>
|
||||
|
||||
/*
|
||||
* Parses the annotations map created by `annotations` features to support
|
||||
* declarative events.
|
||||
*
|
||||
* Depends on `annotations` and `events` features.
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// instance-time
|
||||
|
||||
_setupAnnotatedListeners: function() {
|
||||
var map = this._template.map;
|
||||
if (map) {
|
||||
map.forEach(function(annotation) {
|
||||
var events = annotation.events;
|
||||
if (events && events.length) {
|
||||
var node = this.findAnnotatedNode(this.root, annotation);
|
||||
events.forEach(function(e) {
|
||||
//console.log('[%s] listening for [%s] on [%s]', e.value, e.name, node.localName);
|
||||
this.listen(node, e.name, e.value);
|
||||
}, this)
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
36
components/polymer/src/features/annotations-nodes.html
Normal file
36
components/polymer/src/features/annotations-nodes.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<script>
|
||||
|
||||
// depends on `annotations` feature
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
$$: function(slctr) {
|
||||
return this.root.querySelector(slctr);
|
||||
},
|
||||
|
||||
// construct $ map (id based)
|
||||
_marshalNodeReferences: function() {
|
||||
this.$ = {};
|
||||
var map = this._template && this._template.map;
|
||||
if (map) {
|
||||
map.forEach(function(annotation) {
|
||||
var id = annotation.id;
|
||||
if (id) {
|
||||
this.$[id] = this.findAnnotatedNode(this.root, annotation);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
// concretize `_nodes` map (annotation based)
|
||||
_marshalAnnotatedNodes: function() {
|
||||
if (this._nodes) {
|
||||
this._nodes = this._nodes.map(function(a) {
|
||||
return this.findAnnotatedNode(this.root, a);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
226
components/polymer/src/features/annotations.html
Normal file
226
components/polymer/src/features/annotations.html
Normal file
@@ -0,0 +1,226 @@
|
||||
<script>
|
||||
|
||||
/**
|
||||
* Scans a template (once per prototype) to produce an annotation map that
|
||||
* stores expression metadata and information to associate the metadata with
|
||||
* nodes in an instance.
|
||||
*
|
||||
* Supported expressions include:
|
||||
*
|
||||
* Double-mustache annotations in text content. The annotation must be the only
|
||||
* content in the tag, compound expressions are not supported.
|
||||
*
|
||||
* <[tag]>{{path.to.host.property}}<[tag]>
|
||||
*
|
||||
* Double-mustache annotations in an attribute.
|
||||
*
|
||||
* <[tag] someAttribute="{{path.to.host.property}}"><[tag]>
|
||||
*
|
||||
* Only immediate host properties can automatically trigger side-effects.
|
||||
* Setting `host.path` in the example above triggers the binding, setting
|
||||
* `host.path.to.host.property` does not.
|
||||
*
|
||||
* `on-` style event declarations.
|
||||
*
|
||||
* <[tag] on-<event-name>="{{hostMethodName}}"><[tag]>
|
||||
*
|
||||
* Note that the `annotations` feature does not actually implement the behaviors
|
||||
* associated with these expressions, it only captures the data. Other
|
||||
* `annotations-*` features contain the actual implementations.
|
||||
*
|
||||
* @class feature: annotations
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Scans a template to produce an annotation map that stores expression metadata
|
||||
and information that associates the metadata to nodes in a template instance.
|
||||
|
||||
Supported annotations are:
|
||||
|
||||
* id attributes
|
||||
* binding annotations in text nodes
|
||||
* double-mustache expressions: {{expression}}
|
||||
* double-bracket expressions: [[expression]]
|
||||
* binding annotations in attributes
|
||||
* attribute-bind expressions: name="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name*="{{expression}} || [[expression]]"
|
||||
* property-bind expressions: name:="expression"
|
||||
* event annotations
|
||||
* event delegation directives: on-<eventName>="expression"
|
||||
|
||||
Generated data-structure:
|
||||
|
||||
[
|
||||
{
|
||||
id: '<id>',
|
||||
events: [
|
||||
{
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
bindings: [
|
||||
{
|
||||
kind: ['text'|'attribute'|'property'],
|
||||
mode: ['auto'|''],
|
||||
name: '<name>'
|
||||
value: '<expression>'
|
||||
}, ...
|
||||
],
|
||||
// TODO(sjmiles): confusingly, this is annotation-parent, not node-parent
|
||||
parent: <reference to parent annotation>,
|
||||
index: <integer index in parent's childNodes collection>
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
TODO(sjmiles): this module should produce either syntactic metadata
|
||||
(e.g. double-mustache, double-bracket, star-attr), or semantic metadata
|
||||
(e.g. manual-bind, auto-bind, property-bind). Right now it's half and half.
|
||||
|
||||
*/
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// instance-time
|
||||
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
if (!annote.parent) {
|
||||
return root;
|
||||
}
|
||||
var parent = this.findAnnotatedNode(root, annote.parent);
|
||||
// enforce locality.
|
||||
var nodes = (parent === this) ? parent.childNodes :
|
||||
(parent.lightChildren || parent.childNodes);
|
||||
return nodes[annote.index];
|
||||
},
|
||||
|
||||
// registration-time
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype._template) {
|
||||
prototype.parseAnnotations(prototype._template)
|
||||
}
|
||||
},
|
||||
|
||||
parseAnnotations: function(template) {
|
||||
// TODO(sjmiles): it's not a map, per se
|
||||
var map = [];
|
||||
this._parseNodeAnnotations(template.content, map);
|
||||
if (map.length) {
|
||||
template.map = map;
|
||||
}
|
||||
return template.map;
|
||||
},
|
||||
|
||||
_parseNodeAnnotations: function(node, map) {
|
||||
return node.nodeType === Node.TEXT_NODE ?
|
||||
this._parseTextNodeAnnotation(node, map) :
|
||||
this._parseElementAnnotations(node, map);
|
||||
},
|
||||
|
||||
_parseTextNodeAnnotation: function(node, map) {
|
||||
var v = node.textContent, escape = v.slice(0, 2);
|
||||
if (escape === '{{' || escape === '[[') {
|
||||
var annotation = {
|
||||
bindings: [{
|
||||
kind: 'text',
|
||||
mode: escape === '{{' ? 'auto' : '',
|
||||
value: v.slice(2, -2)
|
||||
}]
|
||||
};
|
||||
node.textContent = '';
|
||||
map.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
},
|
||||
|
||||
_parseElementAnnotations: function(node, map) {
|
||||
var annote = {
|
||||
bindings: [],
|
||||
events: []
|
||||
};
|
||||
this._parseChildNodesAnnotations(node, annote, map);
|
||||
if (node.attributes) {
|
||||
this._parseNodeAttributeAnnotations(node, annote, map);
|
||||
}
|
||||
if (annote.bindings.length || annote.events.length || annote.id) {
|
||||
map.push(annote);
|
||||
}
|
||||
return annote;
|
||||
},
|
||||
|
||||
_parseChildNodesAnnotations: function(root, annotation, map) {
|
||||
if (root.firstChild) {
|
||||
for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++) {
|
||||
var childAnnotation = this._parseNodeAnnotations(node, map);
|
||||
if (childAnnotation) {
|
||||
childAnnotation.parent = annotation;
|
||||
childAnnotation.index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotations: function(node, annotation) {
|
||||
for (var i=0, a; (a=node.attributes[i]); i++) {
|
||||
var n = a.name, v = a.value;
|
||||
// id
|
||||
if (n === 'id') {
|
||||
annotation.id = v;
|
||||
}
|
||||
// on-* (event)
|
||||
else if (n.slice(0, 3) === 'on-') {
|
||||
i--;
|
||||
node.removeAttribute(n);
|
||||
annotation.events.push({
|
||||
name: n.slice(3),
|
||||
value: v
|
||||
});
|
||||
}
|
||||
// other attribute
|
||||
else {
|
||||
var b = this._parseNodeAttributeAnnotation(node, n, v);
|
||||
if (b) {
|
||||
i--;
|
||||
annotation.bindings.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseNodeAttributeAnnotation: function(node, n, v) {
|
||||
var escape = v.slice(0, 2), lastChar = n[n.length-1];
|
||||
var kind = 'attribute', mode = '';
|
||||
if (lastChar === '*' || lastChar === ':') {
|
||||
n = n.slice(0, -1);
|
||||
kind = 'property';
|
||||
mode = 'auto';
|
||||
}
|
||||
if (escape === '{{') {
|
||||
mode = 'auto';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (escape === '[[') {
|
||||
mode = 'manual';
|
||||
v = v.slice(2, -2);
|
||||
}
|
||||
if (mode) {
|
||||
if (n === 'style') {
|
||||
kind = 'style';
|
||||
}
|
||||
node.removeAttribute(n);
|
||||
return {
|
||||
kind: kind,
|
||||
mode: mode,
|
||||
name: n,
|
||||
value: v
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
115
components/polymer/src/features/attributes.html
Normal file
115
components/polymer/src/features/attributes.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<script>
|
||||
|
||||
/*
|
||||
* Support for `hostAttributes` property.
|
||||
*
|
||||
* `hostAttributes` is a space separated string of attributes to
|
||||
* install on every instance.
|
||||
*
|
||||
* There is room for addition `attributes` features, namely:
|
||||
*
|
||||
* - potentially automatic handling of attributeChanged
|
||||
* - capturing initial configuration values from attributes
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
init: function() {
|
||||
if (this.hostAttributes) {
|
||||
this.cloneAttributes(this, this.hostAttributes);
|
||||
}
|
||||
},
|
||||
|
||||
cloneAttributes: function(node, attr$) {
|
||||
attr$.split(' ').forEach(function(a) {
|
||||
node.setAttribute(a, '');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Support for `published` property.
|
||||
*
|
||||
* `published` object maps the names of attributes that the user
|
||||
* wants mapped as inputs to properties to the data-type of that property.
|
||||
*
|
||||
* This feature overwrites `attributeChanged` to support automatic
|
||||
* propagation of attribute values at run-time.
|
||||
*
|
||||
* Static values in attributes at creation time can be captured by
|
||||
* `takeAttributes`.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* published: {
|
||||
* // values set to index attribute are converted to Number and propagated
|
||||
* // to index property
|
||||
* index: Number,
|
||||
* // values set to label attribute are propagated to index property
|
||||
* label: String
|
||||
* }
|
||||
*
|
||||
* Supported types:
|
||||
*
|
||||
* - Number
|
||||
* - Boolean
|
||||
* - String
|
||||
* - Object (JSON)
|
||||
* - Array (JSON)
|
||||
* - Date
|
||||
*
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
/* attribute publishing feature, requires `published` feature */
|
||||
|
||||
takeAttributes: function() {
|
||||
for (var name in this.published) {
|
||||
var type = this.getPublishedPropertyType(name);
|
||||
if (type === Boolean || this.hasAttribute(name)) {
|
||||
this.attributeChanged(name, type);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
attributeChanged: function(name, type) {
|
||||
var type = type || this.getPublishedPropertyType(name);
|
||||
if (type) {
|
||||
this[name] = this.deserialize(name, this.getAttribute(name), type);
|
||||
}
|
||||
},
|
||||
|
||||
deserialize: function(name, value, type) {
|
||||
switch(type) {
|
||||
case Number:
|
||||
value = Number(value);
|
||||
break;
|
||||
|
||||
case Boolean:
|
||||
value = this.hasAttribute(name);
|
||||
break;
|
||||
|
||||
case Object:
|
||||
case Array:
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch(x) {
|
||||
value = '[invalid JSON]';
|
||||
}
|
||||
break;
|
||||
|
||||
case Date:
|
||||
value = Date.parse(value);
|
||||
break;
|
||||
|
||||
case String:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
216
components/polymer/src/features/bind-effects.html
Normal file
216
components/polymer/src/features/bind-effects.html
Normal file
@@ -0,0 +1,216 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// per instance
|
||||
|
||||
init: function() {
|
||||
this._data = Object.create(null);
|
||||
},
|
||||
|
||||
_setupBindListeners: function() {
|
||||
this._bindListeners.forEach(this._setupBindListener, this);
|
||||
},
|
||||
|
||||
_setupBindListener: function(info) {
|
||||
// <node>.on.<property>-changed: <path]> = e.detail.value
|
||||
//console.log('[_setupBindListener]: [%s][%s] listening for [%s][%s-changed]', this.localName, info.path, info.id || info.index, info.property);
|
||||
var node = info.id ? this.$[info.id] : this._nodes[info.index];
|
||||
var fn = new Function('e', 'this.' + info.path + ' = e.detail.value;');
|
||||
node.addEventListener(info.property + '-changed', fn.bind(this));
|
||||
},
|
||||
|
||||
_notifyChange: function(property) {
|
||||
this.fire(property + '-changed', {value: this[property]}, null, false);
|
||||
},
|
||||
|
||||
_setDataCalls: 0,
|
||||
_setData: function(property, value) {
|
||||
Base._setDataCalls++;
|
||||
var old = this._data[property];
|
||||
if (old !== value) {
|
||||
this._data[property] = value;
|
||||
}
|
||||
return old;
|
||||
},
|
||||
|
||||
// per prototype
|
||||
|
||||
register: function(prototype) {
|
||||
prototype._bindListeners = [];
|
||||
prototype._createBindings();
|
||||
},
|
||||
|
||||
_createBindings: function() {
|
||||
var fx$ = this._propertyEffects;
|
||||
if (fx$) {
|
||||
//console.group(this.name);
|
||||
for (var n in fx$) {
|
||||
//console.group(n);
|
||||
var fx = fx$[n];
|
||||
fx.sort(this._sortPropertyEffects);
|
||||
//console.log(fx);
|
||||
var compiledEffects = fx.map(function(x) {
|
||||
return this._buildEffect(n, x);
|
||||
}, this);
|
||||
this._bindPropertyEffects(n, compiledEffects);
|
||||
//console.log(fxt.join('\n'));
|
||||
//console.groupEnd();
|
||||
}
|
||||
//console.groupEnd();
|
||||
}
|
||||
},
|
||||
|
||||
_sortPropertyEffects: function(a, b) {
|
||||
switch (a.kind) {
|
||||
case 'compute':
|
||||
return b.kind === 'compute' ? 0 : -1;
|
||||
case 'notify':
|
||||
return b.kind === 'notify' ? 0 : 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
_buildEffect: function(property, fx) {
|
||||
return this['_' + fx.kind + 'EffectBuilder'](property, fx.effect);
|
||||
},
|
||||
|
||||
_methodEffectBuilder: function(source, effect) {
|
||||
// TODO(sjmiles): validation system requires a blessed
|
||||
// validator effect which needs to be processed first.
|
||||
/*
|
||||
if (typeof this[effect] === 'function') {
|
||||
return [
|
||||
'var validated = this.' + effect + '(value, old)',
|
||||
'if (validated !== undefined) {',
|
||||
' // recurse',
|
||||
' this[property] = validated;',
|
||||
' return;',
|
||||
'}'
|
||||
].join('\n');
|
||||
}
|
||||
*/
|
||||
//
|
||||
return 'this.' + effect + '(this._data.' + source + ', old);'
|
||||
},
|
||||
|
||||
// basic modus operandi
|
||||
//
|
||||
// <hostPath> %=% <targetPath>
|
||||
// (node = <$.id | nodes[index]>)
|
||||
// <model[.path]> %=% node.<property>
|
||||
//
|
||||
// flow-up:
|
||||
// set(model): node.<property> = <model[.path]>
|
||||
//
|
||||
// flow-down:
|
||||
// node.on.<property>-changed: <model[.path]> = e.detail.value
|
||||
|
||||
_bindEffectBuilder: function(hostProperty, targetPath) {
|
||||
var parts = targetPath.split('.');
|
||||
var id = parts[0], property = parts[1];
|
||||
if (!property) {
|
||||
property = 'textContent';
|
||||
// textContent never flows-up
|
||||
} else {
|
||||
// flow-up
|
||||
this._bindListeners.push({
|
||||
id: id,
|
||||
property: property,
|
||||
path: hostProperty
|
||||
});
|
||||
}
|
||||
//
|
||||
// flow-down
|
||||
//
|
||||
//console.log('[_bindEffectBuilder]: [%s] %=% [%s].[%s]', hostProperty, id, property);
|
||||
return 'this.$.' + id + '.' + property + ' = '
|
||||
+ 'this._data.' + hostProperty + ';'
|
||||
},
|
||||
|
||||
_notifyEffectBuilder: function(source) {
|
||||
return 'this._notifyChange(\'' + source + '\')';
|
||||
},
|
||||
|
||||
_computeEffectBuilder: function(source, effect) {
|
||||
return 'this.' + effect.property
|
||||
+ ' = this.' + effect.method + '(this._data.' + source + ');';
|
||||
},
|
||||
|
||||
// implement effect directives from template annotations
|
||||
// _nodes[info.index][info.name] = {{info.value}}
|
||||
_annotationEffectBuilder: function(hostProperty, info) {
|
||||
var property = info.name || 'textContent';
|
||||
if (property !== 'textContent') {
|
||||
// <node>.on.<property>-changed: <path> = e.detail.value
|
||||
this._addAnnotatedListener(info.index, property, info.value);
|
||||
}
|
||||
//
|
||||
// flow-down
|
||||
//
|
||||
// construct the effect to occur when [property] changes:
|
||||
// set nodes[index][name] to this[value]
|
||||
//
|
||||
//console.log('[_annotationEffectBuilder]: [%s] %=% [%s].[%s]', info.value, info.index, property);
|
||||
return 'this._nodes[' + info.index + '].' + property
|
||||
+ ' = this._data.' + info.value + ';';
|
||||
},
|
||||
|
||||
_addAnnotatedListener: function(index, property, path) {
|
||||
// <node>.on.<property>-changed: <path> = e.detail.value
|
||||
this._bindListeners.push({
|
||||
index: index,
|
||||
property: property,
|
||||
path: path
|
||||
});
|
||||
},
|
||||
|
||||
_bindAnnotationProperty: function(name, path, index) {
|
||||
return 'this._nodes[' + index + '].' + name
|
||||
+ ' = this._data.' + path + ';';
|
||||
},
|
||||
|
||||
_addBindListener: function(property, path, id) {
|
||||
var bl = this._requireBindListeners(property);
|
||||
bl.targets.push({
|
||||
id: id,
|
||||
path: path
|
||||
});
|
||||
},
|
||||
|
||||
// create accessors that implement effects
|
||||
_bindPropertyEffects: function(property, effects) {
|
||||
var defun = {
|
||||
get: function() {
|
||||
return this._data[property];
|
||||
}
|
||||
}
|
||||
if (effects.length) {
|
||||
// combine effects
|
||||
effects = effects.join('\n\t\t');
|
||||
// construct effector
|
||||
var effector = '_' + property + 'Effector';
|
||||
this[effector] = new Function('old', effects);
|
||||
// construct setter body
|
||||
var body = '\tvar old = this._setData(\'' + property + '\', value);\n'
|
||||
+ '\tif (value !== old) {\n'
|
||||
+ '\t\tthis.' + effector + '(old);\n'
|
||||
+ '\t}';
|
||||
var setter = new Function('value', body);
|
||||
// ReadOnly properties have a private setter only
|
||||
if (this.isReadOnlyProperty(property)) {
|
||||
this['_set_' + property] = setter;
|
||||
}
|
||||
// other properties have a proper setter
|
||||
else {
|
||||
defun.set = setter;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(this, property, defun);
|
||||
//console.log(prop.set ? prop.set.toString() : '(read-only)');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
115
components/polymer/src/features/bind.html
Normal file
115
components/polymer/src/features/bind.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<script>
|
||||
|
||||
/**
|
||||
* Needs new name.
|
||||
*
|
||||
* Support for the declarative property sugaring via a `bind` object
|
||||
* on the prototype.
|
||||
*
|
||||
* Building applications by hand, a pattern emerges: operations such as
|
||||
* data propagation need to trigger effects. For example, changes to data
|
||||
* need to be reflected into DOM, or trigger additional value computations.
|
||||
*
|
||||
* This module provides an API for registering effects against properties.
|
||||
* The effect data is consumed by the `bind-effects` module which compiles
|
||||
* the effects into efficient JavaScript that is triggered, e.g., when a
|
||||
* property is set to a new value.
|
||||
*
|
||||
* Property effects can be created imperatively, by template-annotations
|
||||
* (e.g. mustache notation), or by declaration in the `bind` object.
|
||||
*
|
||||
* The bind object syntax is as follows:
|
||||
*
|
||||
* bind {
|
||||
* // if `method` is the name of a method on the current object, the
|
||||
* // method is invoked with the property changes. The method is provided
|
||||
* // arguments as follows: `method(value, oldValue)`
|
||||
* property: 'method'
|
||||
*
|
||||
* // if the value is not the name of a method, it's assumed to be a
|
||||
* // id of an element in the `$` hash. Remember that when using a
|
||||
* // template, `$` maps element ids to elements. By default, changes in
|
||||
* // the named property are sent to the target element's `textContent`.
|
||||
* // In this case, when `property2` changes, it's value is set to
|
||||
* // `this.$.myId.textContent`.
|
||||
* property2: 'myId'
|
||||
*
|
||||
* // A target property other than `textContent` can be specified using
|
||||
* // dot notation. In this case, when `property3` changes, it's value is
|
||||
* // set to `this.$.myId.value`.
|
||||
* property3: 'myId.value'
|
||||
*
|
||||
* // To have a property modification trigger multiple side effects, use
|
||||
* // an array.
|
||||
* property4: [
|
||||
* 'property4Changed',
|
||||
* 'myId.data',
|
||||
* 'otherId.value'
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @class feature: bind
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
// per prototype
|
||||
|
||||
// TODO(sjmiles): initialization of `_propertyEffects` and the
|
||||
// `addPropertyEffect` itself are really the domain of bind-effects
|
||||
// but these things needs to happen before bind-effects itself initializes.
|
||||
// We need to factor bind-effects into before and after features instead
|
||||
// and let this feature be for dealing with `bind` object.
|
||||
|
||||
register: function(prototype) {
|
||||
prototype._addPropertyBindEffects();
|
||||
},
|
||||
|
||||
// TODO(sjmiles): really ad hoc self-modifying code
|
||||
// to resolve initialization ordering around optional
|
||||
// module
|
||||
addPropertyEffect: function(property, kind, effect) {
|
||||
// prepare storage on first invocation
|
||||
this._propertyEffects = {};
|
||||
// add the effect
|
||||
this._addPropertyEffect(property, kind, effect);
|
||||
// subsequent invocations skip preparation step implementation
|
||||
this.addPropertyEffect = this._addPropertyEffect;
|
||||
},
|
||||
|
||||
_addPropertyEffect: function(property, kind, effect) {
|
||||
var fx = this._propertyEffects[property];
|
||||
if (!fx) {
|
||||
fx = this._propertyEffects[property] = [];
|
||||
}
|
||||
fx.push({
|
||||
kind: kind,
|
||||
effect: effect
|
||||
});
|
||||
},
|
||||
|
||||
_addPropertyBindEffects: function() {
|
||||
for (var n in this.bind) {
|
||||
var bind = this.bind[n];
|
||||
if (typeof bind === 'object') {
|
||||
// multiplexed definition
|
||||
for (var nn in bind) {
|
||||
this._addPropertyBindEffect(n, bind[nn]);
|
||||
}
|
||||
} else {
|
||||
// single definition
|
||||
this._addPropertyBindEffect(n, bind);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addPropertyBindEffect: function(property, bindEffect) {
|
||||
var kind = 'bind';
|
||||
if (typeof this[bindEffect] === 'function') {
|
||||
kind = 'method';
|
||||
}
|
||||
this.addPropertyEffect(property, kind, bindEffect);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
39
components/polymer/src/features/computed.html
Normal file
39
components/polymer/src/features/computed.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
/* computed property feature */
|
||||
|
||||
computed: {
|
||||
},
|
||||
|
||||
register: function(prototype) {
|
||||
prototype.defineComputedProperties(prototype.computed);
|
||||
},
|
||||
|
||||
defineComputedProperties: function(computed) {
|
||||
for (var n in computed) {
|
||||
this.defineComputedProperty(n, computed[n]);
|
||||
}
|
||||
},
|
||||
|
||||
defineComputedProperty: function(name, expression) {
|
||||
var index = expression.indexOf('(');
|
||||
var method = expression.slice(0, index);
|
||||
var args = expression.slice(index + 1, -1).replace(/ /g, '').split(',');
|
||||
console.log('%c on [%s] compute [%s] via [%s]', 'color: green', args[0], name, method);
|
||||
this.addPropertyEffect(args[0], 'compute', {
|
||||
property: name,
|
||||
method: method
|
||||
});
|
||||
/*
|
||||
this.compoundWatch(args, function() {
|
||||
Polymer.log.watches && console.log('[compute] [%s]', name, arguments);
|
||||
this[name] = method.apply(this, arguments);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
54
components/polymer/src/features/content.html
Normal file
54
components/polymer/src/features/content.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// TODO(sjmiles): ad-hoc signal for `ShadowDOM-lite-enhanced` nodes
|
||||
isHost: true,
|
||||
|
||||
register: function(prototype) {
|
||||
var t = prototype._template;
|
||||
// TODO(sorvell): is qsa is wrong here due to distribution?
|
||||
// TODO(sjmiles): No element should ever actually stamp a <content> node
|
||||
// into it's composed tree, so I believe this is actually correct.
|
||||
// However, I wonder if it's more efficient to capture during annotation
|
||||
// parsing, since the parse step does a tree walk in any case, and the
|
||||
// tree is smaller before element expansion.
|
||||
prototype._useContent = Boolean(t && t.content.querySelector('content'));
|
||||
},
|
||||
|
||||
poolContent: function() {
|
||||
// pool the light dom
|
||||
var pool = document.createDocumentFragment();
|
||||
while (this.firstChild) {
|
||||
pool.appendChild(this.firstChild);
|
||||
}
|
||||
this.contentPool = pool;
|
||||
// capture lightChildren to help reify dom scoping
|
||||
this.lightChildren =
|
||||
Array.prototype.slice.call(this.contentPool.childNodes, 0);
|
||||
},
|
||||
|
||||
distributeContent: function() {
|
||||
var content, pool = this.contentPool;
|
||||
// replace <content> with nodes teleported from pool
|
||||
while (content = this.querySelector('content')) {
|
||||
var select = content.getAttribute('select');
|
||||
var frag = pool;
|
||||
if (select) {
|
||||
frag = document.createDocumentFragment();
|
||||
// TODO(sjmiles): diverges from ShadowDOM spec behavior: ShadowDOM
|
||||
// only selects top level nodes from pool. Iterate children and match
|
||||
// manually instead.
|
||||
var nodes = pool.querySelectorAll(select);
|
||||
for (var i=0, l=nodes.length; i<l; i++) {
|
||||
frag.appendChild(nodes[i]);
|
||||
}
|
||||
}
|
||||
// content self-destructs
|
||||
content.parentNode.replaceChild(frag, content);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
44
components/polymer/src/features/events.html
Normal file
44
components/polymer/src/features/events.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
listeners: {},
|
||||
|
||||
init: function() {
|
||||
},
|
||||
|
||||
// TODO(sjmiles): support for '.' notation requires 'nodes' feature
|
||||
listenListeners: function() {
|
||||
for (var key in this.listeners) {
|
||||
var node = this, name = key;
|
||||
if (name.indexOf('.') >= 0) {
|
||||
name = name.split('.');
|
||||
node = this.$[name[0]];
|
||||
name = name[1];
|
||||
}
|
||||
this.listen(node, name, this.listeners[key]);
|
||||
}
|
||||
},
|
||||
|
||||
listen: function(node, eventName, methodName) {
|
||||
node.addEventListener(eventName, function(e) {
|
||||
this[methodName](e, e.detail);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// TODO(sjmiles): use a dictionary for options after `detail`
|
||||
fire: function(type, detail, onNode, bubbles, cancelable) {
|
||||
var node = onNode || this;
|
||||
var detail = (detail === null || detail === undefined) ? {} : detail;
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles !== undefined ? bubbles : true,
|
||||
cancelable: cancelable !== undefined ? cancelable : true,
|
||||
detail: detail
|
||||
});
|
||||
node.dispatchEvent(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
31
components/polymer/src/features/keys.html
Normal file
31
components/polymer/src/features/keys.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
keyPresses: {},
|
||||
|
||||
listenKeyPresses: function() {
|
||||
// for..in here to gate empty keyPresses object (iterates once or never)
|
||||
for (var n in this.keyPresses) {
|
||||
// only get here if there is something in keyPresses
|
||||
this.addEventListener('keypress', this.keyPressesFeatureHandler);
|
||||
// map string keys to numeric codes
|
||||
for (n in this.keyPresses) {
|
||||
if (typeof n === 'string') {
|
||||
this.keyPresses[Event.prototype.keys[n]] = this.keyPresses[n];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
keyPressesFeatureHandler: function(e) {
|
||||
var method = this.keyPresses[e.keyCode];
|
||||
if (method && this[method]) {
|
||||
return this[method](e.keyCode, e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
287
components/polymer/src/features/layout.html
Normal file
287
components/polymer/src/features/layout.html
Normal file
@@ -0,0 +1,287 @@
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<style>
|
||||
/*******************************
|
||||
Flex Layout
|
||||
*******************************/
|
||||
|
||||
[layout][horizontal], [layout][vertical] {
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[layout][horizontal][inline], [layout][vertical][inline] {
|
||||
display: -ms-inline-flexbox;
|
||||
display: -webkit-inline-flex;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
[layout][horizontal] {
|
||||
-ms-flex-direction: row;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
[layout][horizontal][reverse] {
|
||||
-ms-flex-direction: row-reverse;
|
||||
-webkit-flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
[layout][vertical] {
|
||||
-ms-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
[layout][vertical][reverse] {
|
||||
-ms-flex-direction: column-reverse;
|
||||
-webkit-flex-direction: column-reverse;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
[layout][wrap] {
|
||||
-ms-flex-wrap: wrap;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
[layout][wrap-reverse] {
|
||||
-ms-flex-wrap: wrap-reverse;
|
||||
-webkit-flex-wrap: wrap-reverse;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
|
||||
[flex] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[flex][auto] {
|
||||
-ms-flex: 1 1 auto;
|
||||
-webkit-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
[flex][none] {
|
||||
-ms-flex: none;
|
||||
-webkit-flex: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
[flex][one] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[flex][two] {
|
||||
-ms-flex: 2;
|
||||
-webkit-flex: 2;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
[flex][three] {
|
||||
-ms-flex: 3;
|
||||
-webkit-flex: 3;
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
[flex][four] {
|
||||
-ms-flex: 4;
|
||||
-webkit-flex: 4;
|
||||
flex: 4;
|
||||
}
|
||||
|
||||
[flex][five] {
|
||||
-ms-flex: 5;
|
||||
-webkit-flex: 5;
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
[flex][six] {
|
||||
-ms-flex: 6;
|
||||
-webkit-flex: 6;
|
||||
flex: 6;
|
||||
}
|
||||
|
||||
[flex][seven] {
|
||||
-ms-flex: 7;
|
||||
-webkit-flex: 7;
|
||||
flex: 7;
|
||||
}
|
||||
|
||||
[flex][eight] {
|
||||
-ms-flex: 8;
|
||||
-webkit-flex: 8;
|
||||
flex: 8;
|
||||
}
|
||||
|
||||
[flex][nine] {
|
||||
-ms-flex: 9;
|
||||
-webkit-flex: 9;
|
||||
flex: 9;
|
||||
}
|
||||
|
||||
[flex][ten] {
|
||||
-ms-flex: 10;
|
||||
-webkit-flex: 10;
|
||||
flex: 10;
|
||||
}
|
||||
|
||||
[flex][eleven] {
|
||||
-ms-flex: 11;
|
||||
-webkit-flex: 11;
|
||||
flex: 11;
|
||||
}
|
||||
|
||||
[flex][twelve] {
|
||||
-ms-flex: 12;
|
||||
-webkit-flex: 12;
|
||||
flex: 12;
|
||||
}
|
||||
|
||||
/* alignment in cross axis */
|
||||
|
||||
[layout][start] {
|
||||
-ms-flex-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
[layout][center], [layout][center-center] {
|
||||
-ms-flex-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[layout][end] {
|
||||
-ms-flex-align: end;
|
||||
-webkit-align-items: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* alignment in main axis */
|
||||
|
||||
[layout][start-justified] {
|
||||
-ms-flex-pack: start;
|
||||
-webkit-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[layout][center-justified], [layout][center-center] {
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[layout][end-justified] {
|
||||
-ms-flex-pack: end;
|
||||
-webkit-justify-content: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
[layout][around-justified] {
|
||||
-ms-flex-pack: around;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
[layout][justified] {
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* self alignment */
|
||||
|
||||
[self-start] {
|
||||
-ms-align-self: flex-start;
|
||||
-webkit-align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
[self-center] {
|
||||
-ms-align-self: center;
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
[self-end] {
|
||||
-ms-align-self: flex-end;
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
[self-stretch] {
|
||||
-ms-align-self: stretch;
|
||||
-webkit-align-self: stretch;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other Layout
|
||||
*******************************/
|
||||
|
||||
[block] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ie support for hidden */
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
[invisible] {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
[relative] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[fit] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[fullbleed] {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
[scroll] {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other
|
||||
*******************************/
|
||||
|
||||
[segment], segment {
|
||||
display: block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 1em 0.5em;
|
||||
padding: 1em;
|
||||
background-color: white;
|
||||
-webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
12
components/polymer/src/features/log.html
Normal file
12
components/polymer/src/features/log.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
log: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = '[%s]: ' + args[0];
|
||||
args.splice(1, 0, this.localName);
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
90
components/polymer/src/features/published.html
Normal file
90
components/polymer/src/features/published.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<script>
|
||||
|
||||
/**
|
||||
* Define public property API.
|
||||
*
|
||||
* published: {
|
||||
* <property>: <Type || Object>,
|
||||
* ...
|
||||
*
|
||||
* // `foo` property can be assigned via attribute, will be deserialized to
|
||||
* // the specified data-type. All `published` properties have this behavior.
|
||||
* foo: String,
|
||||
*
|
||||
* // `bar` property has additional behavior specifiers.
|
||||
* // type: as above, type for (de-)serialization
|
||||
* // notify: true to send a signal when a value is set to this property
|
||||
* // reflect: true to serialize the property to an attribute
|
||||
* // readOnly: if true, the property has no setter
|
||||
* bar: {
|
||||
* type: Boolean,
|
||||
* notify: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* By itself the published feature doesn't do anything but provide property
|
||||
* information. Other features use this information to control behavior.
|
||||
*
|
||||
* The `type` information is used by the `attributes` feature to convert
|
||||
* String values in attributes to properties.
|
||||
*
|
||||
* The `bind-effects` feature uses property information to control property
|
||||
* access.
|
||||
*
|
||||
* Marking a property as `notify` causes a change in the property to
|
||||
* fire a non-bubbling event called `<property>-changed`. Elements that
|
||||
* have enabled two-way binding to the property use this event to
|
||||
* observe changes.
|
||||
*
|
||||
* `readOnly` properties have a getter, but no setter. To set a read-only
|
||||
* property, use the private setter method `_set_<property>(value)`.
|
||||
*
|
||||
* @class feature: published
|
||||
*/
|
||||
Base.addFeature({
|
||||
|
||||
published: {
|
||||
},
|
||||
|
||||
nob: Object.create(null),
|
||||
|
||||
register: function(prototype) {
|
||||
// TODO(sjmiles): move to a different module
|
||||
if (prototype.addPropertyEffect) {
|
||||
for (var n in prototype.published) {
|
||||
if (prototype.isNotifyProperty(n)) {
|
||||
prototype.addPropertyEffect(n, 'notify');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getPublishInfo: function(property) {
|
||||
var p = this.published[property];
|
||||
if (typeof(p) === 'function') {
|
||||
p = this.published[property] = {
|
||||
type: p
|
||||
};
|
||||
}
|
||||
return p || Base.nob;
|
||||
},
|
||||
|
||||
getPublishedPropertyType: function(property) {
|
||||
return this.getPublishInfo(property).type;
|
||||
},
|
||||
|
||||
isReadOnlyProperty: function(property) {
|
||||
return this.getPublishInfo(property).readOnly;
|
||||
},
|
||||
|
||||
isNotifyProperty: function(property) {
|
||||
return this.getPublishInfo(property).notify;
|
||||
},
|
||||
|
||||
isReflectedProperty: function(property) {
|
||||
return this.getPublishInfo(property).reflect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
38
components/polymer/src/features/template.html
Normal file
38
components/polymer/src/features/template.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
register: function(prototype) {
|
||||
var script = (document._currentScript || document.currentScript);
|
||||
var prev = script.previousElementSibling;
|
||||
if (prev && prev.localName === 'template') {
|
||||
prototype._template = prev;
|
||||
}
|
||||
},
|
||||
|
||||
stampTemplate: function(template) {
|
||||
this._stampTemplate(template || this._template, this.root);
|
||||
// TODO(sjmiles): hello prollyfill
|
||||
if (window.CustomElements && CustomElements.upgradeSubtree) {
|
||||
CustomElements.upgradeSubtree(this.root);
|
||||
}
|
||||
},
|
||||
|
||||
_stampTemplate: function(template, target) {
|
||||
// TODO(sorvell): light dom children will invalidate annotations.
|
||||
// TODO(sjmiles): ^ why?
|
||||
var instance = this.instanceTemplate(template);
|
||||
// identify host
|
||||
for (var e = instance.firstElementChild; e; e=e.nextElementSibling) {
|
||||
e.host = this;
|
||||
}
|
||||
target.insertBefore(instance, target.firstElementChild);
|
||||
},
|
||||
|
||||
instanceTemplate: function(template) {
|
||||
return document.importNode(template.content, true);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
42
components/polymer/src/features/utils.html
Normal file
42
components/polymer/src/features/utils.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
async: function(method) {
|
||||
var handled = false;
|
||||
var handle = function() {
|
||||
if (!handled) {
|
||||
handled = true;
|
||||
method.call(this);
|
||||
}
|
||||
}.bind(this);
|
||||
// minimize latency by racing timeout against rAF
|
||||
setTimeout(handle);
|
||||
requestAnimationFrame(handle);
|
||||
},
|
||||
|
||||
toggleAttribute: function(name, value, node) {
|
||||
(node || this)[value ? 'setAttribute' : 'removeAttribute'](name, '');
|
||||
},
|
||||
|
||||
attributeFollows: function(name, neo, old) {
|
||||
if (old) {
|
||||
old.removeAttribute(name);
|
||||
}
|
||||
if (neo) {
|
||||
neo.setAttribute(name, '');
|
||||
}
|
||||
},
|
||||
|
||||
queryHost: function(node) {
|
||||
return this.host || this._queryHost(this);
|
||||
},
|
||||
|
||||
_queryHost: function(node) {
|
||||
return node &&
|
||||
(node.host || (node.host = this._queryHost(node.parentNode)));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
42
components/polymer/src/lang.html
Normal file
42
components/polymer/src/lang.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<script>
|
||||
|
||||
// a tiny bit of sugar for `document.currentScript.ownerDocument`
|
||||
// sadly `import` is reserved, so we need another name or
|
||||
// you have to refer to this value `window.import`
|
||||
Object.defineProperty(window, 'import', {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return (document._currentScript || document.currentScript).ownerDocument;
|
||||
}
|
||||
});
|
||||
|
||||
// copy own properties from 'api' to 'prototype, with name hinting for 'super'
|
||||
function extend(prototype, api) {
|
||||
if (prototype && api) {
|
||||
// use only own properties of 'api'
|
||||
Object.getOwnPropertyNames(api).forEach(function(n) {
|
||||
// acquire property descriptor
|
||||
var pd = Object.getOwnPropertyDescriptor(api, n);
|
||||
if (pd) {
|
||||
// clone property via descriptor
|
||||
Object.defineProperty(prototype, n, pd);
|
||||
// cache name-of-method for 'super' engine
|
||||
/*
|
||||
if (typeof pd.value == 'function') {
|
||||
// hint the 'super' engine
|
||||
pd.value.nom = n;
|
||||
}
|
||||
*/
|
||||
}
|
||||
});
|
||||
}
|
||||
return prototype;
|
||||
};
|
||||
|
||||
Event.prototype.keys = {
|
||||
ESC_KEY: 27,
|
||||
ENTER_KEY: 13
|
||||
};
|
||||
|
||||
</script>
|
||||
118
components/polymer/src/more-features/bind-effects-object.html
Normal file
118
components/polymer/src/more-features/bind-effects-object.html
Normal file
@@ -0,0 +1,118 @@
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
|
||||
var originalSetupBindListeners = Base._setupBindListeners;
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
// TODO(sorvell): hax to automate path listener setup; figure out how to
|
||||
// integrate this better.
|
||||
_setupBindListeners: function() {
|
||||
originalSetupBindListeners.call(this);
|
||||
this._setupPathListeners();
|
||||
},
|
||||
|
||||
/*
|
||||
Enables structured data flow. Looks at bindings and adds an event listener
|
||||
in two cases:
|
||||
|
||||
1. the binding path is compound (contains a `.`).
|
||||
2. the binding property is an object valued published property.
|
||||
*/
|
||||
_setupPathListeners: function() {
|
||||
var $b = this._bindListeners;
|
||||
for (var i=0, l=$b.length, info; (i<l) && (info=$b[i]); i++) {
|
||||
var node = this._nodeForBinding(info);
|
||||
var model = modelForPath(info.path);
|
||||
var event;
|
||||
// determine if and what event we need to listen on
|
||||
if (node._isPublishedObject &&
|
||||
node._isPublishedObject(info.property)) {
|
||||
event = info.property + EVENT_PROP_CHANGED;
|
||||
} else if (model !== info.path) {
|
||||
event = info.property + EVENT_CHANGED;
|
||||
}
|
||||
// listen iff we need to
|
||||
if (event) {
|
||||
this._setupPathListener(node, event, model, info.path);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO(sorvell): at one point the listener had to be async to propagate
|
||||
// changes correctly; this no longer seems to be necessary but need
|
||||
// to keep an eye on it until it's understood fully.
|
||||
_setupPathListener: function(node, event, model, path) {
|
||||
var self = this;
|
||||
node.addEventListener(event, function(e) {
|
||||
self.notifyPropertyChange(model, path);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Notify that a property has changed. This method can be used to
|
||||
flow data through the system when a path in an Object valued property
|
||||
changes. For example:
|
||||
|
||||
this.item.user.name = 'Bob';
|
||||
this.notifyPropertyChange('item', 'user.name');
|
||||
*/
|
||||
notifyPropertyChange: function(property, path) {
|
||||
this._forcePropertyEffect(property);
|
||||
this._firePropertyChange(property, path);
|
||||
},
|
||||
|
||||
// Force property side effects.
|
||||
// 1. calls the _propertyEffector
|
||||
// 2. for bindings to Object valued properties, we need to cascade
|
||||
// recursively through dirty check prevention
|
||||
// (e.g. if foo.item = this.item is dirty checked away, we ensure
|
||||
// foo.item's side effects run)
|
||||
_forcePropertyEffect: function(property) {
|
||||
this['_' + property + 'Effector']();
|
||||
// if the property is bound to other Object valued properties,
|
||||
// force those properties to effect changes.
|
||||
var $b = this._bindListeners;
|
||||
for (var i=0, l=$b.length, b; (i<l) && (b=$b[i]); i++) {
|
||||
this._forceBoundPropertyEffect(property, b);
|
||||
}
|
||||
},
|
||||
|
||||
_forceBoundPropertyEffect: function(property, binding) {
|
||||
var node = this._nodeForBinding(binding);
|
||||
var model = modelForPath(binding.path);
|
||||
if ((model === property) && node._isPublishedObject(binding.property)) {
|
||||
node._forcePropertyEffect(binding.property);
|
||||
}
|
||||
},
|
||||
|
||||
_firePropertyChange: function(modelProperty, path) {
|
||||
var detail = {
|
||||
property: this[modelProperty],
|
||||
path: path
|
||||
};
|
||||
this.fire(modelProperty + '-property-changed', detail, null, false);
|
||||
},
|
||||
|
||||
_nodeForBinding: function(info) {
|
||||
return info.id ? this.$[info.id] : this._nodes[info.index];
|
||||
},
|
||||
|
||||
_isPublishedObject: function(property) {
|
||||
return this.published[property] &&
|
||||
(this.published[property].type === Object);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function modelForPath(path) {
|
||||
return path.split('.').shift();
|
||||
}
|
||||
|
||||
var EVENT_CHANGED = '-changed';
|
||||
var EVENT_PROP_CHANGED = '-property' + EVENT_CHANGED;
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
63
components/polymer/src/more-features/extends.html
Normal file
63
components/polymer/src/more-features/extends.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
Provides basic inheritance capability via the `extends` property.
|
||||
*/
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
register: function(prototype) {
|
||||
if (prototype.extends) {
|
||||
prototype.__proto__ = getExtendsPrototype(prototype.extends);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// TODO(sorvell): requires redefining `Polymer`.
|
||||
Polymer = function(prototype) {
|
||||
prototype.__proto__ = Base;
|
||||
prototype.registerCallback();
|
||||
var config = {prototype: prototype};
|
||||
if (prototype.extends && !isCustomElementName(prototype.extends)) {
|
||||
config.extends = prototype.extends;
|
||||
}
|
||||
Polymer[prototype.name] = document.registerElement(prototype.name, config);
|
||||
};
|
||||
|
||||
Polymer.log = {
|
||||
};
|
||||
|
||||
function getExtendsPrototype(name) {
|
||||
var ctor = Polymer[name];
|
||||
if (ctor) {
|
||||
return ctor.prototype;
|
||||
} else {
|
||||
if (isCustomElementName(name)) {
|
||||
console.warn('Cannot find extended prototype named', name);
|
||||
} else {
|
||||
return getExtendedNativePrototype(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isCustomElementName(name) {
|
||||
return name.indexOf('-') >= 0;
|
||||
}
|
||||
|
||||
var nativePrototypes = {};
|
||||
|
||||
function getExtendedNativePrototype(name) {
|
||||
if (!nativePrototypes[name]) {
|
||||
var proto = Object.getPrototypeOf(document.createElement(name));
|
||||
extend(proto, Base);
|
||||
nativePrototypes[name] = proto;
|
||||
}
|
||||
return nativePrototypes[name];
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
23
components/polymer/src/more-features/polymer-shadow.html
Normal file
23
components/polymer/src/more-features/polymer-shadow.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<link rel="import" href="../polymer.html">
|
||||
<link rel="import" href="../features/template.html">
|
||||
|
||||
<!-- shadow-styler must be before annotations! -->
|
||||
<link rel="import" href="shadow-styler.html">
|
||||
|
||||
<!-- load normal polymer, imports takes care of de-duping -->
|
||||
<link rel="import" href="../../polymer.html">
|
||||
|
||||
<link rel="import" href="shadow.html">
|
||||
<link rel="import" href="shadow-layout.html">
|
||||
|
||||
<!-- here we want to change the basic behavior for all elements, but in general
|
||||
this could be a setting. -->
|
||||
<script>
|
||||
Base.features = function() {
|
||||
this._useContent = false;
|
||||
if (this._template) {
|
||||
this.createShadow();
|
||||
}
|
||||
this.defaultFeatures();
|
||||
}
|
||||
</script>
|
||||
120
components/polymer/src/more-features/ready.html
Normal file
120
components/polymer/src/more-features/ready.html
Normal file
@@ -0,0 +1,120 @@
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
|
||||
Provides `ready` lifecycle callback which is called parent to child.
|
||||
|
||||
Allows initial property values to be set on the prototype and applied
|
||||
iff no other input value is given via attribute literal or binding.
|
||||
|
||||
*/
|
||||
|
||||
// TODO(sorvell): monkey path for now.
|
||||
var originalAttachedCallback = Base.attachedCallback;
|
||||
var originalCreateBindings = Base._createBindings;
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
attachedCallback: function() {
|
||||
this._checkReady();
|
||||
originalAttachedCallback.call(this);
|
||||
},
|
||||
|
||||
// Checks if this element is inside another "readyable" element. If so,
|
||||
// then pushes this element into an array of `_needsReady` elements; if
|
||||
// not, starts the ready cascade.
|
||||
_checkReady: function() {
|
||||
if (!this._readied) {
|
||||
var host = this._findHostParent();
|
||||
if (host) {
|
||||
host._registerForReady(this);
|
||||
} else {
|
||||
this._cascadeReady();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerForReady: function(element) {
|
||||
if (!this._needsReady) {
|
||||
this._needsReady = [];
|
||||
}
|
||||
this._needsReady.push(element);
|
||||
},
|
||||
|
||||
_findHostParent: function() {
|
||||
var n = this;
|
||||
while (n = n.parentNode) {
|
||||
if (n.isHost) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_cascadeReady: function() {
|
||||
//console.group('[' + this.name + '#' + this.id + ']', 'ready');
|
||||
this._readyCallback();
|
||||
var n$ = this._needsReady;
|
||||
if (n$) {
|
||||
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
|
||||
if (n._cascadeReady) {
|
||||
n._cascadeReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
this._needsReady = null;
|
||||
//console.groupEnd('[' + this.name + '#' + this.id + ']', 'ready');
|
||||
},
|
||||
|
||||
_readyCallback: function() {
|
||||
this.ready();
|
||||
this._applyInitialValues();
|
||||
this._readied = true;
|
||||
},
|
||||
|
||||
// user extension point.
|
||||
ready: function() {},
|
||||
|
||||
// overridden to allow recording of initial values
|
||||
_createBindings: function() {
|
||||
this._recordInitialValues();
|
||||
originalCreateBindings.call(this);
|
||||
},
|
||||
|
||||
_recordInitialValues: function() {
|
||||
this._initialValues = {};
|
||||
var fx$ = this._propertyEffects;
|
||||
if (fx$) {
|
||||
for (var n in fx$) {
|
||||
var v = this[n];
|
||||
if (v !== undefined) {
|
||||
this._initialValues[n] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO(sorvell): preferrably we would squelch method side effects here
|
||||
_applyInitialValues: function() {
|
||||
for (var i in this._initialValues) {
|
||||
this.setDefaultValue(i, this._initialValues[i]);
|
||||
}
|
||||
this._initialValues = null;
|
||||
},
|
||||
|
||||
/**
|
||||
Ensures `property` has a value. If it does not, then the property is
|
||||
set to `defaultValue`.
|
||||
*/
|
||||
setDefaultValue: function(property, defaultValue) {
|
||||
if (this[property] === undefined) {
|
||||
this[property] = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
286
components/polymer/src/more-features/shadow-layout.html
Normal file
286
components/polymer/src/more-features/shadow-layout.html
Normal file
@@ -0,0 +1,286 @@
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<style>
|
||||
/*******************************
|
||||
Flex Layout
|
||||
*******************************/
|
||||
|
||||
html /deep/ [layout][horizontal], html /deep/ [layout][vertical] {
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
html /deep/ [layout][horizontal][inline], html /deep/ [layout][vertical][inline] {
|
||||
display: -ms-inline-flexbox;
|
||||
display: -webkit-inline-flex;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
html /deep/ [layout][horizontal] {
|
||||
-ms-flex-direction: row;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html /deep/ [layout][horizontal][reverse] {
|
||||
-ms-flex-direction: row-reverse;
|
||||
-webkit-flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
html /deep/ [layout][vertical] {
|
||||
-ms-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
html /deep/ [layout][vertical][reverse] {
|
||||
-ms-flex-direction: column-reverse;
|
||||
-webkit-flex-direction: column-reverse;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
html /deep/ [layout][wrap] {
|
||||
-ms-flex-wrap: wrap;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
html /deep/ [layout][wrap-reverse] {
|
||||
-ms-flex-wrap: wrap-reverse;
|
||||
-webkit-flex-wrap: wrap-reverse;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
|
||||
html /deep/ [flex] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
html /deep/ [flex][auto] {
|
||||
-ms-flex: 1 1 auto;
|
||||
-webkit-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
html /deep/ [flex][none] {
|
||||
-ms-flex: none;
|
||||
-webkit-flex: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
html /deep/ [flex][one] {
|
||||
-ms-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
html /deep/ [flex][two] {
|
||||
-ms-flex: 2;
|
||||
-webkit-flex: 2;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
html /deep/ [flex][three] {
|
||||
-ms-flex: 3;
|
||||
-webkit-flex: 3;
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
html /deep/ [flex][four] {
|
||||
-ms-flex: 4;
|
||||
-webkit-flex: 4;
|
||||
flex: 4;
|
||||
}
|
||||
|
||||
html /deep/ [flex][five] {
|
||||
-ms-flex: 5;
|
||||
-webkit-flex: 5;
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
html /deep/ [flex][six] {
|
||||
-ms-flex: 6;
|
||||
-webkit-flex: 6;
|
||||
flex: 6;
|
||||
}
|
||||
|
||||
html /deep/ [flex][seven] {
|
||||
-ms-flex: 7;
|
||||
-webkit-flex: 7;
|
||||
flex: 7;
|
||||
}
|
||||
|
||||
html /deep/ [flex][eight] {
|
||||
-ms-flex: 8;
|
||||
-webkit-flex: 8;
|
||||
flex: 8;
|
||||
}
|
||||
|
||||
html /deep/ [flex][nine] {
|
||||
-ms-flex: 9;
|
||||
-webkit-flex: 9;
|
||||
flex: 9;
|
||||
}
|
||||
|
||||
html /deep/ [flex][ten] {
|
||||
-ms-flex: 10;
|
||||
-webkit-flex: 10;
|
||||
flex: 10;
|
||||
}
|
||||
|
||||
html /deep/ [flex][eleven] {
|
||||
-ms-flex: 11;
|
||||
-webkit-flex: 11;
|
||||
flex: 11;
|
||||
}
|
||||
|
||||
html /deep/ [flex][twelve] {
|
||||
-ms-flex: 12;
|
||||
-webkit-flex: 12;
|
||||
flex: 12;
|
||||
}
|
||||
|
||||
/* alignment in cross axis */
|
||||
|
||||
html /deep/ [layout][start] {
|
||||
-ms-flex-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
html /deep/ [layout][center], html /deep/ [layout][center-center] {
|
||||
-ms-flex-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
html /deep/ [layout][end] {
|
||||
-ms-flex-align: end;
|
||||
-webkit-align-items: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* alignment in main axis */
|
||||
|
||||
html /deep/ [layout][start-justified] {
|
||||
-ms-flex-pack: start;
|
||||
-webkit-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
html /deep/ [layout][center-justified], html /deep/ [layout][center-center] {
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
html /deep/ [layout][end-justified] {
|
||||
-ms-flex-pack: end;
|
||||
-webkit-justify-content: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
html /deep/ [layout][around-justified] {
|
||||
-ms-flex-pack: around;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
html /deep/ [layout][justified] {
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* self alignment */
|
||||
|
||||
html /deep/ [self-start] {
|
||||
-ms-align-self: flex-start;
|
||||
-webkit-align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
html /deep/ [self-center] {
|
||||
-ms-align-self: center;
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
html /deep/ [self-end] {
|
||||
-ms-align-self: flex-end;
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
html /deep/ [self-stretch] {
|
||||
-ms-align-self: stretch;
|
||||
-webkit-align-self: stretch;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other Layout
|
||||
*******************************/
|
||||
|
||||
html /deep/ [block] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ie support for hidden */
|
||||
html /deep/ [hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
html /deep/ [invisible] {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
html /deep/ [relative] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html /deep/ [fit] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[fullbleed] {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
html /deep/ [scroll] {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Other
|
||||
*******************************/
|
||||
|
||||
html /deep/ [segment], html /deep/ segment {
|
||||
display: block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 1em 0.5em;
|
||||
padding: 1em;
|
||||
background-color: white;
|
||||
-webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
63
components/polymer/src/more-features/shadow-styler.html
Normal file
63
components/polymer/src/more-features/shadow-styler.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<script>
|
||||
|
||||
/* Configures elemnent styles to stamped into ShadowDOM.
|
||||
|
||||
NOTE: using this feature will currently change all templates to be configured
|
||||
to be styled for ShadowDOM.
|
||||
|
||||
* If an element has a template, style is placed in the template and element
|
||||
name is replaced with `:host`.
|
||||
* If an element does not have a template, style remains in place, but rules
|
||||
are prepended with `html /deep/`.
|
||||
|
||||
*/
|
||||
(function() {
|
||||
|
||||
XStyles = window.XStyles || {};
|
||||
|
||||
Base.addFeature({
|
||||
|
||||
register: function(prototype) {
|
||||
var template = prototype._template;
|
||||
var hook = template ||
|
||||
(document._currentScript || document.currentScript);
|
||||
var styles = [];
|
||||
var prev = hook.previousElementSibling;
|
||||
if (prev && prev.localName === 'style') {
|
||||
styles.push(prev);
|
||||
}
|
||||
styles = styles.concat(XStyles[prototype.name] || []);
|
||||
prototype._styles = styles;
|
||||
if (!styles.length) {
|
||||
return;
|
||||
}
|
||||
// use :host or /deep/ depending on if the element has a template and
|
||||
// therefore will have a shadowRoot.
|
||||
var selector = template ? ':host' : 'html /deep/ $&';
|
||||
this._processStyles(prototype._styles, prototype.name, selector);
|
||||
if (template) {
|
||||
this.insertStyles(prototype._styles, template.content);
|
||||
}
|
||||
},
|
||||
|
||||
_processStyles: function(styles, name, selector) {
|
||||
var re = new RegExp(name, 'g');
|
||||
for (var i=0, l=styles.length, style; (i<l) && (style=styles[i]); i++) {
|
||||
style.textContent = style.textContent.replace(re, selector);
|
||||
}
|
||||
},
|
||||
|
||||
insertStyles: function(styles, root) {
|
||||
var ref = root.firstChild;
|
||||
for (var i=0, l=styles.length, style, n; (i<l) && (style=styles[i]); i++) {
|
||||
n = document.createElement('style');
|
||||
n.textContent = style.textContent;
|
||||
ref = root.insertBefore(n,
|
||||
ref.nextElementSibling);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
9
components/polymer/src/more-features/shadow.html
Normal file
9
components/polymer/src/more-features/shadow.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
|
||||
Base.addFeature({
|
||||
createShadow: function(template) {
|
||||
this.root = this.createShadowRoot();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
17
components/polymer/src/polymer.html
Normal file
17
components/polymer/src/polymer.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<link rel="import" href="lang.html">
|
||||
<link rel="import" href="base.html">
|
||||
|
||||
<script>
|
||||
|
||||
Base.__proto__ = HTMLElement.prototype;
|
||||
|
||||
Polymer = function(prototype) {
|
||||
prototype.__proto__ = Base;
|
||||
prototype.registerCallback();
|
||||
document.registerElement(prototype.name, {prototype: prototype});
|
||||
};
|
||||
|
||||
Polymer.log = {
|
||||
};
|
||||
|
||||
</script>
|
||||
BIN
components/polymer/test/assets/Beaker2.jpg
Normal file
BIN
components/polymer/test/assets/Beaker2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
5
components/polymer/test/assets/test-style-path.html
Normal file
5
components/polymer/test/assets/test-style-path.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<style>
|
||||
.beaker {
|
||||
background-image: url('Beaker2.jpg');
|
||||
}
|
||||
</style>
|
||||
310
components/polymer/test/compat/polymer-smoke-elements.html
Normal file
310
components/polymer/test/compat/polymer-smoke-elements.html
Normal file
@@ -0,0 +1,310 @@
|
||||
<link rel="import" href="../../polymer/polymer.html">
|
||||
|
||||
<!--
|
||||
Below, some elements add additional features to Base.
|
||||
Ideally we Just Work under this scenario, but we need to take care
|
||||
because we are sharing a base class and there could be name-collisions.
|
||||
-->
|
||||
|
||||
<!-- raw -->
|
||||
|
||||
<style>
|
||||
|
||||
x-trivial {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-trivial',
|
||||
|
||||
// stubbing out `features` gives us maximum vanilla
|
||||
|
||||
features: function() {
|
||||
},
|
||||
|
||||
// These lifecycle callbacks are non-standard: the standard callbacks are
|
||||
// all suffixed by `Callback`. We reserve the native callbacks for system
|
||||
// processing in Base and supply the shorter names for individual elements
|
||||
// to override as necessary.
|
||||
|
||||
created: function() {
|
||||
this.innerHTML = '<i>Hey</i>, is this script <b>on</b>?';
|
||||
},
|
||||
|
||||
// these lifecycle callbacks are also available
|
||||
|
||||
attached: function() {
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
},
|
||||
|
||||
attributeChanged: function(name) {
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!-- using a template -->
|
||||
|
||||
<style>
|
||||
|
||||
x-template {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i>Hey</i>, is this template <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-template',
|
||||
|
||||
features: function() {
|
||||
// use template feature
|
||||
this.stampTemplate();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!-- inexpensive `nodes` feature -->
|
||||
|
||||
<style>
|
||||
|
||||
x-cheapgood-nodes {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim"></i>, is the node feature <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-cheapgood-nodes',
|
||||
|
||||
features: function() {
|
||||
// use template feature
|
||||
this.stampTemplate();
|
||||
// use `nodes` feature
|
||||
this.marshalNodeReferences();
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// `nodes` features populates `$` map from id's in the template
|
||||
this.$.exclaim.textContent = 'Hey';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!-- inexpensive `listeners` feature -->
|
||||
|
||||
<style>
|
||||
|
||||
x-cheapgood-listeners {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim">Hey</i>, are listeners <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-cheapgood-listeners',
|
||||
|
||||
listeners: {
|
||||
click: 'clickAction',
|
||||
'exclaim.click': 'exclaimClickAction'
|
||||
},
|
||||
|
||||
features: function() {
|
||||
// use template feature
|
||||
this.stampTemplate();
|
||||
// use `nodes` feature
|
||||
this.marshalNodeReferences();
|
||||
// use `listeners` feature
|
||||
this.listenListeners();
|
||||
},
|
||||
|
||||
clickAction: function() {
|
||||
this.style.backgroundColor = 'lightblue';
|
||||
},
|
||||
|
||||
exclaimClickAction: function(e) {
|
||||
e.stopPropagation();
|
||||
this.style.backgroundColor = 'orange';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!-- uses standard features (template, nodes, listeners) -->
|
||||
|
||||
<style>
|
||||
x-simple {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim"></i>, is simple mode <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-simple',
|
||||
|
||||
listeners: {
|
||||
click: 'clickAction',
|
||||
'exclaim.click': 'exclaimClickAction'
|
||||
},
|
||||
|
||||
created: function() {
|
||||
this.$.exclaim.textContent = 'Hey';
|
||||
},
|
||||
|
||||
clickAction: function() {
|
||||
this.style.backgroundColor = 'lightblue';
|
||||
},
|
||||
|
||||
exclaimClickAction: function(e) {
|
||||
e.stopPropagation();
|
||||
this.style.backgroundColor = 'orange';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!--
|
||||
|
||||
Can stop here if you want clean-and-simple.
|
||||
|
||||
This smoke test goes on to import and exercise some optional features
|
||||
to make sure everything plays well together.
|
||||
|
||||
-->
|
||||
|
||||
<!-- import and use `bind` feature -->
|
||||
|
||||
<link rel="import" href="../../polymer/more-features/bind.html">
|
||||
|
||||
<style>
|
||||
|
||||
x-fancy-bind {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim"></i>, is fancy bind mode <b id="state"></b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-fancy-bind',
|
||||
|
||||
listeners: {
|
||||
click: 'clickAction'
|
||||
},
|
||||
|
||||
bind: {
|
||||
// property: target (set property is pushed to $.<target>[.textContent])
|
||||
exclaim: 'exclaim',
|
||||
state: 'state'
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// NOTE: binding feature turns on automatically at prototype level, if
|
||||
// you include a `bind` object in your prototype.
|
||||
// These properties automatically propagate to DOM as defined
|
||||
// by the `bind` object.
|
||||
this.exclaim = 'Hey';
|
||||
this.state = 'on';
|
||||
},
|
||||
|
||||
clickAction: function() {
|
||||
this.state = 'LIVE';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- template annotations feature -->
|
||||
|
||||
<link rel="import" href="../../polymer/more-features/annotations.html">
|
||||
<link rel="import" href="../../polymer/more-features/bind-annotations.html">
|
||||
|
||||
<style>
|
||||
|
||||
x-fancy-annotations {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i>{{exclaim}}</i>, is fancy annotations mode <b>{{state}}</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-fancy-annotations',
|
||||
|
||||
created: function() {
|
||||
// NOTE: annotations feature turns on automatically at prototype level,
|
||||
// need to gate this as it's affecting all elements with templates
|
||||
// now.
|
||||
// Create bindings from annotations.
|
||||
this.marshalBoundNodes();
|
||||
// These properties automatically propagate to DOM as defined
|
||||
// by the template annotations.
|
||||
this.exclaim = 'Hey';
|
||||
this.state = 'on';
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
23
components/polymer/test/compat/polymer-smoke-polyfill.html
Normal file
23
components/polymer/test/compat/polymer-smoke-polyfill.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<script src="//www.polymer-project.org/components/platform/platform.js"></script>
|
||||
|
||||
<link rel="import" href="polymer-smoke-elements.html">
|
||||
|
||||
<x-trivial></x-trivial>
|
||||
|
||||
<!-- using a template -->
|
||||
<x-template></x-template>
|
||||
|
||||
<!-- inexpensive `nodes` feature -->
|
||||
<x-cheapgood-nodes></x-cheapgood-nodes>
|
||||
|
||||
<!-- inexpensive `listeners` feature -->
|
||||
<x-cheapgood-listeners></x-cheapgood-listeners>
|
||||
|
||||
<!-- uses standard features (shadow, template, nodes, listeners) -->
|
||||
<x-simple></x-simple>
|
||||
|
||||
<!-- import and use `bind` feature -->
|
||||
<x-fancy-bind></x-fancy-bind>
|
||||
|
||||
<!-- template annotations feature -->
|
||||
<x-fancy-annotations></x-fancy-annotations>
|
||||
155
components/polymer/test/elements-smoke.html
Normal file
155
components/polymer/test/elements-smoke.html
Normal file
@@ -0,0 +1,155 @@
|
||||
<link rel="import" href="../polymer.html">
|
||||
<link rel="import" href="../elements/x-toolbar.html">
|
||||
<link rel="import" href="../elements/x-overlay.html">
|
||||
<link rel="import" href="../elements/x-icon.html">
|
||||
<link rel="import" href="../elements/x-icon-button.html">
|
||||
<link rel="import" href="../elements/x-pages.html">
|
||||
<link rel="import" href="../elements/x-selector.html">
|
||||
<link rel="import" href="../elements/x-search-input.html">
|
||||
<link rel="import" href="../elements/x-drop-button.html">
|
||||
<link rel="import" href="../elements/paper-button.html">
|
||||
<link rel="import" href="../assets/icons.html">
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
x-icon-button {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
background-color: white;
|
||||
border: 1px outset silver;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
#overlay2 {
|
||||
width: 200px;
|
||||
height: 600px;
|
||||
background-color: white;
|
||||
border: 1px outset silver;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
#overlay3 {
|
||||
xwidth: 100px;
|
||||
background-color: white;
|
||||
border: 1px outset silver;
|
||||
}
|
||||
|
||||
#overlay3 [selected] {
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
#overlay3 h3 {
|
||||
padding: 8px 24px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#overlay3 [tools] {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
paper-button {
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
page {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#one {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
#two {
|
||||
background-color: #FFDCCA;
|
||||
}
|
||||
|
||||
x-drop-button {
|
||||
xdisplay: block;
|
||||
border: 1px dotted silver;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<body fullbleed vertical layout>
|
||||
|
||||
<x-toolbar>
|
||||
<x-icon-button icon="menu"></x-icon-button>
|
||||
<h2>Hello World</h2>
|
||||
<x-search-input></x-search-input>
|
||||
<span flex></span>
|
||||
<x-icon-button icon="schedule" onclick="pages.selected = 1"></x-icon-button>
|
||||
<x-icon-button icon="settings" onclick="pages.selected = 0"></x-icon-button>
|
||||
<span flex five></span>
|
||||
</x-toolbar>
|
||||
|
||||
<x-pages id="pages" flex scroll>
|
||||
|
||||
<page id="one">
|
||||
|
||||
<div style="height: 500px; padding: 16px;">
|
||||
<x-drop-button onclick="overlay3.open(this.getBoundingClientRect(), true);">Foo</x-drop-button>
|
||||
</div>
|
||||
|
||||
<paper-button onclick="overlay.open();">Dialog</paper-button>
|
||||
<paper-button onclick="overlay3.open(this.getBoundingClientRect(), true);">Pop</paper-button>
|
||||
<div style="height: 2500px;"></div>
|
||||
|
||||
</page>
|
||||
|
||||
<page id="two">
|
||||
|
||||
<br>
|
||||
<h2>Two</h2>
|
||||
<br>
|
||||
|
||||
</page>
|
||||
|
||||
</x-pages>
|
||||
|
||||
<x-overlay id="overlay" vertical layout>
|
||||
|
||||
<div horizontal end-justified layout>
|
||||
<paper-button onclick="overlay2.open();">Nest</paper-button>
|
||||
</div>
|
||||
<div flex></div>
|
||||
<div horizontal end-justified layout>
|
||||
<paper-button onclick="overlay.close();">Close</paper-button>
|
||||
</div>
|
||||
|
||||
</x-overlay>
|
||||
|
||||
<x-overlay id="overlay2" vertical layout>
|
||||
|
||||
<div horizontal end-justified layout>
|
||||
<paper-button onclick="overlay3.open(this.getBoundingClientRect());">Nest 2</paper-button>
|
||||
</div>
|
||||
<div flex></div>
|
||||
<div horizontal end-justified layout>
|
||||
<paper-button onclick="overlay2.close();">Close</paper-button>
|
||||
</div>
|
||||
|
||||
</x-overlay>
|
||||
|
||||
<x-overlay id="overlay3" vertical layout>
|
||||
|
||||
<x-selector block flex>
|
||||
<h3>Alpha</h3>
|
||||
<h3>Beta</h3>
|
||||
<h3>Gamma</h3>
|
||||
</x-selector>
|
||||
<div tools horizontal end-justified layout>
|
||||
<paper-button onclick="overlay3.close();">Close</paper-button>
|
||||
</div>
|
||||
|
||||
</x-overlay>
|
||||
|
||||
</body>
|
||||
14
components/polymer/test/index.html
Normal file
14
components/polymer/test/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="../../web-component-tester/browser.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
WCT.loadSuites([
|
||||
'unit/base.html',
|
||||
]);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
337
components/polymer/test/polymer-smoke.html
Normal file
337
components/polymer/test/polymer-smoke.html
Normal file
@@ -0,0 +1,337 @@
|
||||
<link rel="import" href="../polymer.html">
|
||||
|
||||
<!--
|
||||
Below, some elements add additional features to Base.
|
||||
Ideally we Just Work under this scenario, but we need to take care
|
||||
because we are sharing a base class and there could be name-collisions.
|
||||
-->
|
||||
|
||||
<!-- raw -->
|
||||
|
||||
<x-trivial></x-trivial>
|
||||
|
||||
<style>
|
||||
|
||||
x-trivial {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-trivial',
|
||||
|
||||
// stubbing out `features` gives us maximum vanilla
|
||||
|
||||
//features: Polymer.noFeatures,
|
||||
|
||||
features: function() {
|
||||
},
|
||||
|
||||
// These lifecycle callbacks are non-standard: the standard callbacks are
|
||||
// all suffixed by `Callback`. We reserve the native callbacks for system
|
||||
// processing in Base and supply the shorter names for individual elements
|
||||
// to override as necessary.
|
||||
|
||||
created: function() {
|
||||
this.innerHTML = '<i>Hey</i>, is this script <b>on</b>?';
|
||||
},
|
||||
|
||||
// these lifecycle callbacks are also available
|
||||
|
||||
attached: function() {
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
},
|
||||
|
||||
attributeChanged: function(name) {
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- using a template -->
|
||||
|
||||
<x-template></x-template>
|
||||
|
||||
<style>
|
||||
|
||||
x-template {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i>Hey</i>, is this template <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-template',
|
||||
|
||||
features: function() {
|
||||
// use template feature
|
||||
this.stampTemplate();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!--
|
||||
Without platform support for tree-scoping (yay ShadowDOM), robust composition
|
||||
support requires careful processing of nodes at the javascript level.
|
||||
For this reason, the very next feature we require is template annotation.
|
||||
When platforms support tree-scoping, we can defer the expense of template
|
||||
annotation processing for more advanced features.
|
||||
-->
|
||||
|
||||
<x-template-annote></x-template-annote>
|
||||
|
||||
<style>
|
||||
|
||||
x-template-annote {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim"></i>, is the annotation feature <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-template-annote',
|
||||
|
||||
features: function() {
|
||||
this.stampTemplate();
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// locate the first annotated node, which we happen to know
|
||||
this.$ = {
|
||||
exclaim: this.findAnnotatedNode(this.root, this._template.map[0])
|
||||
};
|
||||
this.$.exclaim.textContent = 'Hey';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!--
|
||||
Once we have enabled parsing template annotations, we can use it to
|
||||
extract nodes with ids (and be safe around simple composition [but not
|
||||
yet projection-safe])
|
||||
-->
|
||||
|
||||
<x-cheapgood-nodes></x-cheapgood-nodes>
|
||||
|
||||
<style>
|
||||
|
||||
x-cheapgood-nodes {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim"></i>, is the nodes feature <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-cheapgood-nodes',
|
||||
|
||||
features: function() {
|
||||
this.stampTemplate();
|
||||
// use `nodes` feature
|
||||
this.marshalNodeReferences();
|
||||
},
|
||||
|
||||
created: function() {
|
||||
this.$.exclaim.textContent = 'Hey';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- inexpensive `listeners` feature -->
|
||||
|
||||
<x-cheapgood-listeners></x-cheapgood-listeners>
|
||||
|
||||
<style>
|
||||
|
||||
x-cheapgood-listeners {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim">Hey</i>, are listeners <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-cheapgood-listeners',
|
||||
|
||||
listeners: {
|
||||
click: 'clickAction',
|
||||
'exclaim.click': 'exclaimClickAction'
|
||||
},
|
||||
|
||||
features: function() {
|
||||
this.stampTemplate();
|
||||
this.marshalNodeReferences();
|
||||
// use `listeners` feature
|
||||
this.listenListeners();
|
||||
},
|
||||
|
||||
clickAction: function() {
|
||||
this.style.backgroundColor = 'lightblue';
|
||||
},
|
||||
|
||||
exclaimClickAction: function(e) {
|
||||
e.stopPropagation();
|
||||
this.style.backgroundColor = 'orange';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- `bind` feature -->
|
||||
|
||||
<x-dom-bind></x-dom-bind>
|
||||
|
||||
<style>
|
||||
|
||||
x-dom-bind {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i id="exclaim"></i>, is dom-bind mode <b id="state"></b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-dom-bind',
|
||||
|
||||
listeners: {
|
||||
click: 'clickAction'
|
||||
},
|
||||
|
||||
bind: {
|
||||
// property: target (set property is pushed to $.<target>[.textContent])
|
||||
exclaim: 'exclaim',
|
||||
state: 'state'
|
||||
},
|
||||
|
||||
// just use the standard set
|
||||
// features: Polymer.defaultFeatures
|
||||
|
||||
created: function() {
|
||||
// NOTE: binding feature turns on automatically at prototype level, if
|
||||
// you include a `bind` object in your prototype.
|
||||
// These properties automatically propagate to DOM as defined
|
||||
// by the `bind` object.
|
||||
this.exclaim = 'Hey';
|
||||
this.state = 'on';
|
||||
},
|
||||
|
||||
clickAction: function() {
|
||||
this.state = 'LIVE';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- annotation-binding feature -->
|
||||
|
||||
<x-bind-annotations></x-bind-annotations>
|
||||
|
||||
<style>
|
||||
|
||||
x-bind-annotations {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i>{{exclaim}}</i>, are bind-annotations mode <b>{{state}}</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-bind-annotations',
|
||||
|
||||
created: function() {
|
||||
// These properties automatically propagate to DOM as defined
|
||||
// by the template annotations.
|
||||
this.exclaim = 'Hey';
|
||||
this.state = 'on';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- projection feature -->
|
||||
|
||||
<x-project>Heya</x-project>
|
||||
|
||||
<style>
|
||||
|
||||
x-project {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<i><content></content></i>, are bind-annotations mode <b>on</b>?
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
name: 'x-project'
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
6
components/polymer/test/style-path-smoke.html
Normal file
6
components/polymer/test/style-path-smoke.html
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
<link rel="import" href="assets/test-style-path.html">
|
||||
|
||||
<body class="beaker">
|
||||
</body>
|
||||
|
||||
171
components/polymer/test/unit/base.html
Normal file
171
components/polymer/test/unit/base.html
Normal file
@@ -0,0 +1,171 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="../../../web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../src/lang.html">
|
||||
<link rel="import" href="../../src/base.html">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
var OrigBase = window.Base;
|
||||
var Base;
|
||||
var Child;
|
||||
var instance;
|
||||
|
||||
beforeEach(function() {
|
||||
// Ensure a clean environment for each test.
|
||||
Base = extend({}, OrigBase);
|
||||
Base._features = [];
|
||||
Child = Object.create(Base);
|
||||
instance = Object.create(Child);
|
||||
});
|
||||
|
||||
suite('addFeature', function() {
|
||||
|
||||
test('mixes the feature into Base', function() {
|
||||
assert.notOk(Base.someProperty);
|
||||
Base.addFeature({someProperty: 123});
|
||||
assert.equal(Base.someProperty, 123);
|
||||
});
|
||||
|
||||
// TODO(nevir): What's up with this behavior?
|
||||
test('kills init', function() {
|
||||
Base.init = function() {};
|
||||
Base.addFeature({});
|
||||
assert.notOk(Base.init);
|
||||
});
|
||||
|
||||
// TODO(nevir): Ditto above.
|
||||
test('kills register', function() {
|
||||
Base.register = function() {};
|
||||
Base.addFeature({});
|
||||
assert.notOk(Base.register);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('registerCallback', function() {
|
||||
|
||||
test('calls register() for any features', function() {
|
||||
var called = [];
|
||||
Base.addFeature({register: function() {called.push('one')}});
|
||||
Base.addFeature({register: function() {called.push('two')}});
|
||||
assert.deepEqual(called, []);
|
||||
|
||||
Child.registerCallback();
|
||||
assert.includeMembers(called, ['one', 'two']);
|
||||
});
|
||||
|
||||
test('passes the prototype to feature register()', function() {
|
||||
Base.addFeature({register: function(prototype) {
|
||||
assert.equal(prototype, Child);
|
||||
}});
|
||||
Child.registerCallback();
|
||||
});
|
||||
|
||||
// TODO(nevir): Pull sinon into WCT to make this sort of test cleaner.
|
||||
test('calls registered() after features', function() {
|
||||
var lastCalled = null;
|
||||
Base.addFeature({register: function() {
|
||||
assert.equal(lastCalled, null);
|
||||
lastCalled = 'feature';
|
||||
}});
|
||||
Child.registered = function() {
|
||||
assert.equal(lastCalled, 'feature');
|
||||
lastCalled = 'registered';
|
||||
};
|
||||
|
||||
Child.registerCallback(Base);
|
||||
assert.equal(lastCalled, 'registered');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('createdCallback', function() {
|
||||
|
||||
// TODO(nevir): sinonify.
|
||||
test('calls lifecycle events in the proper order', function() {
|
||||
var lastCalled = null;
|
||||
Child.beforeCreated = function() {
|
||||
assert.equal(lastCalled, null);
|
||||
lastCalled = 'beforeCreated';
|
||||
};
|
||||
Base.addFeature({init: function() {
|
||||
assert.equal(lastCalled, 'beforeCreated');
|
||||
lastCalled = 'feature';
|
||||
}});
|
||||
Child.created = function() {
|
||||
assert.equal(lastCalled, 'feature');
|
||||
lastCalled = 'created';
|
||||
};
|
||||
Child.afterCreated = function() {
|
||||
assert.equal(lastCalled, 'created');
|
||||
lastCalled = 'afterCreated';
|
||||
};
|
||||
|
||||
instance.createdCallback();
|
||||
assert.equal(lastCalled, 'afterCreated');
|
||||
});
|
||||
|
||||
test('calls init() for any features', function() {
|
||||
var called = [];
|
||||
Base.addFeature({init: function() {called.push('one')}});
|
||||
Base.addFeature({init: function() {called.push('two')}});
|
||||
assert.deepEqual(called, []);
|
||||
|
||||
instance.createdCallback();
|
||||
assert.includeMembers(called, ['one', 'two']);
|
||||
});
|
||||
|
||||
test('calls feature init() with the correct `this`', function() {
|
||||
Base.addFeature({init: function() {
|
||||
assert.equal(this, instance);
|
||||
}});
|
||||
instance.createdCallback();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('attachedCallback', function() {
|
||||
|
||||
test('calls attached()', function() {
|
||||
var called = false;
|
||||
Child.attached = function() {called = true};
|
||||
instance.attachedCallback();
|
||||
assert.isTrue(called);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('detachedCallback', function() {
|
||||
|
||||
test('calls detached()', function() {
|
||||
var called = false;
|
||||
Child.detached = function() {called = true};
|
||||
instance.detachedCallback();
|
||||
assert.isTrue(called);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('attributeChangedCallback', function() {
|
||||
|
||||
test('calls attributeChanged()', function() {
|
||||
var args = null;
|
||||
Child.attributeChanged = function() {args = arguments};
|
||||
instance.attributeChangedCallback('attr', null, 1, 'stuff');
|
||||
|
||||
assert.equal(args.length, 4);
|
||||
assert.equal(args[0], 'attr');
|
||||
assert.equal(args[1], null);
|
||||
assert.equal(args[2], 1);
|
||||
assert.equal(args[3], 'stuff');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1
components/vulcanize/.gitignore
vendored
Normal file
1
components/vulcanize/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
111
components/vulcanize/bin/vulcanize
Normal file
111
components/vulcanize/bin/vulcanize
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var nopt = require('nopt');
|
||||
var vulcan = require('../lib/vulcan.js');
|
||||
|
||||
var pkg = require('../package.json');
|
||||
|
||||
var help = [
|
||||
'vulcanize: Concatenate a set of Web Components into one file',
|
||||
'',
|
||||
'Usage:',
|
||||
' vulcanize [OPTIONS] <html file>*',
|
||||
'',
|
||||
'Options:',
|
||||
' --output, -o: Output file name (defaults to vulcanized.html)',
|
||||
' --verbose, -v: More verbose logging',
|
||||
' --help, -h, -?: Print this message',
|
||||
' --config: Read the given config file',
|
||||
' --strip, -s: Remove comments and empty text nodes',
|
||||
' --abspath, -p: Specify site root. Resolve paths to absolute paths based on site root',
|
||||
' --csp: Extract inline scripts to a separate file (uses <output file name>.js)',
|
||||
' --inline: The opposite of CSP mode, inline all assets (script and css) into the document',
|
||||
' --csp --inline: Bundle all javascript (inline and external) into <output file name>.js',
|
||||
' --version, -V: print version information',
|
||||
' --no-strip-excludes: Keep imports excluded from inlining',
|
||||
' --no-update-notifier: disable "update vulcanize" checks',
|
||||
'',
|
||||
'Config:',
|
||||
' JSON file for additional options',
|
||||
'',
|
||||
' {',
|
||||
' "excludes": {',
|
||||
' "imports": [ "regex-to-exclude" ],',
|
||||
' "styles": [ "regex-to-exclude" ],',
|
||||
' "scripts": [ "regex-to-exclude" ],',
|
||||
' }',
|
||||
' }'
|
||||
];
|
||||
|
||||
function printHelp() {
|
||||
console.log(help.join('\n'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var options = nopt(
|
||||
{
|
||||
'config': path,
|
||||
'csp': Boolean,
|
||||
'help': Boolean,
|
||||
'inline': Boolean,
|
||||
'output': path,
|
||||
'abspath': path,
|
||||
'strip': Boolean,
|
||||
'verbose': Boolean,
|
||||
'version': Boolean
|
||||
},
|
||||
{
|
||||
'?': ['--help'],
|
||||
'h': ['--help'],
|
||||
'o': ['--output'],
|
||||
'p': ['--abspath'],
|
||||
's': ['--strip'],
|
||||
'v': ['--verbose'],
|
||||
'V': ['--version']
|
||||
}
|
||||
);
|
||||
|
||||
if (options.help || process.argv.length === 2) {
|
||||
printHelp();
|
||||
}
|
||||
|
||||
if (options.version) {
|
||||
console.log('vulcanize %s', pkg.version);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (options['update-notifier'] !== false) {
|
||||
(function() {
|
||||
try {
|
||||
require('update-notifier')({
|
||||
packageName: pkg.name,
|
||||
packageVersion: pkg.version
|
||||
}).notify();
|
||||
} catch(_) {}
|
||||
})();
|
||||
}
|
||||
|
||||
var argv = options.argv.remain;
|
||||
|
||||
if (argv[0]) {
|
||||
options.input = path.resolve(argv[0]);
|
||||
}
|
||||
|
||||
vulcan.setOptions(options, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
vulcan.processDocument();
|
||||
});
|
||||
2
components/vulcanize/example/.gitignore
vendored
Normal file
2
components/vulcanize/example/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
bower_components
|
||||
vulcanized*
|
||||
12
components/vulcanize/example/absolutes.html
Normal file
12
components/vulcanize/example/absolutes.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<link rel="import" href="http://example.com/foo/bar.html">
|
||||
<link rel="import" href="http://example.com/foo/fizz/../bar.html">
|
||||
<link rel="import" href="/foo/bar.html">
|
||||
5
components/vulcanize/example/config.json
Normal file
5
components/vulcanize/example/config.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"excludes": {
|
||||
"imports": ["polymer.html$"]
|
||||
}
|
||||
}
|
||||
10
components/vulcanize/example/empty.css
Normal file
10
components/vulcanize/example/empty.css
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @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
|
||||
*/
|
||||
|
||||
15
components/vulcanize/example/import-test.css
Normal file
15
components/vulcanize/example/import-test.css
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* @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
|
||||
*/
|
||||
|
||||
:host([type="platform"]) { background-color: red; }
|
||||
:host([type="core"]) { background-color: red; }
|
||||
:host([type="elements"]) { background-color: red; }
|
||||
polyfill-next-selector { content: ':host header'; }
|
||||
polyfill-next-selector { content: 'I WIN'; }
|
||||
58
components/vulcanize/example/import-test.html
Normal file
58
components/vulcanize/example/import-test.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<link rel="import" href="bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="sub-import/sub-import.html">
|
||||
<polymer-element name="x-import">
|
||||
<template>
|
||||
<link rel="stylesheet" href="import-test.css" shim-shadowdom>
|
||||
[Imported: <content></content>]
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 630 630" fill="#fff" width="630" height="630" flex?="{{mode !== 'cover'}}">
|
||||
<rect x="0" y="0" width="630" height="630" fill="#3c790a"/>
|
||||
<path d="m212 497c11 17 20 31 40 31 19 0 32-8 32-37v-201h59v202c0 61-36 89-88 89-47 0-75-25-89-54M423 492c13 21 29 36 58 36 25 0 40-12 40-29 0-20-16-27-43-39l-15-6c-43-18-71-41-71-89 0-44 34-78 87-78 38 0 65 13 84 47l-46 30c-10-18-21-25-38-25-17 0-28 11-28 25 0 18 11 25 36 36l15 6c50 22 79 44 79 93 0 53-42 82-98 82-55 0-91-26-108-60"/>
|
||||
<polygon />
|
||||
<path />
|
||||
<polygon/>
|
||||
<path />
|
||||
</svg>
|
||||
<ceci-definition>
|
||||
{
|
||||
"name": "Signal Transformer",
|
||||
"thumbnail": "./thumbnail.png",
|
||||
"description": "Transforms an incoming signal to a value you set and broadcasts it.",
|
||||
"broadcasts": {
|
||||
"transformedMessage": {
|
||||
"label": "Transformed Message"
|
||||
}
|
||||
},
|
||||
"listeners": {
|
||||
"incomingMessage": {
|
||||
"label": "Incoming Message",
|
||||
"default" : true
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
"message": {
|
||||
"label": "Message",
|
||||
"editable": "Text",
|
||||
"default" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
</ceci-definition>
|
||||
" entities! " < >
|
||||
<span>é</span>
|
||||
</template>
|
||||
<script>
|
||||
Polymer('x-import');
|
||||
</script>
|
||||
<script type="application/fbs">
|
||||
WHAT GOES HERE?
|
||||
</script>
|
||||
</polymer-element>
|
||||
33
components/vulcanize/example/index.html
Normal file
33
components/vulcanize/example/index.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Vulcanizer Test</title>
|
||||
<script src="bower_components/platform/platform.js"></script>
|
||||
<link rel="import" href="absolutes.html">
|
||||
<link rel="import" href="import-test.html" />
|
||||
<link rel="stylesheet" href="empty.css">
|
||||
</head>
|
||||
<body>
|
||||
<x-import>Hello Import!</x-import>
|
||||
<y-import></y-import>
|
||||
<polymer-element name="x-foo" attributes="">
|
||||
<script>Polymer()</script>
|
||||
</polymer-element>
|
||||
<polymer-element name="x-bar" attributes="">
|
||||
<script>Polymer({})</script>
|
||||
</polymer-element>
|
||||
<script>
|
||||
console.log('<hi>');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
23
components/vulcanize/example/sub-import/sub-import.html
Normal file
23
components/vulcanize/example/sub-import/sub-import.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<polymer-element name="y-import">
|
||||
<template>
|
||||
<style>
|
||||
:host{
|
||||
background: url(../foo.jpg);
|
||||
}
|
||||
</style>
|
||||
<a href="mailto:azakus@users.noreply.github.com">REPORT A BUG</a>
|
||||
</template>
|
||||
<script>
|
||||
Polymer('y-import');
|
||||
</script>
|
||||
</polymer-element>
|
||||
32
components/vulcanize/lib/constants.js
Normal file
32
components/vulcanize/lib/constants.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
var JS = 'script:not([type]), script[type="text/javascript"]';
|
||||
var URL_ATTR = ['href', 'src', 'action', 'style'];
|
||||
|
||||
module.exports = {
|
||||
EOL: require('os').EOL,
|
||||
ELEMENTS: 'polymer-element:not([assetpath])',
|
||||
ELEMENTS_NOSCRIPT: 'polymer-element[noscript]',
|
||||
ABS_URL: /(^data:)|(^http[s]?:)|(^\/)|(^mailto:)/,
|
||||
REMOTE_ABS_URL: /(^http[s]?\:)|(^\/\/)/,
|
||||
IMPORTS: 'link[rel="import"][href]',
|
||||
URL: /url\([^)]*\)/g,
|
||||
URL_ATTR: URL_ATTR,
|
||||
URL_ATTR_SEL: '[' + URL_ATTR.join('],[') + ']',
|
||||
URL_TEMPLATE: '{{.*}}',
|
||||
JS: JS,
|
||||
JS_SRC: JS.split(',').map(function(s){ return s + '[src]'; }).join(','),
|
||||
JS_INLINE: JS.split(',').map(function(s) { return s + ':not([src])'; }).join(','),
|
||||
CSS: 'style:not([type]), style[type="text/css"]',
|
||||
// Output match is [ 'Polymer(', NAME_OF_ELEMENT OR undefined, ',', { or ) ]
|
||||
POLYMER_INVOCATION: /Polymer\(([^,{]+)?(,\s*)?({|\))/,
|
||||
NEOPRENE_INVOCATION: /name:\s*['"]([^'"]*)['"]/
|
||||
};
|
||||
104
components/vulcanize/lib/optparser.js
Normal file
104
components/vulcanize/lib/optparser.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
var CONSTANTS = require('./constants.js');
|
||||
var ABS_URL = CONSTANTS.ABS_URL;
|
||||
var REMOTE_ABS_URL = CONSTANTS.REMOTE_ABS_URL;
|
||||
var DEFAULT = 'vulcanized.html';
|
||||
|
||||
// validate options with boolean return
|
||||
function processOptions(options, callback) {
|
||||
var config = {};
|
||||
var excludes = {
|
||||
imports: [],
|
||||
scripts: [],
|
||||
styles: []
|
||||
};
|
||||
|
||||
options = options || Object.create(null);
|
||||
|
||||
if (options.config) {
|
||||
var configBlob;
|
||||
try {
|
||||
// TODO(dfreedm): Make this async
|
||||
configBlob = fs.readFileSync(options.config, 'utf8');
|
||||
} catch(e) {
|
||||
return callback('Config file not found!');
|
||||
}
|
||||
try {
|
||||
config = JSON.parse(configBlob);
|
||||
} catch(e) {
|
||||
return callback('Malformed config JSON!');
|
||||
}
|
||||
}
|
||||
|
||||
options.input = options.input || config.input;
|
||||
if (!options.input) {
|
||||
return callback('No input file given!');
|
||||
}
|
||||
|
||||
options.excludes = options.excludes || config.excludes;
|
||||
if (options.excludes) {
|
||||
var e = options.excludes;
|
||||
try {
|
||||
if (e.imports) {
|
||||
e.imports.forEach(function(r) {
|
||||
excludes.imports.push(new RegExp(r));
|
||||
});
|
||||
}
|
||||
if (e.scripts) {
|
||||
e.scripts.forEach(function(r) {
|
||||
excludes.scripts.push(new RegExp(r));
|
||||
});
|
||||
}
|
||||
if (e.styles) {
|
||||
e.styles.forEach(function(r) {
|
||||
excludes.styles.push(new RegExp(r));
|
||||
});
|
||||
}
|
||||
} catch(_) {
|
||||
return callback('Malformed import exclude config');
|
||||
}
|
||||
}
|
||||
|
||||
options.output = options.output || config.output;
|
||||
if (!options.output) {
|
||||
options.output = path.resolve(path.dirname(options.input), DEFAULT);
|
||||
}
|
||||
options.outputDir = path.dirname(options.output);
|
||||
|
||||
options.csp = options.csp || config.csp;
|
||||
if (options.csp) {
|
||||
options.csp = options.output.replace(/\.html$/, '.js');
|
||||
}
|
||||
|
||||
options.abspath = options.abspath || config.abspath;
|
||||
if (options.abspath) {
|
||||
options.abspath = path.resolve(options.abspath);
|
||||
excludes.imports.push(REMOTE_ABS_URL);
|
||||
excludes.scripts.push(REMOTE_ABS_URL);
|
||||
excludes.styles.push(REMOTE_ABS_URL);
|
||||
} else {
|
||||
excludes.imports.push(ABS_URL);
|
||||
excludes.scripts.push(ABS_URL);
|
||||
excludes.styles.push(ABS_URL);
|
||||
}
|
||||
|
||||
options.excludes = excludes;
|
||||
|
||||
options.keepExcludes = options['strip-excludes'] === false || config['strip-excludes'] === false;
|
||||
|
||||
callback(null, options);
|
||||
}
|
||||
|
||||
exports.processOptions = processOptions;
|
||||
84
components/vulcanize/lib/pathresolver.js
Normal file
84
components/vulcanize/lib/pathresolver.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var constants = require('./constants.js');
|
||||
var utils = require('./utils.js');
|
||||
var setTextContent = utils.setTextContent;
|
||||
var getTextContent = utils.getTextContent;
|
||||
|
||||
function resolvePaths($, input, output, abspath) {
|
||||
var assetPath;
|
||||
if (abspath) {
|
||||
assetPath = rebasePath(input, abspath);
|
||||
} else {
|
||||
assetPath = path.relative(output, input);
|
||||
}
|
||||
// make sure assetpath is a folder, but not root!
|
||||
if (assetPath) {
|
||||
assetPath = utils.unixPath(assetPath) + '/';
|
||||
}
|
||||
// resolve attributes
|
||||
$(constants.URL_ATTR_SEL).each(function() {
|
||||
var el = $(this);
|
||||
constants.URL_ATTR.forEach(function(a) {
|
||||
var val = el.attr(a);
|
||||
if (val) {
|
||||
if (val.search(constants.URL_TEMPLATE) < 0) {
|
||||
if (a === 'style') {
|
||||
el.attr(a, rewriteURL(input, output, val, abspath));
|
||||
} else {
|
||||
el.attr(a, rewriteRelPath(input, output, val, abspath));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
$(constants.CSS).each(function() {
|
||||
var el = $(this);
|
||||
var text = rewriteURL(input, output, getTextContent(el), abspath);
|
||||
setTextContent(el, text);
|
||||
});
|
||||
$(constants.ELEMENTS).each(function() {
|
||||
$(this).attr('assetpath', assetPath);
|
||||
});
|
||||
}
|
||||
|
||||
function rebasePath(absolutePath, baselinePath) {
|
||||
var absBase = new RegExp('^' + utils.escapeForRegExp(baselinePath));
|
||||
return absolutePath.replace(absBase, '');
|
||||
}
|
||||
|
||||
function rewriteRelPath(inputPath, outputPath, rel, abspath) {
|
||||
if (constants.ABS_URL.test(rel)) {
|
||||
return rel;
|
||||
}
|
||||
|
||||
var abs = path.resolve(inputPath, rel);
|
||||
|
||||
if (abspath) {
|
||||
return utils.unixPath(rebasePath(abs, abspath));
|
||||
}
|
||||
|
||||
var relPath = path.relative(outputPath, abs);
|
||||
return utils.unixPath(relPath);
|
||||
}
|
||||
|
||||
function rewriteURL(inputPath, outputPath, cssText, abspath) {
|
||||
return cssText.replace(constants.URL, function(match) {
|
||||
var path = match.replace(/["']/g, "").slice(4, -1);
|
||||
path = rewriteRelPath(inputPath, outputPath, path, abspath);
|
||||
return 'url("' + path + '")';
|
||||
});
|
||||
}
|
||||
|
||||
exports.resolvePaths = resolvePaths;
|
||||
exports.rewriteRelPath = rewriteRelPath;
|
||||
exports.rewriteURL = rewriteURL;
|
||||
63
components/vulcanize/lib/utils.js
Normal file
63
components/vulcanize/lib/utils.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
// directly update the textnode child of <style>
|
||||
// equivalent to <style>.textContent
|
||||
setTextContent: function(node, text) {
|
||||
var unwrapped = node.get(0);
|
||||
var child = unwrapped.children[0];
|
||||
if (child) {
|
||||
child.data = text;
|
||||
} else {
|
||||
unwrapped.children[0] = {
|
||||
data: text,
|
||||
type: 'text',
|
||||
next: null,
|
||||
prev: null,
|
||||
parent: unwrapped
|
||||
};
|
||||
}
|
||||
},
|
||||
getTextContent: function(node) {
|
||||
var unwrapped = node.get(0);
|
||||
var child = unwrapped.children[0];
|
||||
return child ? child.data : '';
|
||||
},
|
||||
// escape a string to be used in new RegExp
|
||||
escapeForRegExp: function(s) {
|
||||
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
},
|
||||
unixPath: function(inpath, optSep) {
|
||||
var sep = optSep || path.sep;
|
||||
if (sep !== '/') {
|
||||
inpath = inpath.split(sep).join('/');
|
||||
}
|
||||
return inpath;
|
||||
},
|
||||
processPolymerInvocation: function(elementName, invocation) {
|
||||
var name = invocation[1] || '';
|
||||
var split = invocation[2] || '';
|
||||
var trailing = invocation[3];
|
||||
var nameIsString = /^['"]/.test(name);
|
||||
if (!split) {
|
||||
// assume "name" is actually the prototype if it is not a string literal
|
||||
if (!name || (name && !nameIsString)) {
|
||||
trailing = name + trailing;
|
||||
name = '\'' + elementName + '\'';
|
||||
}
|
||||
if (trailing !== ')') {
|
||||
split = ',';
|
||||
}
|
||||
}
|
||||
return 'Polymer(' + name + split + trailing;
|
||||
}
|
||||
};
|
||||
308
components/vulcanize/lib/vulcan.js
Normal file
308
components/vulcanize/lib/vulcan.js
Normal file
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
// jshint node: true
|
||||
|
||||
var cleancss = require('clean-css');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var uglify = require('uglify-js');
|
||||
var url = require('url');
|
||||
var whacko = require('whacko');
|
||||
|
||||
var constants = require('./constants.js');
|
||||
var optparser = require('./optparser.js');
|
||||
var pathresolver = require('./pathresolver');
|
||||
var utils = require('./utils');
|
||||
var setTextContent = utils.setTextContent;
|
||||
var getTextContent = utils.getTextContent;
|
||||
|
||||
var read = {};
|
||||
var options = {};
|
||||
|
||||
// validate options with boolean return
|
||||
function setOptions(optHash, callback) {
|
||||
optparser.processOptions(optHash, function(err, o) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
options = o;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function exclude(regexes, href) {
|
||||
return regexes.some(function(r) {
|
||||
return r.test(href);
|
||||
});
|
||||
}
|
||||
|
||||
function excludeImport(href) {
|
||||
return exclude(options.excludes.imports, href);
|
||||
}
|
||||
|
||||
function excludeScript(href) {
|
||||
return exclude(options.excludes.scripts, href);
|
||||
}
|
||||
|
||||
function excludeStyle(href) {
|
||||
return exclude(options.excludes.styles, href);
|
||||
}
|
||||
|
||||
function readFile(file) {
|
||||
var content = fs.readFileSync(file, 'utf8');
|
||||
return content.replace(/^\uFEFF/, '');
|
||||
}
|
||||
|
||||
// inline relative linked stylesheets into <style> tags
|
||||
function inlineSheets($, inputPath, outputPath) {
|
||||
$('link[rel="stylesheet"]').each(function() {
|
||||
var el = $(this);
|
||||
var href = el.attr('href');
|
||||
if (href && !excludeStyle(href)) {
|
||||
|
||||
var rel = href;
|
||||
var inputPath = path.dirname(options.input);
|
||||
if (constants.ABS_URL.test(rel)) {
|
||||
var abs = path.resolve(inputPath, path.join(options.abspath, rel));
|
||||
rel = path.relative(options.outputDir, abs);
|
||||
}
|
||||
|
||||
var filepath = path.resolve(options.outputDir, rel);
|
||||
// fix up paths in the stylesheet to be relative to the location of the style
|
||||
var content = pathresolver.rewriteURL(path.dirname(filepath), outputPath, readFile(filepath));
|
||||
var styleEl = whacko('<style>' + content + '</style>');
|
||||
// clone attributes
|
||||
styleEl.attr(el.attr());
|
||||
// don't set href or rel on the <style>
|
||||
styleEl.attr('href', null);
|
||||
styleEl.attr('rel', null);
|
||||
el.replaceWith(whacko.html(styleEl));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function inlineScripts($, dir) {
|
||||
$(constants.JS_SRC).each(function() {
|
||||
var el = $(this);
|
||||
var src = el.attr('src');
|
||||
if (src && !excludeScript(src)) {
|
||||
|
||||
var rel = src;
|
||||
var inputPath = path.dirname(options.input);
|
||||
if (constants.ABS_URL.test(rel)) {
|
||||
var abs = path.resolve(inputPath, path.join(options.abspath, rel));
|
||||
rel = path.relative(options.outputDir, abs);
|
||||
}
|
||||
|
||||
var filepath = path.resolve(dir, rel);
|
||||
var content = readFile(filepath);
|
||||
// NOTE: reusing UglifyJS's inline script printer (not exported from OutputStream :/)
|
||||
content = content.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
|
||||
el.replaceWith('<script>' + content + '</script>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function concat(filename) {
|
||||
if (!read[filename]) {
|
||||
read[filename] = true;
|
||||
var $ = whacko.load(readFile(filename));
|
||||
var dir = path.dirname(filename);
|
||||
pathresolver.resolvePaths($, dir, options.outputDir, options.abspath);
|
||||
processImports($);
|
||||
inlineSheets($, dir, options.outputDir);
|
||||
return $;
|
||||
} else if (options.verbose) {
|
||||
console.log('Dependency deduplicated');
|
||||
}
|
||||
}
|
||||
|
||||
function processImports($, mainDoc) {
|
||||
var bodyContent = [];
|
||||
$(constants.IMPORTS).each(function() {
|
||||
var el = $(this);
|
||||
var href = el.attr('href');
|
||||
if (!excludeImport(href)) {
|
||||
var rel = href;
|
||||
var inputPath = path.dirname(options.input);
|
||||
if (constants.ABS_URL.test(rel)) {
|
||||
var abs = path.resolve(inputPath, path.join(options.abspath, rel));
|
||||
rel = path.relative(options.outputDir, abs);
|
||||
}
|
||||
var $$ = concat(path.resolve(options.outputDir, rel));
|
||||
if (!$$) {
|
||||
// remove import link
|
||||
el.remove();
|
||||
return;
|
||||
}
|
||||
// append import document head to main document head
|
||||
el.replaceWith($$('head').html());
|
||||
var bodyHTML = $$('body').html();
|
||||
// keep the ordering of the import body in main document, before main document's body
|
||||
bodyContent.push(bodyHTML);
|
||||
} else if (!options.keepExcludes) {
|
||||
// if the path is excluded for being absolute, then the import link must remain
|
||||
var absexclude = options.abspath ? constants.REMOTE_ABS_URL : constants.ABS_URL;
|
||||
if (!absexclude.test(href)) {
|
||||
el.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
// prepend the import document body contents to the main document, in order
|
||||
var content = bodyContent.join('\n');
|
||||
// hide import body content in the main document
|
||||
if (mainDoc && content) {
|
||||
content = '<div hidden>' + content + '</div>';
|
||||
}
|
||||
$('body').prepend(content);
|
||||
}
|
||||
|
||||
function findScriptLocation($) {
|
||||
var pos = $('body').last();
|
||||
if (!pos.length) {
|
||||
pos = $.root();
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// arguments are (index, node), where index is unnecessary
|
||||
function isCommentOrEmptyTextNode(_, node) {
|
||||
if (node.type === 'comment'){
|
||||
return true;
|
||||
} else if (node.type === 'text') {
|
||||
// return true if the node is only whitespace
|
||||
return !((/\S/).test(node.data));
|
||||
}
|
||||
}
|
||||
|
||||
function compressJS(content, inline) {
|
||||
var ast = uglify.parse(content);
|
||||
return ast.print_to_string({inline_script: inline});
|
||||
}
|
||||
|
||||
function removeCommentsAndWhitespace($) {
|
||||
$(constants.JS_INLINE).each(function() {
|
||||
var el = $(this);
|
||||
var content = getTextContent(el);
|
||||
setTextContent(el, compressJS(content, true));
|
||||
});
|
||||
$(constants.CSS).each(function() {
|
||||
var el = $(this);
|
||||
var content = getTextContent(el);
|
||||
setTextContent(el, new cleancss({noAdvanced: true}).minify(content));
|
||||
});
|
||||
|
||||
$('*').contents().filter(isCommentOrEmptyTextNode).remove();
|
||||
}
|
||||
|
||||
function writeFileSync(filename, data, eop) {
|
||||
if (!options.outputSrc) {
|
||||
fs.writeFileSync(filename, data, 'utf8');
|
||||
} else {
|
||||
options.outputSrc(filename, data, eop);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMainDocument() {
|
||||
// reset shared buffers
|
||||
read = {};
|
||||
var content = options.inputSrc ? options.inputSrc.toString() : readFile(options.input);
|
||||
var $ = whacko.load(content);
|
||||
var dir = path.dirname(options.input);
|
||||
pathresolver.resolvePaths($, dir, options.outputDir, options.abspath);
|
||||
processImports($, true);
|
||||
if (options.inline) {
|
||||
inlineSheets($, dir, options.outputDir);
|
||||
}
|
||||
|
||||
if (options.inline) {
|
||||
inlineScripts($, options.outputDir);
|
||||
}
|
||||
|
||||
$(constants.JS_INLINE).each(function() {
|
||||
var el = $(this);
|
||||
var content = getTextContent(el);
|
||||
// find ancestor polymer-element node
|
||||
var templateElement = el.prev('template').get(0);
|
||||
if (templateElement) {
|
||||
var match = constants.NEOPRENE_INVOCATION.exec(content);
|
||||
var elementName = $(templateElement).attr('id', match[1]);
|
||||
}
|
||||
});
|
||||
|
||||
// strip noscript from elements, and instead inject explicit Polymer() invocation
|
||||
// script, so registration order is preserved
|
||||
$(constants.ELEMENTS_NOSCRIPT).each(function() {
|
||||
var el = $(this);
|
||||
var name = el.attr('name');
|
||||
if (options.verbose) {
|
||||
console.log('Injecting explicit Polymer invocation for noscript element "' + name + '"');
|
||||
}
|
||||
el.append('<script>Polymer(\'' + name + '\');</script>');
|
||||
el.attr('noscript', null);
|
||||
});
|
||||
|
||||
// strip scripts into a separate file
|
||||
if (options.csp) {
|
||||
if (options.verbose) {
|
||||
console.log('Separating scripts into separate file');
|
||||
}
|
||||
|
||||
// CSPify main page by removing inline scripts
|
||||
var scripts = [];
|
||||
$(constants.JS_INLINE).each(function() {
|
||||
var el = $(this);
|
||||
var content = getTextContent(el);
|
||||
scripts.push(content);
|
||||
el.remove();
|
||||
});
|
||||
|
||||
// join scripts with ';' to prevent breakages due to EOF semicolon insertion
|
||||
var scriptName = path.basename(options.output, '.html') + '.js';
|
||||
var scriptContent = scripts.join(';' + constants.EOL);
|
||||
if (options.strip) {
|
||||
scriptContent = compressJS(scriptContent, false);
|
||||
}
|
||||
|
||||
writeFileSync(path.resolve(options.outputDir, scriptName), scriptContent);
|
||||
// insert out-of-lined script into document
|
||||
findScriptLocation($).append('<script src="' + scriptName + '"></script>');
|
||||
}
|
||||
|
||||
deduplicateImports($);
|
||||
|
||||
if (options.strip) {
|
||||
removeCommentsAndWhitespace($);
|
||||
}
|
||||
|
||||
writeFileSync(options.output, $.html(), true);
|
||||
}
|
||||
|
||||
function deduplicateImports($) {
|
||||
var imports = {};
|
||||
$(constants.IMPORTS).each(function() {
|
||||
var el = $(this);
|
||||
var href = el.attr('href');
|
||||
// TODO(dfreedm): allow a user defined base url?
|
||||
var abs = url.resolve('http://', href);
|
||||
if (!imports[abs]) {
|
||||
imports[abs] = true;
|
||||
} else {
|
||||
if(options.verbose) {
|
||||
console.log('Import Dependency deduplicated');
|
||||
}
|
||||
el.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.processDocument = handleMainDocument;
|
||||
exports.setOptions = setOptions;
|
||||
41
components/vulcanize/package.json
Normal file
41
components/vulcanize/package.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "vulcanize",
|
||||
"version": "0.6.1",
|
||||
"description": "Process Web Components into one output file",
|
||||
"main": "lib/vulcan.js",
|
||||
"bin": {
|
||||
"vulcanize": "bin/vulcanize"
|
||||
},
|
||||
"dependencies": {
|
||||
"clean-css": "^2.2.11",
|
||||
"nopt": "^3.0.1",
|
||||
"uglify-js": "^2.4.15",
|
||||
"whacko": "^0.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jshint": "^2.5.6",
|
||||
"mocha": "^2.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/jshint --verbose lib/*.js bin/* test/*.js && node_modules/.bin/mocha"
|
||||
},
|
||||
"author": "Daniel Freedman",
|
||||
"license": "BSD",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"keywords": [
|
||||
"web components",
|
||||
"polymer"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Polymer/vulcanize.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Polymer/vulcanize/issues"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"update-notifier": "^0.2.2"
|
||||
}
|
||||
}
|
||||
3
components/vulcanize/test/broken_config.json
Normal file
3
components/vulcanize/test/broken_config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
input: "index.html",
|
||||
}
|
||||
6
components/vulcanize/test/config.json
Normal file
6
components/vulcanize/test/config.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"csp": true,
|
||||
"excludes": {
|
||||
"imports": [".*"]
|
||||
}
|
||||
}
|
||||
22
components/vulcanize/test/html/default.html
Normal file
22
components/vulcanize/test/html/default.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="import" href="imports/simple-import.html">
|
||||
<link rel="import" href="imports/simple-import.html">
|
||||
<link rel="import" href="http://example.com/foo/bar.html">
|
||||
<title>Sample Build</title>
|
||||
</head>
|
||||
<body>
|
||||
<my-element></my-element>
|
||||
</body>
|
||||
</html>
|
||||
22
components/vulcanize/test/html/imports/simple-import.html
Normal file
22
components/vulcanize/test/html/imports/simple-import.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<polymer-element name="my-element" noscript>
|
||||
<template>
|
||||
<link rel="stylesheet" href="simple-style.css" shim-shadowdom>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 630 630">
|
||||
<rect x="0" y="0" width="630" height="630" fill="#3c790a"/>
|
||||
<path d="m212 497c11"/>
|
||||
<polygon />
|
||||
<path />
|
||||
<polygon/>
|
||||
<path />
|
||||
</svg>
|
||||
</template>
|
||||
</polymer-element>
|
||||
14
components/vulcanize/test/html/imports/simple-style.css
Normal file
14
components/vulcanize/test/html/imports/simple-style.css
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
@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
|
||||
*/
|
||||
:host([type="platform"]) { background-color: red; }
|
||||
:host([type="core"]) { background-color: red; }
|
||||
:host([type="elements"]) { background-color: red; }
|
||||
polyfill-next-selector { content: ':host header'; }
|
||||
polyfill-next-selector { content: 'I WIN'; }
|
||||
2
components/vulcanize/test/mocha.opts
Normal file
2
components/vulcanize/test/mocha.opts
Normal file
@@ -0,0 +1,2 @@
|
||||
--ui tdd
|
||||
--slow 300
|
||||
451
components/vulcanize/test/test.js
Normal file
451
components/vulcanize/test/test.js
Normal file
@@ -0,0 +1,451 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
// jshint node: true
|
||||
var assert = require('assert');
|
||||
var path = require('path');
|
||||
|
||||
assert.AssertionError.prototype.showDiff = true;
|
||||
|
||||
suite('constants', function() {
|
||||
var constants = require('../lib/constants.js');
|
||||
|
||||
suite('URLs', function() {
|
||||
|
||||
test('absolute urls', function() {
|
||||
var abs = constants.ABS_URL;
|
||||
|
||||
assert(abs.test('data:charset=utf8,'), 'data urls');
|
||||
assert(abs.test('http://foo.com'), 'http');
|
||||
assert(abs.test('https://foo.com'), 'https');
|
||||
assert(abs.test('mailto:foo@bar.com'), 'mailto');
|
||||
assert(abs.test('//foo.com'), 'protocol-free');
|
||||
assert(abs.test('/components/'), '/');
|
||||
assert(!abs.test('../foo/bar.html'), '../');
|
||||
assert(!abs.test('bar.html'), 'sibling dependency');
|
||||
});
|
||||
|
||||
test('remote absolute urls', function() {
|
||||
var rabs = constants.REMOTE_ABS_URL;
|
||||
|
||||
assert(rabs.test('http://foo.com'), 'http');
|
||||
assert(rabs.test('https://foo.com'), 'https');
|
||||
assert(rabs.test('//foo.com'), 'protocol-free');
|
||||
assert(!rabs.test('../foo/bar.html'), '../');
|
||||
assert(!rabs.test('bar.html'), 'sibling dependency');
|
||||
assert(!rabs.test('/components/'), '/');
|
||||
});
|
||||
|
||||
test('CSS URLs', function() {
|
||||
var url = constants.URL;
|
||||
|
||||
assert('url(foo.html)'.match(url), 'naked');
|
||||
assert('url(\'foo.html\')'.match(url), 'single quote');
|
||||
assert('url("foo.html")'.match(url), 'double quote');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Polymer Invocation', function() {
|
||||
var polymer = constants.POLYMER_INVOCATION;
|
||||
|
||||
function test(invocation, msg) {
|
||||
var matches = polymer.exec(invocation);
|
||||
assert(matches, 'polymer invocation found', msg);
|
||||
}
|
||||
|
||||
test('Polymer(\'core-input\', {})', 'full');
|
||||
test('Polymer(\'core-input\')', 'name-only');
|
||||
test('Polymer()', 'none');
|
||||
test('Polymer({})', 'object-only');
|
||||
test('Polymer(p)', 'indirect');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Path Resolver', function() {
|
||||
var pathresolver = require('../lib/pathresolver.js');
|
||||
var inputPath = '/foo/bar/my-element';
|
||||
var outputPath = '/foo/bar';
|
||||
|
||||
test('Rewrite URLs', function() {
|
||||
var css = [
|
||||
'x-element {',
|
||||
' background-image: url(foo.jpg);',
|
||||
'}',
|
||||
'x-bar {',
|
||||
' background-image: url(data:xxxxx);',
|
||||
'}',
|
||||
'x-quuz {',
|
||||
' background-image: url(\'https://foo.bar/baz.jpg\');',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'x-element {',
|
||||
' background-image: url("my-element/foo.jpg");',
|
||||
'}',
|
||||
'x-bar {',
|
||||
' background-image: url("data:xxxxx");',
|
||||
'}',
|
||||
'x-quuz {',
|
||||
' background-image: url("https://foo.bar/baz.jpg");',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
var actual = pathresolver.rewriteURL(inputPath, outputPath, css);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('Rewrite Paths', function() {
|
||||
function testPath(val, expected, abs, msg) {
|
||||
var actual = pathresolver.rewriteRelPath(inputPath, outputPath, val, abs);
|
||||
assert.equal(actual, expected, msg);
|
||||
}
|
||||
|
||||
testPath('biz.jpg', 'my-element/biz.jpg', null, 'local');
|
||||
testPath('http://foo/biz.jpg', 'http://foo/biz.jpg', null, 'remote');
|
||||
testPath('biz.jpg', 'bar/my-element/biz.jpg', '/foo/', 'build path');
|
||||
});
|
||||
|
||||
test('Resolve Paths', function() {
|
||||
var html = [
|
||||
'<link rel="import" href="../polymer/polymer.html">',
|
||||
'<link rel="stylesheet" href="my-element.css">',
|
||||
'<polymer-element name="my-element">',
|
||||
'<template>',
|
||||
'<style>:host { background-image: url(background.svg); }</style>',
|
||||
'<script>Polymer()</script>',
|
||||
'</template>',
|
||||
'</polymer-element>'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'<html><head><link rel="import" href="polymer/polymer.html">',
|
||||
'<link rel="stylesheet" href="my-element/my-element.css">',
|
||||
'</head><body><polymer-element name="my-element" assetpath="my-element/">',
|
||||
'<template>',
|
||||
'<style>:host { background-image: url("my-element/background.svg"); }</style>',
|
||||
'<script>Polymer()</script>',
|
||||
'</template>',
|
||||
'</polymer-element></body></html>'
|
||||
].join('\n');
|
||||
|
||||
var expected2 = [
|
||||
'<html><head><link rel="import" href="/bar/polymer/polymer.html">',
|
||||
'<link rel="stylesheet" href="/bar/my-element/my-element.css">',
|
||||
'</head><body><polymer-element name="my-element" assetpath="/bar/my-element/">',
|
||||
'<template>',
|
||||
'<style>:host { background-image: url("/bar/my-element/background.svg"); }</style>',
|
||||
'<script>Polymer()</script>',
|
||||
'</template>',
|
||||
'</polymer-element></body></html>'
|
||||
].join('\n');
|
||||
|
||||
var actual;
|
||||
var whacko = require('whacko');
|
||||
var $ = whacko.load(html);
|
||||
|
||||
pathresolver.resolvePaths($, inputPath, outputPath);
|
||||
|
||||
actual = $.html();
|
||||
assert.equal(actual, expected, 'relative');
|
||||
|
||||
$ = whacko.load(html);
|
||||
|
||||
pathresolver.resolvePaths($, inputPath, outputPath, '/foo');
|
||||
|
||||
actual = $.html();
|
||||
assert.equal(actual, expected2, 'absolute');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Utils', function() {
|
||||
var constants = require('../lib/constants.js');
|
||||
var utils = require('../lib/utils.js');
|
||||
|
||||
test('getTextContent', function() {
|
||||
var whacko = require('whacko');
|
||||
var divEl = whacko('<div>some text!</div>');
|
||||
assert.equal(utils.getTextContent(divEl), 'some text!', 'a textnode child');
|
||||
var blankEl = whacko('<div></div>');
|
||||
assert.equal(utils.getTextContent(blankEl), '', 'no textnode children');
|
||||
});
|
||||
|
||||
test('setTextContent', function() {
|
||||
var whacko = require('whacko');
|
||||
var divEl = whacko('<div></div>');
|
||||
utils.setTextContent(divEl, 'some text!');
|
||||
assert.equal(utils.getTextContent(divEl), 'some text!', 'create text node');
|
||||
utils.setTextContent(divEl, 'some text 2!');
|
||||
assert.equal(utils.getTextContent(divEl), 'some text 2!', 'override text node');
|
||||
});
|
||||
|
||||
test('unixPath', function() {
|
||||
var pp = ['foo', 'bar', 'baz'];
|
||||
var p = pp.join('/');
|
||||
var actual = utils.unixPath(p);
|
||||
assert.equal(actual, p, 'started unix');
|
||||
var p2 = pp.join('\\');
|
||||
actual = utils.unixPath(p2, '\\');
|
||||
assert.equal(actual, p, 'windows path');
|
||||
});
|
||||
|
||||
test('escapeForRegExp', function() {
|
||||
var actual = utils.escapeForRegExp('foo-bar');
|
||||
assert.equal(actual, 'foo\\-bar', 'element name');
|
||||
actual = utils.escapeForRegExp('foo/bar/baz');
|
||||
assert.equal(actual, 'foo\\/bar\\/baz', 'absolute path');
|
||||
});
|
||||
|
||||
test('Polymer Invocation', function() {
|
||||
var polymer = constants.POLYMER_INVOCATION;
|
||||
|
||||
function test(invocation, expected, msg) {
|
||||
var matches = polymer.exec(invocation);
|
||||
assert(matches, 'polymer invocation found');
|
||||
var replacement = utils.processPolymerInvocation('core-input', matches);
|
||||
var actual = invocation.replace(matches[0], replacement);
|
||||
assert.strictEqual(actual, expected, msg);
|
||||
}
|
||||
|
||||
test('Polymer(\'core-input\', {})', 'Polymer(\'core-input\', {})', 'full');
|
||||
test('Polymer(\'core-input\')', 'Polymer(\'core-input\')', 'name-only');
|
||||
test('Polymer()', 'Polymer(\'core-input\')', 'none');
|
||||
test('Polymer({})', 'Polymer(\'core-input\',{})', 'object-only');
|
||||
test('Polymer(p)', 'Polymer(\'core-input\',p)', 'indirect');
|
||||
|
||||
});
|
||||
|
||||
test('#82', function() {
|
||||
var constants = require('../lib/constants.js');
|
||||
var whacko = require('whacko');
|
||||
var $ = whacko.load('<polymer-element name="paper-button-base"><script>(function(){ Polymer(p);}()</script></polymer-element>');
|
||||
$(constants.JS_INLINE).each(function() {
|
||||
var el = $(this);
|
||||
var content = utils.getTextContent(el);
|
||||
assert(content);
|
||||
var parentElement = el.closest('polymer-element').get(0);
|
||||
if (parentElement) {
|
||||
var match = constants.POLYMER_INVOCATION.exec(content);
|
||||
var elementName = $(parentElement).attr('name');
|
||||
if (match) {
|
||||
var invocation = utils.processPolymerInvocation(elementName, match);
|
||||
content = content.replace(match[0], invocation);
|
||||
utils.setTextContent(el, content);
|
||||
}
|
||||
}
|
||||
assert.equal(utils.getTextContent(el), '(function(){ Polymer(\'paper-button-base\',p);}()');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
suite('Optparser', function() {
|
||||
var path = require('path');
|
||||
var optParser = require('../lib/optparser.js');
|
||||
var constants = require('../lib/constants.js');
|
||||
var ABS_URL = constants.ABS_URL;
|
||||
var REMOTE_ABS_URL = constants.REMOTE_ABS_URL;
|
||||
|
||||
function optParserTest(fn, opts, skipFail) {
|
||||
if (typeof opts === 'undefined') {
|
||||
opts = {input: path.resolve('index.html')};
|
||||
}
|
||||
optParser.processOptions(opts, function(err, options) {
|
||||
if (!skipFail) {
|
||||
assert.equal(err, null);
|
||||
}
|
||||
fn(err, options);
|
||||
});
|
||||
}
|
||||
|
||||
test('Error on no input', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(err, 'No input file given!');
|
||||
done();
|
||||
}, null, true);
|
||||
});
|
||||
|
||||
test('Defaults', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(options.input, path.resolve('index.html'));
|
||||
assert.equal(options.output, path.resolve('vulcanized.html'));
|
||||
assert.equal(options.outputDir, path.dirname(path.resolve('vulcanized.html')));
|
||||
assert(!options.csp);
|
||||
assert(!options.abspath);
|
||||
assert.deepEqual(options.excludes, {imports:[ABS_URL], scripts:[ABS_URL], styles:[ABS_URL]});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('CSP', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(options.csp, path.resolve('vulcanized.js'));
|
||||
done();
|
||||
}, {input: 'index.html', csp: true});
|
||||
});
|
||||
|
||||
test('output', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(options.output, path.resolve('build.html'));
|
||||
assert.equal(options.csp, path.resolve('build.js'));
|
||||
done();
|
||||
}, {input: path.resolve('index.html'), output: path.resolve('build.html'), csp: true});
|
||||
});
|
||||
|
||||
test('abspath', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(options.abspath, path.resolve('../'));
|
||||
assert.deepEqual(options.excludes, {imports:[REMOTE_ABS_URL], scripts:[REMOTE_ABS_URL], styles:[REMOTE_ABS_URL]});
|
||||
done();
|
||||
}, {input: path.resolve('index.html'), abspath: path.resolve('../')});
|
||||
});
|
||||
|
||||
test('excludes', function(done) {
|
||||
var excludes = {
|
||||
imports: [
|
||||
'.*'
|
||||
]
|
||||
};
|
||||
var expected = [/.*/, ABS_URL];
|
||||
|
||||
optParserTest(function(err, options) {
|
||||
assert.deepEqual(options.excludes.imports, expected);
|
||||
done();
|
||||
}, {input: path.resolve('index.html'), excludes: excludes});
|
||||
|
||||
});
|
||||
|
||||
test('config file', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(options.input, path.resolve('index.html'));
|
||||
assert.equal(options.output, path.resolve('build.html'));
|
||||
assert.equal(options.csp, path.resolve('build.js'));
|
||||
assert(!options.abspath);
|
||||
assert.deepEqual(options.excludes, {imports:[/.*/, ABS_URL], scripts:[ABS_URL], styles:[ABS_URL]});
|
||||
done();
|
||||
}, {config: path.resolve('test/config.json'), input: path.resolve('index.html'), output: path.resolve('build.html'), csp: true});
|
||||
});
|
||||
|
||||
test('report broken config file', function(done) {
|
||||
optParserTest(function(err, options) {
|
||||
assert.equal(err, 'Malformed config JSON!');
|
||||
done();
|
||||
}, {config: path.resolve('test/broken_config.json')}, true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Vulcan', function() {
|
||||
var vulcan = require('../lib/vulcan.js');
|
||||
var outputPath = path.resolve('test/html/actual.html');
|
||||
var inputPath = path.resolve('test/html/default.html');
|
||||
|
||||
test('set options', function(done) {
|
||||
var options = {
|
||||
input: 'index.html'
|
||||
};
|
||||
vulcan.setOptions(options, done);
|
||||
});
|
||||
|
||||
function process(options, fn) {
|
||||
var outputs = Object.create(null);
|
||||
options.outputSrc = function(name, data, eop) {
|
||||
if (!data) {
|
||||
throw new Error("Writing empty data");
|
||||
}
|
||||
outputs[name] = data;
|
||||
};
|
||||
vulcan.setOptions(options, function(err) {
|
||||
assert(!err);
|
||||
vulcan.processDocument();
|
||||
Object.keys(outputs).forEach(function(o) {
|
||||
assert.equal(typeof outputs[o], 'string', 'all buffers are closed');
|
||||
});
|
||||
fn(outputs);
|
||||
});
|
||||
}
|
||||
|
||||
test('defaults', function(done) {
|
||||
var getTextContent = require('../lib/utils.js').getTextContent;
|
||||
|
||||
process({input: inputPath, output: outputPath}, function(outputs) {
|
||||
assert.equal(Object.keys(outputs).length, 1);
|
||||
var vulcanized = outputs[outputPath];
|
||||
assert(vulcanized);
|
||||
var $ = require('whacko').load(vulcanized);
|
||||
assert.equal($('body > div[hidden]').length, 1, 'only one div[hidden]');
|
||||
assert.equal($('head > link[rel="import"]:not([href^="http://"])').length, 0, 'all relative imports removed');
|
||||
assert.equal($('polymer-element').length, 1, 'imports were deduplicated');
|
||||
assert.equal($('polymer-element').attr('noscript'), null, 'noscript removed');
|
||||
assert.equal(getTextContent($('polymer-element > script')), 'Polymer(\'my-element\');', 'polymer script included');
|
||||
assert.equal($('polymer-element > template > link').length, 0, 'external styles removed');
|
||||
assert.equal($('polymer-element > template > style').length, 1, 'styles inlined');
|
||||
assert.equal($('polymer-element > template > svg > *').length, 6, 'svg children propery nested');
|
||||
assert.equal($('polymer-element').attr('assetpath'), 'imports/', 'assetpath set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('CSP', function(done) {
|
||||
|
||||
process({input: inputPath, output: outputPath, csp: true}, function(outputs) {
|
||||
assert.equal(Object.keys(outputs).length, 2);
|
||||
var vulcanized = outputs[outputPath];
|
||||
var vulcanizedJS = outputs[path.resolve(outputPath, '../actual.js')];
|
||||
assert(vulcanized);
|
||||
assert(vulcanizedJS);
|
||||
var $ = require('whacko').load(vulcanized);
|
||||
assert($('body > script[src="actual.js"]'), 'vulcanized script in body');
|
||||
assert.equal($('body script:not([src])').length, 0, 'inline scripts removed');
|
||||
assert.equal(vulcanizedJS, 'Polymer(\'my-element\');', 'csp element script');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('exclude', function(done) {
|
||||
|
||||
var i = 3;
|
||||
function reallyDone() {
|
||||
if (--i === 0) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
process({input: inputPath, output: outputPath, excludes: {imports: ['simple-import']}}, function(outputs) {
|
||||
var vulcanized = outputs[outputPath];
|
||||
assert(vulcanized);
|
||||
var $ = require('whacko').load(vulcanized);
|
||||
assert.equal($('head > link[href="imports/simple-import.html"]').length, 0, 'import excluded');
|
||||
assert.equal($('head > link[rel="stylesheet"][href="imports/simple-style.css"]').length, 0, 'import content excluded');
|
||||
assert.equal($('head > link[href="http://example.com/foo/bar.html"]').length, 1, 'external import is not excluded');
|
||||
reallyDone();
|
||||
});
|
||||
|
||||
process({input: inputPath, output: outputPath, excludes: {styles: ['simple-style']}}, function(outputs) {
|
||||
var vulcanized = outputs[outputPath];
|
||||
assert(vulcanized);
|
||||
var $ = require('whacko').load(vulcanized);
|
||||
assert.equal($('polymer-element[name="my-element"] > template > link[href="imports/simple-style.css"]').length, 1, 'style excluded');
|
||||
reallyDone();
|
||||
});
|
||||
|
||||
process({input: inputPath, output: outputPath, excludes: {imports: ['simple-import']}, 'strip-excludes': false}, function(outputs) {
|
||||
var vulcanized = outputs[outputPath];
|
||||
assert(vulcanized);
|
||||
var $ = require('whacko').load(vulcanized);
|
||||
assert.equal($('link[href="imports/simple-import.html"]').length, 1, 'excluded import not stripped');
|
||||
reallyDone();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
23
components/vulcanize/util/changelogs.sh
Normal file
23
components/vulcanize/util/changelogs.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# @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
|
||||
#
|
||||
|
||||
# tags sorted semver style
|
||||
TAGS=($(git tag -l | sort -k1,1r -k2,2r -k3,3r -t.))
|
||||
|
||||
TO=(${TAGS[@]})
|
||||
|
||||
FROM=(${TAGS[@]:1})
|
||||
FROM+=(`git rev-list --max-parents=0 HEAD`)
|
||||
|
||||
for i in ${!FROM[@]}; do
|
||||
echo "### ${TO[$i]}"
|
||||
git log ${FROM[$i]}..${TO[$i]} --pretty="- %s"
|
||||
done
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user