388 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			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 @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>
 |