Skip to content
This repository has been archived by the owner on Sep 14, 2021. It is now read-only.

Auto style scoping and class scoping #25

Merged
merged 12 commits into from
Nov 9, 2016
Merged
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ addons:
before_script:
- npm install -g bower
- bower install
- gulp test-modules
script:
- xvfb-run wct
- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
Expand Down
135 changes: 133 additions & 2 deletions shadycss.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion shadycss.min.js.map

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions src/ShadyCSS.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import StyleCache from './style-cache'

// TODO(dfreedm): consider spliting into separate global
import ApplyShim from './apply-shim'
import {flush} from './document-watcher'

let styleCache = new StyleCache();

export let ShadyCSS = {
flush: flush,
scopeCounter: {},
nativeShadow: nativeShadow,
nativeCss: nativeCssVariables,
Expand Down Expand Up @@ -305,6 +307,36 @@ export let ShadyCSS = {
// trim whitespace that can come after the `:` in css
// example: padding: 2px -> " 2px"
return value.trim();
},
// given an element and a classString, replaces
// the element's class with the provided classString and adds
// any necessary ShadyCSS static and property based scoping selectors
// NOTE: this method is suitable to be called in an environment in which
// setAttribute('class', ...) and className setter have been overridden so
// it cannot rely on those methods.
setElementClass(element, classString) {
// use classList to clear existing classes
while (element.classList.length) {
element.classList.remove(element.classList[0]);
}
// add user classString
element.classList.add(...classString.split(' '));
// add static scoping: scope by shadyRoot
let root = element.getRootNode();
if (root.host) {
element.classList.add(StyleTransformer.SCOPE_NAME, root.host.localName);
}
// add property scoping: scope by special selector
if (!this.nativeCss) {
let styleInfo = StyleInfo.get(element);
if (styleInfo && styleInfo.scopeSelector) {
element.classList.add(StyleProperties.XSCOPE_NAME,
styleInfo.scopeSelector);
}
}
},
_styleInfoForNode(node) {
return StyleInfo.get(node);
}
}

Expand Down
74 changes: 74 additions & 0 deletions src/document-watcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
@license
Copyright (c) 2016 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
*/

'use strict';

import {nativeShadow} from './style-settings'
import {StyleTransformer} from './style-transformer'

export let flush = function() {};

if (!nativeShadow) {
let handler = (mxns) => {
for (let x=0; x < mxns.length; x++) {
let mxn = mxns[x];
for (let i=0; i < mxn.addedNodes.length; i++) {
let n = mxn.addedNodes[i];
if (n.nodeType === Node.ELEMENT_NODE &&
!n.classList.contains(StyleTransformer.SCOPE_NAME)) {
let root = n.getRootNode();
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
// may no longer be in a shadowroot
let host = root.host;
if (host) {
let scope = host.is || host.localName;
StyleTransformer.dom(n, scope);
}
}
}
}
for (let i=0; i < mxn.removedNodes.length; i++) {
let n = mxn.removedNodes[i];
if (n.nodeType === Node.ELEMENT_NODE) {
let classIdx = Array.from(n.classList)
.indexOf(StyleTransformer.SCOPE_NAME);
if (classIdx >= 0) {
// NOTE: relies on the scoping class always being adjacent to the
// SCOPE_NAME class.
let scope = n.classList[classIdx + 1];
if (scope) {
StyleTransformer.dom(n, scope, true);
}
}
}
}
}
};

let observer = new MutationObserver(handler);
const startState = 'interactive';

let start = () => observer.observe(document.body, {childList: true, subtree: true});
if (window.HTMLImports) {
window.HTMLImports.whenReady(start);
} else if (document.readyState === startState) {
requestAnimationFrame(start);
} else {
document.addEventListener('readystatechange', function() {
if (document.readyState === startState) {
start();
}
});
}

flush = function() {
handler(observer.takeRecords());
}
}
6 changes: 4 additions & 2 deletions src/style-transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {nativeShadow} from './style-settings'
* :host(:dir[rtl]) -> scopeName:dir(rtl) -> [dir="rtl"] scopeName, scopeName[dir="rtl"]

*/
let SCOPE_NAME = 'style-scope';

export let StyleTransformer = {

// Given a node and scope name, add a scoping class to each node
Expand Down Expand Up @@ -262,10 +264,10 @@ export let StyleTransformer = {
return selector.match(SLOTTED) ?
this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR) :
this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR);
}
},
SCOPE_NAME: SCOPE_NAME
};

let SCOPE_NAME = 'style-scope';
let SCOPE_DOC_SELECTOR = `:not(.${SCOPE_NAME})`;
let COMPLEX_SELECTOR_SEP = ',';
let SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=\[])+)/g;
Expand Down
3 changes: 2 additions & 1 deletion tests/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
<script>
var suites = [
'css-parse.html',
'apply-shim.html'
'apply-shim.html',
'scoping.html'
];
WCT.loadSuites(suites);
</script>
Loading