Last active
December 19, 2017 21:43
-
-
Save semanticpixel/32fdfc97e4c2a9fed033c15b4f4a5757 to your computer and use it in GitHub Desktop.
ember-styled-components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
//import styled from '../styled'; | |
/* | |
export default styled.h1` | |
color: #333; | |
font-size: 30px; | |
`; | |
*/ | |
export default Ember.Component.extend({ | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TODO |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import interleave from '../utils/interleave' | |
import flatten from '../utils/flatten' | |
export default (rules, ...interpolations) => ( | |
flatten(interleave(rules, interpolations)) | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import css from './css' | |
import GlobalStyle from '../models/GlobalStyle' | |
const injectGlobal = (strings, ...interpolations) => { | |
const globalStyle = new GlobalStyle(css(strings, ...interpolations)) | |
globalStyle.generateAndInject() | |
} | |
export default injectGlobal |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TODO |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import css from './css' | |
import domElements from '../utils/domElements' | |
export default (createStyledComponent) => { | |
const styled = (tagName, props = {}) => ( | |
(cssRules, ...interpolations) => ( | |
createStyledComponent(tagName, css(cssRules, ...interpolations), props) | |
) | |
) | |
domElements.forEach(domElement => { | |
styled[domElement] = styled(domElement) | |
}) | |
return styled | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Controller.extend({ | |
appName: 'Ember Twiddle' | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hashStr from '../vendor/glamor/hash' | |
import stylis from '../vendor/stylis' | |
import flatten from '../utils/flatten' | |
import styleSheet from './StyleSheet' | |
export default (nameGenerator) => { | |
const inserted = {} | |
class ComponentStyle { | |
constructor(rules) { | |
this.rules = rules | |
if (!styleSheet.injected) { | |
styleSheet.inject() | |
} | |
this.insertedRule = styleSheet.insert('') | |
} | |
/* | |
* Flattens a rule set into valid CSS | |
* Hashes it, wraps the whole chunk in a ._hashName {} | |
* Parses that with PostCSS then runs PostCSS-Nested on it | |
* Returns the hash to be injected on render() | |
* */ | |
generateAndInjectStyles (executionContext) { | |
const flatCSS = flatten(this.rules, executionContext).join('') | |
.replace(/^\s*\/\/.*$/gm, '') // replace JS comments | |
const hash = hashStr(flatCSS) | |
if (!inserted[hash]) { | |
const selector = nameGenerator(hash) | |
inserted[hash] = selector | |
const css = stylis(`.${selector}`, flatCSS, false, false) | |
this.insertedRule.appendRule(css) | |
} | |
return inserted[hash] | |
} | |
} | |
return ComponentStyle | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import flatten from '../utils/flatten' | |
import styleSheet from './StyleSheet' | |
import stylis from '../vendor/stylis' | |
export default class ComponentStyle { | |
constructor (rules, selector) { | |
this.rules = rules | |
this.selector = selector | |
} | |
generateAndInject () { | |
if (!styleSheet.injected) styleSheet.inject() | |
const flatCSS = flatten(this.rules).join('') | |
const cssString = this.selector ? `${this.selector} { ${flatCSS} }` : flatCSS | |
const css = stylis('', cssString, false, false) | |
styleSheet.insert(css, {global: true}) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is the file that is important!!! | |
import Ember from 'ember' | |
export default (ComponentStyle) => { | |
const createStyledComponent = (target, rules, props) => { | |
const isTag = target && typeof target !== 'string'; | |
const prevProps = isTag ? (typeof target === 'object' ? target.props : (typeof target === 'function' ? target.options.props : {})) : {} | |
const mergedProps = Object.assign({}, prevProps, props) | |
const componentStyle = new ComponentStyle(rules) | |
if (isTag) { | |
return Ember.Component.extend({ | |
tagName: target, | |
classNameBindings: ['_generatedClassName'], | |
//attributeBindings: ['_componentStyle:style'], | |
//_componentStyle: styles, | |
_generatedClassName: 'Hello', | |
generatedClassName() { | |
const componentProps = Object.assign({}, {}) | |
return this.generateAndInjectStyles(componentProps) | |
}, | |
generateAndInjectStyles (componentProps) { | |
return componentStyle.generateAndInjectStyles(componentProps) | |
} | |
}); | |
} else { | |
return target.extend({ | |
classNameBindings: ['_generatedClassName'], | |
//attributeBindings: ['_componentStyle:style'], | |
//_componentStyle: styles, | |
_generatedClassName: 'Hello', | |
generatedClassName() { | |
const componentProps = Object.assign({}, {}) | |
return this.generateAndInjectStyles(componentProps) | |
}, | |
generateAndInjectStyles (componentProps) { | |
return componentStyle.generateAndInjectStyles(componentProps) | |
} | |
}); | |
} | |
/* | |
const StyledComponent = { | |
props: mergedProps, | |
render: function (createElement) { | |
return createElement( | |
target, | |
{ | |
class: [this.generatedClassName], | |
props: this.$props | |
}, | |
this.$slots.default | |
) | |
}, | |
methods: { | |
generateAndInjectStyles (componentProps) { | |
return componentStyle.generateAndInjectStyles(componentProps) | |
} | |
}, | |
computed: { | |
generatedClassName () { | |
const componentProps = Object.assign({}, this.$props) | |
return this.generateAndInjectStyles(componentProps) | |
} | |
} | |
} | |
return StyledComponent | |
*/ | |
} | |
return createStyledComponent | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Wraps glamor's stylesheet and exports a singleton for styled components | |
to use. */ | |
import { StyleSheet as GlamorSheet } from '../vendor/glamor/sheet' | |
class StyleSheet { | |
constructor () { | |
/* Don't specify a maxLength for the global sheet, since these rules | |
* are defined at initialization and should remain static after that */ | |
this.globalStyleSheet = new GlamorSheet({ speedy: false }) | |
this.componentStyleSheet = new GlamorSheet({ speedy: false, maxLength: 40 }) | |
} | |
get injected () { | |
return this.globalStyleSheet.injected && this.componentStyleSheet.injected | |
} | |
inject () { | |
this.globalStyleSheet.inject() | |
this.componentStyleSheet.inject() | |
} | |
flush () { | |
if (this.globalStyleSheet.sheet) this.globalStyleSheet.flush() | |
if (this.componentStyleSheet.sheet) this.componentStyleSheet.flush() | |
} | |
insert (rule, opts = { global: false }) { | |
const sheet = opts.global ? this.globalStyleSheet : this.componentStyleSheet | |
return sheet.insert(rule) | |
} | |
rules () { | |
return this.globalStyleSheet.rules().concat(this.componentStyleSheet.rules()) | |
} | |
} | |
/* Export stylesheet as a singleton class */ | |
export default new StyleSheet() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import generateAlphabeticName from './utils/generateAlphabeticName' | |
import css from './constructors/css' | |
import injectGlobal from './constructors/injectGlobal' | |
import _styledComponent from './models/StyledComponent' | |
import _componentStyle from './models/ComponentStyle' | |
import _styled from './constructors/styled' | |
const styled = _styled(_styledComponent(_componentStyle(generateAlphabeticName))) | |
export default styled | |
export { css, injectGlobal } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.12.1", | |
"EmberENV": { | |
"FEATURES": {} | |
}, | |
"options": { | |
"use_pods": false, | |
"enable-testing": false | |
}, | |
"dependencies": { | |
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js", | |
"ember": "2.12.0", | |
"ember-template-compiler": "2.12.0", | |
"ember-testing": "2.12.0" | |
}, | |
"addons": { | |
"ember-data": "2.12.1" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Handy list of valid HTML tags | |
* | |
*/ | |
export default [ | |
'a', | |
'abbr', | |
'address', | |
'area', | |
'article', | |
'aside', | |
'audio', | |
'b', | |
'base', | |
'bdi', | |
'bdo', | |
'big', | |
'blockquote', | |
'body', | |
'br', | |
'button', | |
'canvas', | |
'caption', | |
'cite', | |
'code', | |
'col', | |
'colgroup', | |
'data', | |
'datalist', | |
'dd', | |
'del', | |
'details', | |
'dfn', | |
'dialog', | |
'div', | |
'dl', | |
'dt', | |
'em', | |
'embed', | |
'fieldset', | |
'figcaption', | |
'figure', | |
'footer', | |
'form', | |
'h1', | |
'h2', | |
'h3', | |
'h4', | |
'h5', | |
'h6', | |
'head', | |
'header', | |
'hgroup', | |
'hr', | |
'html', | |
'i', | |
'iframe', | |
'img', | |
'input', | |
'ins', | |
'kbd', | |
'keygen', | |
'label', | |
'legend', | |
'li', | |
'link', | |
'main', | |
'map', | |
'mark', | |
'menu', | |
'menuitem', | |
'meta', | |
'meter', | |
'nav', | |
'noscript', | |
'object', | |
'ol', | |
'optgroup', | |
'option', | |
'output', | |
'p', | |
'param', | |
'picture', | |
'pre', | |
'progress', | |
'q', | |
'rp', | |
'rt', | |
'ruby', | |
's', | |
'samp', | |
'script', | |
'section', | |
'select', | |
'small', | |
'source', | |
'span', | |
'strong', | |
'style', | |
'sub', | |
'summary', | |
'sup', | |
'table', | |
'tbody', | |
'td', | |
'textarea', | |
'tfoot', | |
'th', | |
'thead', | |
'time', | |
'title', | |
'tr', | |
'track', | |
'u', | |
'ul', | |
'var', | |
'video', | |
'wbr', | |
// SVG | |
'circle', | |
'clipPath', | |
'defs', | |
'ellipse', | |
'g', | |
'image', | |
'line', | |
'linearGradient', | |
'mask', | |
'path', | |
'pattern', | |
'polygon', | |
'polyline', | |
'radialGradient', | |
'rect', | |
'stop', | |
'svg', | |
'text', | |
'tspan' | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hyphenate from '../vendor/hyphenateStyleName' | |
import isPlainObject from '../vendor/isPlainObject' | |
export const objToCss = (obj, prevKey) => { | |
const css = Object.keys(obj).map(key => { | |
if (isPlainObject(obj[key])) return objToCss(obj[key], key) | |
return `${hyphenate(key)}: ${obj[key]};` | |
}).join(' ') | |
return prevKey ? `${prevKey} { | |
${css} | |
}` : css | |
} | |
const flatten = (chunks, executionContext) => ( | |
chunks.reduce((ruleSet, chunk) => { | |
/* Remove falsey values */ | |
if (chunk === undefined || chunk === null || chunk === false || chunk === '') return ruleSet | |
/* Flatten ruleSet */ | |
if (Array.isArray(chunk)) return [...ruleSet, ...flatten(chunk, executionContext)] | |
/* Either execute or defer the function */ | |
if (typeof chunk === 'function') { | |
return executionContext | |
? ruleSet.concat(...flatten([chunk(executionContext)], executionContext)) | |
: ruleSet.concat(chunk) | |
} | |
/* Handle objects */ | |
// $FlowFixMe have to add %checks somehow to isPlainObject | |
return ruleSet.concat(isPlainObject(chunk) ? objToCss(chunk) : chunk.toString()) | |
}, []) | |
) | |
export default flatten |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') | |
/* Some high number, usually 9-digit base-10. Map it to base-😎 */ | |
const generateAlphabeticName = (code) => { | |
const lastDigit = chars[code % chars.length] | |
return code > chars.length | |
? `${generateAlphabeticName(Math.floor(code / chars.length))}${lastDigit}` | |
: lastDigit | |
} | |
export default generateAlphabeticName |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default ( | |
strings, | |
interpolations, | |
) => ( | |
interpolations.reduce((array, interp, i) => ( | |
array.concat(interp, strings[i + 1]) | |
), [strings[0]]) | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const objectProto = Object.prototype | |
const hasOwnProperty = objectProto.hasOwnProperty | |
const toString = objectProto.toString | |
const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined | |
/** | |
* The base implementation of `getTag` without fallbacks for buggy environments. | |
* | |
* @private | |
* @param {*} value The value to query. | |
* @returns {string} Returns the `toStringTag`. | |
*/ | |
function baseGetTag(value) { | |
if (value == null) { | |
return value === undefined ? '[object Undefined]' : '[object Null]' | |
} | |
if (!(symToStringTag && symToStringTag in Object(value))) { | |
return toString.call(value) | |
} | |
const isOwn = hasOwnProperty.call(value, symToStringTag) | |
const tag = value[symToStringTag] | |
let unmasked = false | |
try { | |
value[symToStringTag] = undefined | |
unmasked = true | |
} catch (e) {} | |
const result = toString.call(value) | |
if (unmasked) { | |
if (isOwn) { | |
value[symToStringTag] = tag | |
} else { | |
delete value[symToStringTag] | |
} | |
} | |
return result | |
} | |
export default baseGetTag |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// murmurhash2 via https://gist.github.com/raycmorgan/588423 | |
export default function doHash(str, seed) { | |
var m = 0x5bd1e995; | |
var r = 24; | |
var h = seed ^ str.length; | |
var length = str.length; | |
var currentIndex = 0; | |
while (length >= 4) { | |
var k = UInt32(str, currentIndex); | |
k = Umul32(k, m); | |
k ^= k >>> r; | |
k = Umul32(k, m); | |
h = Umul32(h, m); | |
h ^= k; | |
currentIndex += 4; | |
length -= 4; | |
} | |
switch (length) { | |
case 3: | |
h ^= UInt16(str, currentIndex); | |
h ^= str.charCodeAt(currentIndex + 2) << 16; | |
h = Umul32(h, m); | |
break; | |
case 2: | |
h ^= UInt16(str, currentIndex); | |
h = Umul32(h, m); | |
break; | |
case 1: | |
h ^= str.charCodeAt(currentIndex); | |
h = Umul32(h, m); | |
break; | |
} | |
h ^= h >>> 13; | |
h = Umul32(h, m); | |
h ^= h >>> 15; | |
return h >>> 0; | |
} | |
function UInt32(str, pos) { | |
return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8) + (str.charCodeAt(pos++) << 16) + (str.charCodeAt(pos) << 24); | |
} | |
function UInt16(str, pos) { | |
return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8); | |
} | |
function Umul32(n, m) { | |
n = n | 0; | |
m = m | 0; | |
var nlo = n & 0xffff; | |
var nhi = n >>> 16; | |
var res = nlo * m + ((nhi * m & 0xffff) << 16) | 0; | |
return res; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
high performance StyleSheet for css-in-js systems | |
- uses multiple style tags behind the scenes for millions of rules | |
- uses `insertRule` for appending in production for *much* faster performance | |
- 'polyfills' on server side | |
// usage | |
import StyleSheet from 'glamor/lib/sheet' | |
let styleSheet = new StyleSheet() | |
styleSheet.inject() | |
- 'injects' the stylesheet into the page (or into memory if on server) | |
styleSheet.insert('#box { border: 1px solid red; }') | |
- appends a css rule into the stylesheet | |
styleSheet.flush() | |
- empties the stylesheet of all its contents | |
*/ | |
function last(arr) { | |
return arr[arr.length -1] | |
} | |
function sheetForTag(tag) { | |
for(let i = 0; i < document.styleSheets.length; i++) { | |
if(document.styleSheets[i].ownerNode === tag) { | |
return document.styleSheets[i] | |
} | |
} | |
} | |
const isBrowser = typeof document !== 'undefined' | |
const isDev = (x => (x === 'development') || !x)(process.env.NODE_ENV) | |
const isTest = process.env.NODE_ENV === 'test' | |
const oldIE = (() => { | |
if(isBrowser) { | |
let div = document.createElement('div') | |
div.innerHTML = '<!--[if lt IE 10]><i></i><![endif]-->' | |
return div.getElementsByTagName('i').length === 1 | |
} | |
})() | |
function makeStyleTag() { | |
let tag = document.createElement('style') | |
tag.type = 'text/css' | |
tag.appendChild(document.createTextNode('')); | |
(document.head || document.getElementsByTagName('head')[0]).appendChild(tag) | |
return tag | |
} | |
export class StyleSheet { | |
constructor({ | |
speedy = !isDev && !isTest, | |
maxLength = (isBrowser && oldIE) ? 4000 : 65000 | |
} = {}) { | |
this.isSpeedy = speedy // the big drawback here is that the css won't be editable in devtools | |
this.sheet = undefined | |
this.tags = [] | |
this.maxLength = maxLength | |
this.ctr = 0 | |
} | |
inject() { | |
if(this.injected) { | |
throw new Error('already injected stylesheet!') | |
} | |
if(isBrowser) { | |
// this section is just weird alchemy I found online off many sources | |
this.tags[0] = makeStyleTag() | |
// this weirdness brought to you by firefox | |
this.sheet = sheetForTag(this.tags[0]) | |
} | |
else { | |
// server side 'polyfill'. just enough behavior to be useful. | |
this.sheet = { | |
cssRules: [], | |
insertRule: rule => { | |
// enough 'spec compliance' to be able to extract the rules later | |
// in other words, just the cssText field | |
const serverRule = { cssText: rule } | |
this.sheet.cssRules.push(serverRule) | |
return {serverRule, appendRule: (newCss => serverRule.cssText += newCss)} | |
} | |
} | |
} | |
this.injected = true | |
} | |
speedy(bool) { | |
if(this.ctr !== 0) { | |
throw new Error(`cannot change speedy mode after inserting any rule to sheet. Either call speedy(${bool}) earlier in your app, or call flush() before speedy(${bool})`) | |
} | |
this.isSpeedy = !!bool | |
} | |
_insert(rule) { | |
// this weirdness for perf, and chrome's weird bug | |
// https://stackoverflow.com/questions/20007992/chrome-suddenly-stopped-accepting-insertrule | |
try { | |
this.sheet.insertRule(rule, this.sheet.cssRules.length) // todo - correct index here | |
} | |
catch(e) { | |
if(isDev) { | |
// might need beter dx for this | |
console.warn('whoops, illegal rule inserted', rule) //eslint-disable-line no-console | |
} | |
} | |
} | |
insert(rule) { | |
let insertedRule | |
if(isBrowser) { | |
// this is the ultrafast version, works across browsers | |
if(this.isSpeedy && this.sheet.insertRule) { | |
this._insert(rule) | |
} | |
else{ | |
const textNode = document.createTextNode(rule) | |
last(this.tags).appendChild(textNode) | |
insertedRule = { textNode, appendRule: newCss => textNode.appendData(newCss)} | |
if(!this.isSpeedy) { | |
// sighhh | |
this.sheet = sheetForTag(last(this.tags)) | |
} | |
} | |
} | |
else{ | |
// server side is pretty simple | |
insertedRule = this.sheet.insertRule(rule) | |
} | |
this.ctr++ | |
if(isBrowser && this.ctr % this.maxLength === 0) { | |
this.tags.push(makeStyleTag()) | |
this.sheet = sheetForTag(last(this.tags)) | |
} | |
return insertedRule | |
} | |
flush() { | |
if(isBrowser) { | |
this.tags.forEach(tag => tag.parentNode.removeChild(tag)) | |
this.tags = [] | |
this.sheet = null | |
this.ctr = 0 | |
// todo - look for remnants in document.styleSheets | |
} | |
else { | |
// simpler on server | |
this.sheet.cssRules = [] | |
} | |
this.injected = false | |
} | |
rules() { | |
if(!isBrowser) { | |
return this.sheet.cssRules | |
} | |
let arr = [] | |
this.tags.forEach(tag => arr.splice(arr.length, 0, ...Array.from( | |
sheetForTag(tag).cssRules | |
))) | |
return arr | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const uppercasePattern = /[A-Z]/g | |
const msPattern = /^ms-/ | |
const cache = {} | |
export default (string) => ( | |
string in cache | |
? cache[string] | |
: cache[string] = string | |
.replace(uppercasePattern, '-$&') | |
.toLowerCase() | |
.replace(msPattern, '-ms-') | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default (value) => typeof value == 'object' && value !== null |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import baseGetTag from './baseGetTag.js' | |
import isObjectLike from './isObjectLike.js' | |
function isPlainObject(value) { | |
if (!isObjectLike(value) || baseGetTag(value) != '[object Object]') { | |
return false; | |
} | |
if (Object.getPrototypeOf(value) === null) { | |
return true; | |
} | |
let proto = value; | |
while (Object.getPrototypeOf(proto) !== null) { | |
proto = Object.getPrototypeOf(proto) | |
} | |
return Object.getPrototypeOf(value) === proto | |
} | |
export default isPlainObject |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* __ ___ | |
* _____/ /___ __/ (_)____ | |
* / ___/ __/ / / / / / ___/ | |
* (__ ) /_/ /_/ / / (__ ) | |
* /____/\__/\__, /_/_/____/ | |
* /____/ | |
* | |
* light - weight css preprocessor @licence MIT | |
*/ | |
(function (factory) {/* eslint-disable */ | |
typeof exports === 'object' && typeof module !== 'undefined' ? (module['exports'] = factory(null)) : | |
typeof define === 'function' && define['amd'] ? define(factory(null)) : | |
(window['stylis'] = factory(null)) | |
}(/** @param {*=} options */function factory (options) {/* eslint-disable */ | |
'use strict' | |
/** | |
* Notes | |
* | |
* The ['<method name>'] pattern is used to support closure compiler | |
* the jsdoc signatures are also used to the same effect | |
* | |
* ---- | |
* | |
* int + int + int === n4 [faster] | |
* | |
* vs | |
* | |
* int === n1 && int === n2 && int === n3 | |
* | |
* ---- | |
* | |
* switch (int) { case ints...} [faster] | |
* | |
* vs | |
* | |
* if (int == 1 && int === 2 ...) | |
* | |
* ---- | |
* | |
* The (first*n1 + second*n2 + third*n3) format used in the property parser | |
* is a simple way to hash the sequence of characters | |
* taking into account the index they occur in | |
* since any number of 3 character sequences could produce duplicates. | |
* | |
* On the other hand sequences that are directly tied to the index of the character | |
* resolve a far more accurate measure, it's also faster | |
* to evaluate one condition in a switch statement | |
* than three in an if statement regardless of the added math. | |
* | |
* This allows the vendor prefixer to be both small and fast. | |
*/ | |
var nullptn = /^\0+/g /* matches leading null characters */ | |
var formatptn = /[\0\r\f]/g /* matches new line, null and formfeed characters */ | |
var colonptn = /: */g /* splits animation rules */ | |
var cursorptn = /zoo|gra/ /* assert cursor varient */ | |
var transformptn = /([,: ])(transform)/g /* vendor prefix transform, older webkit */ | |
var animationptn = /,+\s*(?![^(]*[)])/g /* splits multiple shorthand notation animations */ | |
var propertiesptn = / +\s*(?![^(]*[)])/g /* animation properties */ | |
var elementptn = / *[\0] */g /* selector elements */ | |
var selectorptn = /,\r+?/g /* splits selectors */ | |
var andptn = /([\t\r\n ])*\f?&/g /* match & */ | |
var escapeptn = /:global\(((?:[^\(\)\[\]]*|\[.*\]|\([^\(\)]*\))*)\)/g /* matches :global(.*) */ | |
var invalidptn = /\W+/g /* removes invalid characters from keyframes */ | |
var keyframeptn = /@(k\w+)\s*(\S*)\s*/ /* matches @keyframes $1 */ | |
var plcholdrptn = /::(place)/g /* match ::placeholder varient */ | |
var readonlyptn = /:(read-only)/g /* match :read-only varient */ | |
var beforeptn = /\s+(?=[{\];=:>])/g /* matches \s before ] ; = : */ | |
var afterptn = /([[}=:>])\s+/g /* matches \s after characters [ } = : */ | |
var tailptn = /(\{[^{]+?);(?=\})/g /* matches tail semi-colons ;} */ | |
var whiteptn = /\s{2,}/g /* matches repeating whitespace */ | |
var pseudoptn = /([^\(])(:+) */g /* pseudo element */ | |
var writingptn = /[svh]\w+-[tblr]{2}/ /* match writing mode property values */ | |
var gradientptn = /([\w-]+t\()/g /* match *gradient property */ | |
var supportsptn = /\(\s*(.*)\s*\)/g /* match supports (groups) */ | |
var propertyptn = /([^]*?);/g /* match properties leading semicolon */ | |
var selfptn = /-self|flex-/g /* match flex- and -self in align-self: flex-*; */ | |
var pseudofmt = /[^]*?(:[rp][el]a[\w-]+)[^]*/ /* extrats :readonly or :placholder from selector */ | |
var trimptn = /[ \t]+$/ /* match tail whitspace */ | |
/* vendors */ | |
var webkit = '-webkit-' | |
var moz = '-moz-' | |
var ms = '-ms-' | |
/* character codes */ | |
var SEMICOLON = 59 /* ; */ | |
var CLOSEBRACES = 125 /* } */ | |
var OPENBRACES = 123 /* { */ | |
var OPENPARENTHESES = 40 /* ( */ | |
var CLOSEPARENTHESES = 41 /* ) */ | |
var OPENBRACKET = 91 /* [ */ | |
var CLOSEBRACKET = 93 /* ] */ | |
var NEWLINE = 10 /* \n */ | |
var CARRIAGE = 13 /* \r */ | |
var TAB = 9 /* \t */ | |
var AT = 64 /* @ */ | |
var SPACE = 32 /* */ | |
var AND = 38 /* & */ | |
var DASH = 45 /* - */ | |
var UNDERSCORE = 95 /* _ */ | |
var STAR = 42 /* * */ | |
var COMMA = 44 /* , */ | |
var COLON = 58 /* : */ | |
var SINGLEQUOTE = 39 /* ' */ | |
var DOUBLEQUOTE = 34 /* " */ | |
var FOWARDSLASH = 47 /* / */ | |
var GREATERTHAN = 62 /* > */ | |
var PLUS = 43 /* + */ | |
var TILDE = 126 /* ~ */ | |
var NULL = 0 /* \0 */ | |
var FORMFEED = 12 /* \f */ | |
var VERTICALTAB = 11 /* \v */ | |
/* special identifiers */ | |
var KEYFRAME = 107 /* k */ | |
var MEDIA = 109 /* m */ | |
var SUPPORTS = 115 /* s */ | |
var PLACEHOLDER = 112 /* p */ | |
var READONLY = 111 /* o */ | |
var IMPORT = 169 /* <at>i */ | |
var CHARSET = 163 /* <at>c */ | |
var DOCUMENT = 100 /* <at>d */ | |
var PAGE = 112 /* <at>p */ | |
var column = 1 /* current column */ | |
var line = 1 /* current line numebr */ | |
var pattern = 0 /* :pattern */ | |
var cascade = 1 /* #id h1 h2 vs h1#id h2#id */ | |
var prefix = 1 /* vendor prefix */ | |
var escape = 1 /* escape :global() pattern */ | |
var compress = 0 /* compress output */ | |
var semicolon = 0 /* no/semicolon option */ | |
var preserve = 0 /* preserve empty selectors */ | |
/* empty reference */ | |
var array = [] | |
/* plugins */ | |
var plugins = [] | |
var plugged = 0 | |
var should = null | |
/* plugin context */ | |
var POSTS = -2 | |
var PREPS = -1 | |
var UNKWN = 0 | |
var PROPS = 1 | |
var BLCKS = 2 | |
var ATRUL = 3 | |
/* plugin newline context */ | |
var unkwn = 0 | |
/* keyframe animation */ | |
var keyed = 1 | |
var key = '' | |
/* selector namespace */ | |
var nscopealt = '' | |
var nscope = '' | |
/** | |
* Compile | |
* | |
* @param {Array<string>} parent | |
* @param {Array<string>} current | |
* @param {string} body | |
* @param {number} id | |
* @param {number} depth | |
* @return {string} | |
*/ | |
function compile (parent, current, body, id, depth) { | |
var bracket = 0 /* brackets [] */ | |
var comment = 0 /* comments /* // or /* */ | |
var parentheses = 0 /* functions () */ | |
var quote = 0 /* quotes '', "" */ | |
var first = 0 /* first character code */ | |
var second = 0 /* second character code */ | |
var code = 0 /* current character code */ | |
var tail = 0 /* previous character code */ | |
var trail = 0 /* character before previous code */ | |
var peak = 0 /* previous non-whitespace code */ | |
var counter = 0 /* count sequence termination */ | |
var context = 0 /* track current context */ | |
var atrule = 0 /* track @at-rule context */ | |
var pseudo = 0 /* track pseudo token index */ | |
var caret = 0 /* current character index */ | |
var format = 0 /* control character formating context */ | |
var insert = 0 /* auto semicolon insertion */ | |
var invert = 0 /* inverted selector pattern */ | |
var length = 0 /* generic length address */ | |
var eof = body.length /* end of file(length) */ | |
var eol = eof - 1 /* end of file(characters) */ | |
var char = '' /* current character */ | |
var chars = '' /* current buffer of characters */ | |
var child = '' /* next buffer of characters */ | |
var out = '' /* compiled body */ | |
var children = '' /* compiled children */ | |
var flat = '' /* compiled leafs */ | |
var selector /* generic selector address */ | |
var result /* generic address */ | |
// ...build body | |
while (caret < eof) { | |
code = body.charCodeAt(caret) | |
// eof varient | |
if (caret === eol) { | |
// last character + noop context, add synthetic padding for noop context to terminate | |
if (comment + quote + parentheses + bracket !== 0) { | |
if (comment !== 0) { | |
code = comment === FOWARDSLASH ? NEWLINE : FOWARDSLASH | |
} | |
quote = parentheses = bracket = 0 | |
eof++ | |
eol++ | |
} | |
} | |
if (comment + quote + parentheses + bracket === 0) { | |
// eof varient | |
if (caret === eol) { | |
if (format > 0) { | |
chars = chars.replace(formatptn, '') | |
} | |
if (chars.trim().length > 0) { | |
switch (code) { | |
case SPACE: | |
case TAB: | |
case SEMICOLON: | |
case CARRIAGE: | |
case NEWLINE: { | |
break | |
} | |
default: { | |
chars += body.charAt(caret) | |
} | |
} | |
code = SEMICOLON | |
} | |
} | |
// auto semicolon insertion | |
if (insert === 1) { | |
switch (code) { | |
// false flags | |
case OPENBRACES: | |
case CLOSEBRACES: | |
case SEMICOLON: | |
case DOUBLEQUOTE: | |
case SINGLEQUOTE: | |
case OPENPARENTHESES: | |
case CLOSEPARENTHESES: | |
case COMMA: { | |
insert = 0 | |
} | |
// ignore | |
case TAB: | |
case CARRIAGE: | |
case NEWLINE: | |
case SPACE: { | |
break | |
} | |
// valid | |
default: { | |
insert = 0 | |
length = caret | |
first = code | |
caret-- | |
code = SEMICOLON | |
while (length < eof) { | |
switch (body.charCodeAt(++length)) { | |
case NEWLINE: | |
case CARRIAGE: | |
case SEMICOLON: { | |
caret++ | |
code = first | |
} | |
case COLON: | |
case OPENBRACES: { | |
length = eof | |
} | |
} | |
} | |
} | |
} | |
} | |
// token varient | |
switch (code) { | |
case OPENBRACES: { | |
chars = chars.trim() | |
first = chars.charCodeAt(0) | |
counter = 1 | |
length = ++caret | |
while (caret < eof) { | |
code = body.charCodeAt(caret) | |
switch (code) { | |
case OPENBRACES: { | |
counter++ | |
break | |
} | |
case CLOSEBRACES: { | |
counter-- | |
break | |
} | |
} | |
if (counter === 0) { | |
break | |
} | |
caret++ | |
} | |
child = body.substring(length, caret) | |
if (first === NULL) { | |
first = (chars = chars.replace(nullptn, '').trim()).charCodeAt(0) | |
} | |
switch (first) { | |
// @at-rule | |
case AT: { | |
if (format > 0) { | |
chars = chars.replace(formatptn, '') | |
} | |
second = chars.charCodeAt(1) | |
switch (second) { | |
case DOCUMENT: | |
case MEDIA: | |
case SUPPORTS: | |
case DASH: { | |
selector = current | |
break | |
} | |
default: { | |
selector = array | |
} | |
} | |
child = compile(current, selector, child, second, depth+1) | |
length = child.length | |
// preserve empty @at-rule | |
if (preserve > 0 && length === 0) { | |
length = chars.length | |
} | |
// execute plugins, @at-rule context | |
if (plugged > 0) { | |
selector = select(array, chars, invert) | |
result = proxy(ATRUL, child, selector, current, line, column, length, second, depth) | |
chars = selector.join('') | |
if (result !== void 0) { | |
if ((length = (child = result.trim()).length) === 0) { | |
second = 0 | |
child = '' | |
} | |
} | |
} | |
if (length > 0) { | |
switch (second) { | |
case SUPPORTS: { | |
chars = chars.replace(supportsptn, supports) | |
} | |
case DOCUMENT: | |
case MEDIA: | |
case DASH: { | |
child = chars + '{' + child + '}' | |
break | |
} | |
case KEYFRAME: { | |
chars = chars.replace(keyframeptn, '$1 $2' + (keyed > 0 ? key : '')) | |
child = chars + '{' + child + '}' | |
if (prefix === 1 || (prefix === 2 && vendor('@'+child, 3))) { | |
child = '@' + webkit + child + '@' + child | |
} else { | |
child = '@' + child | |
} | |
break | |
} | |
default: { | |
child = chars + child | |
if (id === PAGE) { | |
child = (out += child, '') | |
} | |
} | |
} | |
} else { | |
child = '' | |
} | |
break | |
} | |
// selector | |
default: { | |
child = compile(current, select(current, chars, invert), child, id, depth+1) | |
} | |
} | |
children += child | |
// reset | |
context = 0 | |
insert = 0 | |
pseudo = 0 | |
format = 0 | |
invert = 0 | |
atrule = 0 | |
chars = '' | |
child = '' | |
code = body.charCodeAt(++caret) | |
break | |
} | |
case CLOSEBRACES: | |
case SEMICOLON: { | |
chars = (format > 0 ? chars.replace(formatptn, '') : chars).trim() | |
if ((length = chars.length) > 1) { | |
// monkey-patch missing colon | |
if (pseudo === 0) { | |
first = chars.charCodeAt(0) | |
// first character is a letter or dash, buffer has a space character | |
if ((first === DASH || first > 96 && first < 123)) { | |
length = (chars = chars.replace(' ', ':')).length | |
} | |
} | |
// execute plugins, property context | |
if (plugged > 0) { | |
if ((result = proxy(PROPS, chars, current, parent, line, column, out.length, id, depth)) !== void 0) { | |
if ((length = (chars = result.trim()).length) === 0) { | |
chars = '\0\0' | |
} | |
} | |
} | |
first = chars.charCodeAt(0) | |
second = chars.charCodeAt(1) | |
switch (first + second) { | |
case NULL: { | |
break | |
} | |
case IMPORT: | |
case CHARSET: { | |
flat += chars + body.charAt(caret) | |
break | |
} | |
default: { | |
if (chars.charCodeAt(length-1) === COLON) | |
break | |
out += property(chars, first, second, chars.charCodeAt(2)) | |
} | |
} | |
} | |
// reset | |
context = 0 | |
insert = 0 | |
pseudo = 0 | |
format = 0 | |
invert = 0 | |
chars = '' | |
code = body.charCodeAt(++caret) | |
break | |
} | |
} | |
} | |
// parse characters | |
switch (code) { | |
case CARRIAGE: | |
case NEWLINE: { | |
// auto insert semicolon | |
if (comment + quote + parentheses + bracket + semicolon === 0) { | |
// valid non-whitespace characters that | |
// may precede a newline | |
switch (peak) { | |
case CLOSEPARENTHESES: | |
case SINGLEQUOTE: | |
case DOUBLEQUOTE: | |
case AT: | |
case TILDE: | |
case GREATERTHAN: | |
case STAR: | |
case PLUS: | |
case FOWARDSLASH: | |
case DASH: | |
case COLON: | |
case COMMA: | |
case SEMICOLON: | |
case OPENBRACES: | |
case CLOSEBRACES: { | |
break | |
} | |
default: { | |
// current buffer has a colon | |
if (pseudo > 0) { | |
insert = 1 | |
} | |
} | |
} | |
} | |
// terminate line comment | |
if (comment === FOWARDSLASH) { | |
comment = 0 | |
} else if (cascade + context === 0) { | |
format = 1 | |
chars += '\0' | |
} | |
// execute plugins, newline context | |
if (plugged * unkwn > 0) { | |
proxy(UNKWN, chars, current, parent, line, column, out.length, id, depth) | |
} | |
// next line, reset column position | |
column = 1 | |
line++ | |
break | |
} | |
case SEMICOLON: | |
case CLOSEBRACES: { | |
if (comment + quote + parentheses + bracket === 0) { | |
column++ | |
break | |
} | |
} | |
default: { | |
// increment column position | |
column++ | |
// current character | |
char = body.charAt(caret) | |
// remove comments, escape functions, strings, attributes and prepare selectors | |
switch (code) { | |
case TAB: | |
case SPACE: { | |
if (quote + bracket + comment === 0) { | |
switch (tail) { | |
case COMMA: | |
case COLON: | |
case TAB: | |
case SPACE: { | |
char = '' | |
break | |
} | |
default: { | |
if (code !== SPACE) { | |
char = ' ' | |
} | |
} | |
} | |
} | |
break | |
} | |
// escape breaking control characters | |
case NULL: { | |
char = '\\0' | |
break | |
} | |
case FORMFEED: { | |
char = '\\f' | |
break | |
} | |
case VERTICALTAB: { | |
char = '\\v' | |
break | |
} | |
// & | |
case AND: { | |
// inverted selector pattern i.e html & | |
if (quote + comment + bracket === 0 && cascade > 0) { | |
invert = 1 | |
format = 1 | |
char = '\f' + char | |
} | |
break | |
} | |
// ::p<l>aceholder, l | |
// :read-on<l>y, l | |
case 108: { | |
if (quote + comment + bracket + pattern === 0 && pseudo > 0) { | |
switch (caret - pseudo) { | |
// ::placeholder | |
case 2: { | |
if (tail === PLACEHOLDER && body.charCodeAt(caret-3) === COLON) { | |
pattern = tail | |
} | |
} | |
// :read-only | |
case 8: { | |
if (trail === READONLY) { | |
pattern = trail | |
} | |
} | |
} | |
} | |
break | |
} | |
// :<pattern> | |
case COLON: { | |
if (quote + comment + bracket === 0) { | |
pseudo = caret | |
} | |
break | |
} | |
// selectors | |
case COMMA: { | |
if (comment + parentheses + quote + bracket === 0) { | |
format = 1 | |
char += '\r' | |
} | |
break | |
} | |
// quotes | |
case DOUBLEQUOTE: { | |
if (comment === 0) { | |
quote = quote === code ? 0 : (quote === 0 ? code : quote) | |
} | |
break | |
} | |
case SINGLEQUOTE: { | |
if (comment === 0) { | |
quote = quote === code ? 0 : (quote === 0 ? code : quote) | |
} | |
break | |
} | |
// attributes | |
case OPENBRACKET: { | |
if (quote + comment + parentheses === 0) { | |
bracket++ | |
} | |
break | |
} | |
case CLOSEBRACKET: { | |
if (quote + comment + parentheses === 0) { | |
bracket-- | |
} | |
break | |
} | |
// functions | |
case CLOSEPARENTHESES: { | |
if (quote + comment + bracket === 0) { | |
parentheses-- | |
} | |
break | |
} | |
case OPENPARENTHESES: { | |
if (quote + comment + bracket === 0) { | |
if (context === 0) { | |
switch (tail*2 + trail*3) { | |
// :matches | |
case 533: { | |
break | |
} | |
// :global, :not, :nth-child etc... | |
default: { | |
counter = 0 | |
context = 1 | |
} | |
} | |
} | |
parentheses++ | |
} | |
break | |
} | |
case AT: { | |
if (comment + parentheses + quote + bracket + pseudo + atrule === 0) { | |
atrule = 1 | |
} | |
break | |
} | |
// block/line comments | |
case STAR: | |
case FOWARDSLASH: { | |
if (quote + bracket + parentheses > 0) { | |
break | |
} | |
switch (comment) { | |
// initialize line/block comment context | |
case 0: { | |
switch (code*2 + body.charCodeAt(caret+1)*3) { | |
// // | |
case 235: { | |
comment = FOWARDSLASH | |
break | |
} | |
// /* | |
case 220: { | |
length = caret | |
comment = STAR | |
break | |
} | |
} | |
break | |
} | |
// end block comment context | |
case STAR: { | |
if (code === FOWARDSLASH && tail === STAR) { | |
// /*<!> ... */, ! | |
if (body.charCodeAt(length+2) === 33) { | |
out += body.substring(length, caret+1) | |
} | |
char = '' | |
comment = 0 | |
} | |
} | |
} | |
} | |
} | |
// ignore comment blocks | |
if (comment === 0) { | |
// aggressive isolation mode, divide each individual selector | |
// including selectors in :not function but excluding selectors in :global function | |
if (cascade + quote + bracket + atrule === 0 && id !== KEYFRAME && code !== SEMICOLON) { | |
switch (code) { | |
case COMMA: | |
case TILDE: | |
case GREATERTHAN: | |
case PLUS: | |
case CLOSEPARENTHESES: | |
case OPENPARENTHESES: { | |
if (context === 0) { | |
// outside of an isolated context i.e nth-child(<...>) | |
switch (tail) { | |
case TAB: | |
case SPACE: | |
case NEWLINE: | |
case CARRIAGE: { | |
char = char + '\0' | |
break | |
} | |
default: { | |
char = '\0' + char + (code === COMMA ? '' : '\0') | |
} | |
} | |
format = 1 | |
} else { | |
// within an isolated context, sleep untill it's terminated | |
switch (code) { | |
case OPENPARENTHESES: { | |
context = ++counter | |
break | |
} | |
case CLOSEPARENTHESES: { | |
if ((context = --counter) === 0) { | |
format = 1 | |
char += '\0' | |
} | |
break | |
} | |
} | |
} | |
break | |
} | |
case TAB: | |
case SPACE: { | |
switch (tail) { | |
case NULL: | |
case OPENBRACES: | |
case CLOSEBRACES: | |
case SEMICOLON: | |
case COMMA: | |
case FORMFEED: | |
case TAB: | |
case SPACE: | |
case NEWLINE: | |
case CARRIAGE: { | |
break | |
} | |
default: { | |
// ignore in isolated contexts | |
if (context === 0) { | |
format = 1 | |
char += '\0' | |
} | |
} | |
} | |
} | |
} | |
} | |
// concat buffer of characters | |
chars += char | |
// previous non-whitespace character code | |
if (code !== SPACE && code !== TAB) { | |
peak = code | |
} | |
} | |
} | |
} | |
// tail character codes | |
trail = tail | |
tail = code | |
// visit every character | |
caret++ | |
} | |
length = out.length | |
// preserve empty selector | |
if (preserve > 0) { | |
if (length === 0 && children.length === 0 && (current[0].length === 0) === false) { | |
if (id !== MEDIA || (current.length === 1 && (cascade > 0 ? nscopealt : nscope) === current[0])) { | |
length = current.join(',').length + 2 | |
} | |
} | |
} | |
if (length > 0) { | |
// cascade isolation mode? | |
selector = cascade === 0 && id !== KEYFRAME ? isolate(current) : current | |
// execute plugins, block context | |
if (plugged > 0) { | |
result = proxy(BLCKS, out, selector, parent, line, column, length, id, depth) | |
if (result !== void 0 && (out = result).length === 0) { | |
return flat + out + children | |
} | |
} | |
out = selector.join(',') + '{' + out + '}' | |
if (prefix*pattern !== 0) { | |
if (prefix === 2 && !vendor(out, 2)) | |
pattern = 0 | |
switch (pattern) { | |
// ::read-only | |
case READONLY: { | |
out = out.replace(readonlyptn, ':'+moz+'$1')+out | |
break | |
} | |
// ::placeholder | |
case PLACEHOLDER: { | |
out = ( | |
out.replace(plcholdrptn, '::' + webkit + 'input-$1') + | |
out.replace(plcholdrptn, '::' + moz + '$1') + | |
out.replace(plcholdrptn, ':' + ms + 'input-$1') + out | |
) | |
break | |
} | |
} | |
pattern = 0 | |
} | |
} | |
return flat + out + children | |
} | |
/** | |
* Select | |
* | |
* @param {Array<string>} parent | |
* @param {string} current | |
* @param {number} invert | |
* @return {Array<string>} | |
*/ | |
function select (parent, current, invert) { | |
var selectors = current.trim().split(selectorptn) | |
var out = selectors | |
var length = selectors.length | |
var l = parent.length | |
switch (l) { | |
// 0-1 parent selectors | |
case 0: | |
case 1: { | |
for (var i = 0, selector = l === 0 ? '' : parent[0] + ' '; i < length; ++i) { | |
out[i] = scope(selector, out[i], invert, l).trim() | |
} | |
break | |
} | |
// >2 parent selectors, nested | |
default: { | |
for (var i = 0, j = 0, out = []; i < length; ++i) { | |
for (var k = 0; k < l; ++k) { | |
out[j++] = scope(parent[k] + ' ', selectors[i], invert, l).trim() | |
} | |
} | |
} | |
} | |
return out | |
} | |
/** | |
* Scope | |
* | |
* @param {string} parent | |
* @param {string} current | |
* @param {number} invert | |
* @param {number} level | |
* @return {string} | |
*/ | |
function scope (parent, current, invert, level) { | |
var selector = current | |
var code = selector.charCodeAt(0) | |
// trim leading whitespace | |
if (code < 33) { | |
code = (selector = selector.trim()).charCodeAt(0) | |
} | |
switch (code) { | |
// & | |
case AND: { | |
switch (cascade + level) { | |
case 0: | |
case 1: { | |
if (parent.trim().length === 0) { | |
break | |
} | |
} | |
default: { | |
return selector.replace(andptn, '$1'+parent.trim()) | |
} | |
} | |
break | |
} | |
// : | |
case COLON: { | |
switch (selector.charCodeAt(1)) { | |
// g in :global | |
case 103: { | |
if (escape > 0 && cascade > 0) { | |
return selector.replace(escapeptn, '$1').replace(andptn, '$1'+nscope) | |
} | |
break | |
} | |
default: { | |
// :hover | |
return parent.trim() + selector | |
} | |
} | |
} | |
default: { | |
// html & | |
if (invert*cascade > 0 && selector.indexOf('\f') > 0) { | |
return selector.replace(andptn, (parent.charCodeAt(0) === COLON ? '' : '$1')+parent.trim()) | |
} | |
} | |
} | |
return parent + selector | |
} | |
/** | |
* Property | |
* | |
* @param {string} input | |
* @param {number} first | |
* @param {number} second | |
* @param {number} third | |
* @return {string} | |
*/ | |
function property (input, first, second, third) { | |
var index = 0 | |
var out = input + ';' | |
var hash = (first*2) + (second*3) + (third*4) | |
var cache | |
// animation: a, n, i characters | |
if (hash === 944) { | |
return animation(out) | |
} else if (prefix === 0 || (prefix === 2 && !vendor(out, 1))) { | |
return out | |
} | |
// vendor prefix | |
switch (hash) { | |
// text-decoration/text-size-adjust: t, e, x | |
case 1015: { | |
// text-size-adjust, - | |
return out.charCodeAt(9) === DASH ? webkit + out + out : out | |
} | |
// filter/fill f, i, l | |
case 951: { | |
// filter, t | |
return out.charCodeAt(3) === 116 ? webkit + out + out : out | |
} | |
// color/column, c, o, l | |
case 963: { | |
// column, n | |
return out.charCodeAt(5) === 110 ? webkit + out + out : out | |
} | |
// box-decoration-break, b, o, x | |
case 1009: { | |
if (out.charCodeAt(4) !== 100) { | |
break | |
} | |
} | |
// mask, m, a, s | |
// clip-path, c, l, i | |
case 969: | |
case 942: { | |
return webkit + out + out | |
} | |
// appearance: a, p, p | |
case 978: { | |
return webkit + out + moz + out + out | |
} | |
// hyphens: h, y, p | |
// user-select: u, s, e | |
case 1019: | |
case 983: { | |
return webkit + out + moz + out + ms + out + out | |
} | |
// background/backface-visibility, b, a, c | |
case 883: { | |
// backface-visibility, - | |
return out.charCodeAt(8) === DASH ? webkit + out + out : out | |
} | |
// flex: f, l, e | |
case 932: { | |
if (out.charCodeAt(4) === DASH) { | |
switch (out.charCodeAt(5)) { | |
// flex-grow, g | |
case 103: { | |
return webkit + 'box-' + out.replace('-grow', '') + webkit + out + ms + out.replace('grow', 'positive') + out | |
} | |
// flex-shrink, s | |
case 115: { | |
return webkit + out + ms + out.replace('shrink', 'negative') + out | |
} | |
// flex-basis, b | |
case 98: { | |
return webkit + out + ms + out.replace('basis', 'preferred-size') + out | |
} | |
} | |
} | |
return webkit + out + ms + out + out | |
} | |
// order: o, r, d | |
case 964: { | |
return webkit + out + ms + 'flex' + '-' + out + out | |
} | |
// justify-items/justify-content, j, u, s | |
case 1023: { | |
// justify-content, c | |
if (out.charCodeAt(8) !== 99) { | |
break | |
} | |
cache = out.substring(out.indexOf(':', 15)).replace('flex-', '').replace('space-between', 'justify') | |
return webkit + 'box-pack' + cache + webkit + out + ms + 'flex-pack' + cache + out | |
} | |
// cursor, c, u, r | |
case 1005: { | |
return cursorptn.test(out) ? out.replace(colonptn, ':' + webkit) + out.replace(colonptn, ':' + moz) + out : out | |
} | |
// writing-mode, w, r, i | |
case 1000: { | |
cache = out.substring(13).trim() | |
index = cache.indexOf('-') + 1 | |
switch (cache.charCodeAt(0)+cache.charCodeAt(index)) { | |
// vertical-lr | |
case 226: { | |
cache = out.replace(writingptn, 'tb') | |
break | |
} | |
// vertical-rl | |
case 232: { | |
cache = out.replace(writingptn, 'tb-rl') | |
break | |
} | |
// horizontal-tb | |
case 220: { | |
cache = out.replace(writingptn, 'lr') | |
break | |
} | |
default: { | |
return out | |
} | |
} | |
return webkit + out + ms + cache + out | |
} | |
// position: sticky | |
case 1017: { | |
if (out.indexOf('sticky', 9) === -1) { | |
return out | |
} | |
} | |
// display(flex/inline-flex/inline-box): d, i, s | |
case 975: { | |
index = (out = input).length - 10 | |
cache = (out.charCodeAt(index) === 33 ? out.substring(0, index) : out).substring(input.indexOf(':', 7) + 1).trim() | |
switch (hash = cache.charCodeAt(0) + (cache.charCodeAt(7)|0)) { | |
// inline- | |
case 203: { | |
// inline-box | |
if (cache.charCodeAt(8) < 111) { | |
break | |
} | |
} | |
// inline-box/sticky | |
case 115: { | |
out = out.replace(cache, webkit+cache)+';'+out | |
break | |
} | |
// inline-flex | |
// flex | |
case 207: | |
case 102: { | |
out = ( | |
out.replace(cache, webkit+(hash > 102 ? 'inline-' : '')+'box')+';'+ | |
out.replace(cache, webkit+cache)+';'+ | |
out.replace(cache, ms+cache+'box')+';'+ | |
out | |
) | |
} | |
} | |
return out + ';' | |
} | |
// align-items, align-center, align-self: a, l, i, - | |
case 938: { | |
if (out.charCodeAt(5) === DASH) { | |
switch (out.charCodeAt(6)) { | |
// align-items, i | |
case 105: { | |
cache = out.replace('-items', '') | |
return webkit + out + webkit + 'box-' + cache + ms + 'flex-' + cache + out | |
} | |
// align-self, s | |
case 115: { | |
return webkit + out + ms + 'flex-item-' + out.replace(selfptn, '') + out | |
} | |
// align-content | |
default: { | |
return webkit + out + ms + 'flex-line-pack' + out.replace('align-content', '') + out | |
} | |
} | |
} | |
break | |
} | |
// width: min-content / width: max-content | |
case 953: { | |
if ((index = out.indexOf('-content', 9)) > 0) { | |
// width: min-content / width: max-content | |
if (out.charCodeAt(index - 3) === 109 && out.charCodeAt(index - 4) !== 45) { | |
cache = out.substring(index - 3) | |
return 'width:' + webkit + cache + 'width:' + moz + cache + 'width:' + cache | |
} | |
} | |
break | |
} | |
// transform, transition: t, r, a | |
case 962: { | |
out = webkit + out + (out.charCodeAt(5) === 102 ? ms + out : '') + out | |
// transitions | |
if (second + third === 211 && out.charCodeAt(13) === 105 && out.indexOf('transform', 10) > 0) { | |
return out.substring(0, out.indexOf(';', 27) + 1).replace(transformptn, '$1' + webkit + '$2') + out | |
} | |
break | |
} | |
} | |
return out | |
} | |
var i = 0 | |
/** | |
* Vendor | |
* | |
* @param {string} content | |
* @param {number} context | |
* @return {boolean} | |
*/ | |
function vendor (content, context) { | |
var index = content.indexOf(context === 1 ? ':' : '{') | |
var key = content.substring(0, context !== 3 ? index : 10) | |
var value = content.substring(index + 1, content.length - 1) | |
return should(context !== 2 ? key : key.replace(pseudofmt, '$1'), value, context) | |
} | |
/** | |
* Supports | |
* | |
* @param {string} match | |
* @param {string} group | |
* @return {string} | |
*/ | |
function supports (match, group) { | |
var out = property(group, group.charCodeAt(0), group.charCodeAt(1), group.charCodeAt(2)) | |
return out !== group+';' ? out.replace(propertyptn, ' or ($1)').substring(4) : '('+group+')' | |
} | |
/** | |
* Animation | |
* | |
* @param {string} input | |
* @return {string} | |
*/ | |
function animation (input) { | |
var length = input.length | |
var index = input.indexOf(':', 9) + 1 | |
var declare = input.substring(0, index).trim() | |
var out = input.substring(index, length-1).trim() | |
switch (input.charCodeAt(9)*keyed) { | |
case 0: { | |
break | |
} | |
// animation-*, - | |
case DASH: { | |
// animation-name, n | |
if (input.charCodeAt(10) !== 110) { | |
break | |
} | |
} | |
// animation/animation-name | |
default: { | |
// split in case of multiple animations | |
var list = out.split((out = '', animationptn)) | |
for (var i = 0, index = 0, length = list.length; i < length; index = 0, ++i) { | |
var value = list[i] | |
var items = value.split(propertiesptn) | |
while (value = items[index]) { | |
var peak = value.charCodeAt(0) | |
if (keyed === 1 && ( | |
// letters | |
(peak > AT && peak < 90) || (peak > 96 && peak < 123) || peak === UNDERSCORE || | |
// dash but not in sequence i.e -- | |
(peak === DASH && value.charCodeAt(1) !== DASH) | |
)) { | |
// not a number/function | |
switch (isNaN(parseFloat(value)) + (value.indexOf('(') !== -1)) { | |
case 1: { | |
switch (value) { | |
// not a valid reserved keyword | |
case 'infinite': case 'alternate': case 'backwards': case 'running': | |
case 'normal': case 'forwards': case 'both': case 'none': case 'linear': | |
case 'ease': case 'ease-in': case 'ease-out': case 'ease-in-out': | |
case 'paused': case 'reverse': case 'alternate-reverse': case 'inherit': | |
case 'initial': case 'unset': case 'step-start': case 'step-end': { | |
break | |
} | |
default: { | |
value += key | |
} | |
} | |
} | |
} | |
} | |
items[index++] = value | |
} | |
out += (i === 0 ? '' : ',') + items.join(' ') | |
} | |
} | |
} | |
out = declare + out + ';' | |
if (prefix === 1 || (prefix === 2 && vendor(out, 1))) | |
return webkit + out + out | |
return out | |
} | |
/** | |
* Isolate | |
* | |
* @param {Array<string>} current | |
*/ | |
function isolate (current) { | |
for (var i = 0, length = current.length, selector = Array(length), padding, element; i < length; ++i) { | |
// split individual elements in a selector i.e h1 h2 === [h1, h2] | |
var elements = current[i].split(elementptn) | |
var out = '' | |
for (var j = 0, size = 0, tail = 0, code = 0, l = elements.length; j < l; ++j) { | |
// empty element | |
if ((size = (element = elements[j]).length) === 0 && l > 1) { | |
continue | |
} | |
tail = out.charCodeAt(out.length-1) | |
code = element.charCodeAt(0) | |
padding = '' | |
if (j !== 0) { | |
// determine if we need padding | |
switch (tail) { | |
case STAR: | |
case TILDE: | |
case GREATERTHAN: | |
case PLUS: | |
case SPACE: | |
case OPENPARENTHESES: { | |
break | |
} | |
default: { | |
padding = ' ' | |
} | |
} | |
} | |
switch (code) { | |
case AND: { | |
element = padding + nscopealt | |
} | |
case TILDE: | |
case GREATERTHAN: | |
case PLUS: | |
case SPACE: | |
case CLOSEPARENTHESES: | |
case OPENPARENTHESES: { | |
break | |
} | |
case OPENBRACKET: { | |
element = padding + element + nscopealt | |
break | |
} | |
case COLON: { | |
switch (element.charCodeAt(1)*2 + element.charCodeAt(2)*3) { | |
// :global | |
case 530: { | |
if (escape > 0) { | |
element = padding + element.substring(8, size - 1) | |
break | |
} | |
} | |
// :hover, :nth-child(), ... | |
default: { | |
if (j < 1 || elements[j-1].length < 1) { | |
element = padding + nscopealt + element | |
} | |
} | |
} | |
break | |
} | |
case COMMA: { | |
padding = '' | |
} | |
default: { | |
if (size > 1 && element.indexOf(':') > 0) { | |
element = padding + element.replace(pseudoptn, '$1' + nscopealt + '$2') | |
} else { | |
element = padding + element + nscopealt | |
} | |
} | |
} | |
out += element | |
} | |
selector[i] = out.replace(formatptn, '').trim() | |
} | |
return selector | |
} | |
/** | |
* Proxy | |
* | |
* @param {number} context | |
* @param {string} content | |
* @param {Array<string>} selectors | |
* @param {Array<string>} parents | |
* @param {number} line | |
* @param {number} column | |
* @param {number} length | |
* @param {number} id | |
* @param {number} depth | |
* @return {(string|void|*)} | |
*/ | |
function proxy (context, content, selectors, parents, line, column, length, id, depth) { | |
for (var i = 0, out = content, next; i < plugged; ++i) { | |
switch (next = plugins[i].call(stylis, context, out, selectors, parents, line, column, length, id, depth)) { | |
case void 0: | |
case false: | |
case true: | |
case null: { | |
break | |
} | |
default: { | |
out = next | |
} | |
} | |
} | |
switch (out) { | |
case void 0: | |
case false: | |
case true: | |
case null: | |
case content: { | |
break | |
} | |
default: { | |
return out | |
} | |
} | |
} | |
/** | |
* Minify | |
* | |
* @param {(string|*)} output | |
* @return {string} | |
*/ | |
function minify (output) { | |
return output | |
.replace(formatptn, '') | |
.replace(beforeptn, '') | |
.replace(afterptn, '$1') | |
.replace(tailptn, '$1') | |
.replace(whiteptn, ' ') | |
} | |
/** | |
* Use | |
* | |
* @param {(Array<function(...?)>|function(...?)|number|void)?} plugin | |
*/ | |
function use (plugin) { | |
switch (plugin) { | |
case void 0: | |
case null: { | |
plugged = plugins.length = 0 | |
break | |
} | |
default: { | |
switch (plugin.constructor) { | |
case Array: { | |
for (var i = 0, length = plugin.length; i < length; ++i) { | |
use(plugin[i]) | |
} | |
break | |
} | |
case Function: { | |
plugins[plugged++] = plugin | |
break | |
} | |
case Boolean: { | |
unkwn = !!plugin|0 | |
} | |
} | |
} | |
} | |
return use | |
} | |
/** | |
* Set | |
* | |
* @param {*} options | |
*/ | |
function set (options) { | |
for (var name in options) { | |
var value = options[name] | |
switch (name) { | |
case 'keyframe': keyed = value|0; break | |
case 'global': escape = value|0; break | |
case 'cascade': cascade = value|0; break | |
case 'compress': compress = value|0; break | |
case 'semicolon': semicolon = value|0; break | |
case 'preserve': preserve = value|0; break | |
case 'prefix': | |
should = null | |
if (!value) { | |
prefix = 0 | |
} else if (typeof value !== 'function') { | |
prefix = 1 | |
} else { | |
prefix = 2 | |
should = value | |
} | |
} | |
} | |
return set | |
} | |
/** | |
* Stylis | |
* | |
* @param {string} selector | |
* @param {string} input | |
* @return {*} | |
*/ | |
function stylis (selector, input) { | |
if (this !== void 0 && this.constructor === stylis) { | |
return factory(selector) | |
} | |
// setup | |
var ns = selector | |
var code = ns.charCodeAt(0) | |
// trim leading whitespace | |
if (code < 33) { | |
code = (ns = ns.trim()).charCodeAt(0) | |
} | |
// keyframe/animation namespace | |
if (keyed > 0) { | |
key = ns.replace(invalidptn, code === OPENBRACKET ? '' : '-') | |
} | |
// reset, used to assert if a plugin is moneky-patching the return value | |
code = 1 | |
// cascade/isolate | |
if (cascade === 1) { | |
nscope = ns | |
} else { | |
nscopealt = ns | |
} | |
var selectors = [nscope] | |
var result | |
// execute plugins, pre-process context | |
if (plugged > 0) { | |
result = proxy(PREPS, input, selectors, selectors, line, column, 0, 0, 0) | |
if (result !== void 0 && typeof result === 'string') { | |
input = result | |
} | |
} | |
// build | |
var output = compile(array, selectors, input, 0, 0) | |
// execute plugins, post-process context | |
if (plugged > 0) { | |
result = proxy(POSTS, output, selectors, selectors, line, column, output.length, 0, 0) | |
// bypass minification | |
if (result !== void 0 && typeof(output = result) !== 'string') { | |
code = 0 | |
} | |
} | |
// reset | |
key = '' | |
nscope = '' | |
nscopealt = '' | |
pattern = 0 | |
line = 1 | |
column = 1 | |
return compress*code === 0 ? output : minify(output) | |
} | |
stylis['use'] = use | |
stylis['set'] = set | |
if (options !== void 0) { | |
set(options) | |
} | |
return stylis | |
})); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment