UNPKG

style-manager

Version:

Manage style, add/replace/delete rules, support media.

271 lines (217 loc) 6.57 kB
/* * style-manager * https://github.com/qiu8310/style-manager * * Copyright (c) 2015 Zhonglei Qiu * Licensed under the MIT license. */ import util from './util'; import RuleControl from './RuleControl'; /* sheet 的结构体 CSSStyleSheet cssRules: CSSRuleList rules: CSSRuleList # cssRules 的别名,建议不要使用 rules,不兼容 IE disabled: Boolean media: MediaList # 这里的 media 一般是一个空的 MediaList ownerNode: Node ownerRule: href: String title: String type: String CSSRuleList [ CSSRule ] # CSSRule 包含了 CSSMediaRule 和 CSSStyleRule length: Number CSSRule # 可以理解成 CSSMediaRule 和 CSSStyleRule 的父类 cssText: String parentRule: CSSRule parentStyleSheet: CSSStyleSheet type: Number # @NOTE CSSMediaRule 上才有的属性 media: MediaList # @NOTE CSSStyleRule 上才有的属性 selectorText: String style: CSSStyleDeclaration # 类似于 element.style,也是 CSSStyleRule 上专有的属性 CSSRule type 支持的值: CSSRule.STYLE_RULE CSSRule.MEDIA_RULE CSSRule.FONT_FACE_RULE CSSRule.PAGE_RULE CSSRule.IMPORT_RULE CSSRule.CHARSET_RULE CSSRule.UNKNOWN_RULE CSSRule.KEYFRAMES_RULE CSSRule.KEYFRAME_RULE # @NOTE reversed for future use CSSRule.NAMESPACE_RULE CSSRule.COUNTER_STYLE_RULE CSSRule.SUPPORTS_RULE CSSRule.DOCUMENT_RULE CSSRule.FONT_FEATURE_VALUES_RULE CSSRule.VIEWPORT_RULE CSSRule.REGION_STYLE_RULE MediaList [ media query string] length: Number mediaText: String */ export default class StyleManager { /** * @param {Node|String} style - Style or Link Node Element or ID * @param {Document} pageDocument */ constructor(style, pageDocument) { this.rc = RuleControl; this.rules = []; this.document = pageDocument || document; this.style = this.sheet = null; if (style.nodeType === Node.ELEMENT_NODE && style.sheet) { this.style = style; if (style.id) this.id = style.id; else this.id = style.id = '__sm__' + Date.now(); } else if (typeof style === 'string') { this.id = '__sm__' + style.replace(/[^\w-]/, ''); } else { throw new Error('StyleManager constructor\'s parameter style is illegal .'); } // init this._initSheet(); } get length () { return this.rules.length; } // 初始化获取 sheet _initSheet() { let {id, style} = this; if (!style) { style = this.document.querySelector('#' + id); if (!style) style = util.createStyleElement(this.document, id); this.style = style; } this.update(); } /** * 返回 rule 在当前 styleSheet 中的位置 * @param {Rule} rule * @returns {Number} */ index(rule) { return this.rules.indexOf(rule); } /** * 返回指定位置上的 rule * @param {Number} index * @returns {Rule} */ get(index) { return this.rules[index]; } /** * 返回 rules 的一个副本 * @returns {Array.<Rule>} */ getAll() { return [].concat(this.rules); } /** * 遍历当前所有的 rules * @param {Function} fn */ each(fn) { for (let i = 0; i < this.rules.length; i++) fn(this.rules[i], i, this.rules); } /** * 在当前 Stylesheet 中插入 cssText * * @param {String} cssText * @param {Number} index * @returns {CssRule} */ insertCssText(cssText, index) { this.sheet.insertRule(cssText, index); return this.sheet.cssRules[index]; } deleteCssRule(index) { this.sheet.deleteRule(index); } /** * 获取当前 Stylesheet 中的所有 CSSRule * @returns {CssRule[]|CSSRule[]} */ getCssRules() { return this.sheet.cssRules; } /** * * @param {CSSRule.type} ruleType * @param {Object} ruleOpts * @param {Number} [insertIndex] - 默认插入文档的最后 */ create(ruleType, ruleOpts, insertIndex) { if (insertIndex == null || insertIndex > this.rules.length) insertIndex = this.rules.length; let rule = this.rc.createRuleByOpts(ruleType, ruleOpts, insertIndex, this); this.rules.splice(insertIndex, 0, rule); return rule; } /** * 修改指定的 rule 的位置,如果 index 和它本来的位置一样,则什么也不做 * @param {Rule} rule * @param {Number} index * @returns {Rule} */ move(rule, index) { let ruleIndex = this.index(rule); let sheet = this.sheet; let cssRule; if (ruleIndex !== index) { this.remove(rule); this.insertCssText(rule.getCssText(), index); cssRule = sheet.cssRules[index]; rule = this.rc.createRuleByCssRule(cssRule, this); this.rules.splice(index, 0, rule); } return rule; } /** * 删除指定的 rule * @param {Rule} rule * @returns {Number} Removed rule's index */ remove(rule) { let index = this.index(rule); if (index < 0) throw new Error(`Rule ${rule} not in current style, can not be removed.`); this.sheet.deleteRule(index); this.rules.splice(index, 1); return index; } /** * 清空此 styleSheet 下面的所有样式 */ empty() { for (let i = 0; i < this.rules.length; i++) this.sheet.deleteRule(0); this.rules.length = 0; } /** * 重新从 sheet 中解析 rules * * 因为 sheet 可能会被其它外部程序改变 */ update() { this.rules.length = 0; let sheet = this.sheet = this.style.sheet; let rules = sheet.cssRules; let rc = this.rc; rc.flatStyleSheetRules(sheet); for (let i = 0; i < rules.length; i++) { this.rules.push(rc.createRuleByCssRule(rules[i], this)); } } /** * * @returns {String} */ getCssText() { let result = []; this.each(rule => result.push(rule.getCssText())); return result.join('\n'); } }