yjs/bower_components/core-style/core-style.html
2015-01-03 03:45:15 +00:00

388 lines
11 KiB
HTML

<!--
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
-->
<!--
The `core-style` element helps manage styling inside other elements and can
be used to make themes. The `core-style` element can be either a producer
or consumer of styling. If it has its `id` property set, it's a producer.
Elements that are producers should include css styling as their text content.
If a `core-style` has its `ref` property set, it's a consumer. A `core-style`
typically sets its `ref` property to the value of the `id` property of the
`core-style` it wants to use. This allows a single producer to be used in
multiple places, for example, in many different elements.
It's common to place `core-style` producer elements inside HTMLImports.
Remote stylesheets should be included this way, the &#64;import css mechanism is
not currently supported.
Here's a basic example:
<polymer-element name="x-test" noscript>
<template>
<core-style ref="x-test"></core-style>
<content></content>
</template>
</polymer-element>
The `x-test` element above will be styled by any `core-style` elements that have
`id` set to `x-test`. These `core-style` producers are separate from the element
definition, allowing a user of the element to style it independent of the author's
styling. For example:
<core-style id="x-test">
:host {
backgound-color: steelblue;
}
</core-style>
The content of the `x-test` `core-style` producer gets included inside the
shadowRoot of the `x-test` element. If the content of the `x-test` producer
`core-style` changes, all consumers of it are automatically kept in sync. This
allows updating styling on the fly.
The `core-style` element also supports bindings, in which case the producer
`core-style` element is the model. Here's an example:
<core-style id="x-test">
:host {
background-color: {{myColor}};
}
</core-style>
<script>
document._currentScript.ownerDocument.getElementById('x-test').myColor = 'orange';
</script>
Finally, to facilitate sharing data between `core-style` elements, all
`core-style` elements have a `g` property which is set to the global
`CoreStyle.g`. Here's an example:
<core-style id="x-test">
:host {
background-color: {{g.myColor}};
}
</core-style>
<script>
CoreStyle.g.myColor = 'tomato';
</script>
Finally, one `core-style` can be nested inside another. The `core-style`
element has a `list` property which is a map of all the `core-style` producers.
A `core-style` producer's content is available via its `cssText` property.
Putting this together:
<core-style id="common">
:host {
font-family: sans-serif;
}
</core-style>
<core-style id="x-test">
{{list.common.cssText}}
:host {
background-color: {{g.myColor}};
}
</core-style>
@group Polymer Core Elements
@element core-style
@homepage github.io
-->
<link rel="import" href="../polymer/polymer.html">
<polymer-element name="core-style" hidden>
<script>
(function() {
window.CoreStyle = window.CoreStyle || {
g: {},
list: {},
refMap: {}
};
Polymer('core-style', {
/**
* The `id` property should be set if the `core-style` is a producer
* of styles. In this case, the `core-style` should have text content
* that is cssText.
*
* @attribute id
* @type string
* @default ''
*/
publish: {
/**
* The `ref` property should be set if the `core-style` element is a
* consumer of styles. Set it to the `id` of the desired `core-style`
* element.
*
* @attribute ref
* @type string
* @default ''
*/
ref: ''
},
// static
g: CoreStyle.g,
refMap: CoreStyle.refMap,
/**
* The `list` is a map of all `core-style` producers stored by `id`. It
* should be considered readonly. It's useful for nesting one `core-style`
* inside another.
*
* @attribute list
* @type object (readonly)
* @default {map of all `core-style` producers}
*/
list: CoreStyle.list,
// if we have an id, we provide style
// if we have a ref, we consume/require style
ready: function() {
if (this.id) {
this.provide();
} else {
this.registerRef(this.ref);
if (!window.ShadowDOMPolyfill) {
this.require();
}
}
},
// can't shim until attached if using SD polyfill because need to find host
attached: function() {
if (!this.id && window.ShadowDOMPolyfill) {
this.require();
}
},
/****** producer stuff *******/
provide: function() {
this.register();
// we want to do this asap, especially so we can do so before definitions
// that use this core-style are registered.
if (this.textContent) {
this._completeProvide();
} else {
this.async(this._completeProvide);
}
},
register: function() {
var i = this.list[this.id];
if (i) {
if (!Array.isArray(i)) {
this.list[this.id] = [i];
}
this.list[this.id].push(this);
} else {
this.list[this.id] = this;
}
},
// stamp into a shadowRoot so we can monitor dom of the bound output
_completeProvide: function() {
this.createShadowRoot();
this.domObserver = new MutationObserver(this.domModified.bind(this))
.observe(this.shadowRoot, {subtree: true,
characterData: true, childList: true});
this.provideContent();
},
provideContent: function() {
this.ensureTemplate();
this.shadowRoot.textContent = '';
this.shadowRoot.appendChild(this.instanceTemplate(this.template));
this.cssText = this.shadowRoot.textContent;
},
ensureTemplate: function() {
if (!this.template) {
this.template = this.querySelector('template:not([repeat]):not([bind])');
// move content into the template
if (!this.template) {
this.template = document.createElement('template');
var n = this.firstChild;
while (n) {
this.template.content.appendChild(n.cloneNode(true));
n = n.nextSibling;
}
}
}
},
domModified: function() {
this.cssText = this.shadowRoot.textContent;
this.notify();
},
// notify instances that reference this element
notify: function() {
var s$ = this.refMap[this.id];
if (s$) {
for (var i=0, s; (s=s$[i]); i++) {
s.require();
}
}
},
/****** consumer stuff *******/
registerRef: function(ref) {
//console.log('register', ref);
this.refMap[this.ref] = this.refMap[this.ref] || [];
this.refMap[this.ref].push(this);
},
applyRef: function(ref) {
this.ref = ref;
this.registerRef(this.ref);
this.require();
},
require: function() {
var cssText = this.cssTextForRef(this.ref);
//console.log('require', this.ref, cssText);
if (cssText) {
this.ensureStyleElement();
// do nothing if cssText has not changed
if (this.styleElement._cssText === cssText) {
return;
}
this.styleElement._cssText = cssText;
if (window.ShadowDOMPolyfill) {
this.styleElement.textContent = cssText;
cssText = WebComponents.ShadowCSS.shimStyle(this.styleElement,
this.getScopeSelector());
}
this.styleElement.textContent = cssText;
}
},
cssTextForRef: function(ref) {
var s$ = this.byId(ref);
var cssText = '';
if (s$) {
if (Array.isArray(s$)) {
var p = [];
for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
p.push(s.cssText);
}
cssText = p.join('\n\n');
} else {
cssText = s$.cssText;
}
}
if (s$ && !cssText) {
console.warn('No styles provided for ref:', ref);
}
return cssText;
},
byId: function(id) {
return this.list[id];
},
ensureStyleElement: function() {
if (!this.styleElement) {
this.styleElement = window.ShadowDOMPolyfill ?
this.makeShimStyle() :
this.makeRootStyle();
}
if (!this.styleElement) {
console.warn(this.localName, 'could not setup style.');
}
},
makeRootStyle: function() {
var style = document.createElement('style');
this.appendChild(style);
return style;
},
makeShimStyle: function() {
var host = this.findHost(this);
if (host) {
var name = host.localName;
var style = document.querySelector('style[' + name + '=' + this.ref +']');
if (!style) {
style = document.createElement('style');
style.setAttribute(name, this.ref);
document.head.appendChild(style);
}
return style;
}
},
getScopeSelector: function() {
if (!this._scopeSelector) {
var selector = '', host = this.findHost(this);
if (host) {
var typeExtension = host.hasAttribute('is');
var name = typeExtension ? host.getAttribute('is') : host.localName;
selector = WebComponents.ShadowCSS.makeScopeSelector(name,
typeExtension);
}
this._scopeSelector = selector;
}
return this._scopeSelector;
},
findHost: function(node) {
while (node.parentNode) {
node = node.parentNode;
}
return node.host || wrap(document.documentElement);
},
/* filters! */
// TODO(dfreedm): add more filters!
cycle: function(rgb, amount) {
if (rgb.match('#')) {
var o = this.hexToRgb(rgb);
if (!o) {
return rgb;
}
rgb = 'rgb(' + o.r + ',' + o.b + ',' + o.g + ')';
}
function cycleChannel(v) {
return Math.abs((Number(v) - amount) % 255);
}
return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) {
return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', '
+ cycleChannel(c) + ')';
});
},
hexToRgb: function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
});
})();
</script>
</polymer-element>