style-manager
Version:
Manage style, add/replace/delete rules, support media.
1,414 lines (1,137 loc) • 57.9 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.StyleManager = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _rulesRuleJs = require('./rules/Rule.js');
var _rulesRuleJs2 = _interopRequireDefault(_rulesRuleJs);
var _rulesMediaRuleJs = require('./rules/MediaRule.js');
var _rulesMediaRuleJs2 = _interopRequireDefault(_rulesMediaRuleJs);
var _rulesStyleRuleJs = require('./rules/StyleRule.js');
var _rulesStyleRuleJs2 = _interopRequireDefault(_rulesStyleRuleJs);
var _rulesImportRuleJs = require('./rules/ImportRule.js');
var _rulesImportRuleJs2 = _interopRequireDefault(_rulesImportRuleJs);
var _rulesFontFaceRuleJs = require('./rules/FontFaceRule.js');
var _rulesFontFaceRuleJs2 = _interopRequireDefault(_rulesFontFaceRuleJs);
var _rulesKeyframesRuleJs = require('./rules/KeyframesRule.js');
var _rulesKeyframesRuleJs2 = _interopRequireDefault(_rulesKeyframesRuleJs);
var RuleCtors = [_rulesMediaRuleJs2['default'], _rulesStyleRuleJs2['default'], _rulesImportRuleJs2['default'], _rulesFontFaceRuleJs2['default'], _rulesKeyframesRuleJs2['default']];
var RuleCtorsMap = {};
RuleCtors.forEach(function (RuleCtor) {
return RuleCtorsMap['type-' + RuleCtor.type] = RuleCtor;
});
function getRuleCtorByType(type) {
var result = RuleCtorsMap['type-' + type];
if (!result) throw new Error('Not supported rule type ' + type);
return result;
}
/**
* 扁平化 sheet rules:
*
* 1. 将 CSSMediaRule 中的 CSSStyleRule 独立出来,即使得一个 CSSMediaRule 中只能含有一个 CSSStyleRule
* 2. 将 CSSStyleRule 中的 多 selector 拆分成单个 selector,例如将一个: a, b {} 拆分成两个: a {} b {}
* // NO 3. 将 @media [only] all {} 中的 CSSStyleRule 放到全局中来
* // NO 4. 将 不含有任何 CSSStyleRule 的 CSSMediaRule 去掉
*
* @param {Stylesheet} sheet
*/
function flatStyleSheetRules(sheet) {
var rules = sheet.cssRules,
rulesLength = rules.length,
rule = undefined,
ruleCtor = undefined,
i = 0,
next = undefined;
while (i < rulesLength) {
rule = rules[i];
ruleCtor = getRuleCtorByType(rule.type);
if (ruleCtor.flat) {
next = ruleCtor.flat(rule, i, sheet);
rulesLength += next - i - 1;
i = next;
} else {
i++;
}
}
}
/**
* 根据 cssRule 创建一个封装了的自定义的 rule
* @param {CssRule|CSSRule} cssRule
* @param {StyleManager} styleManager
* @param {Object} [opts]
* @returns {Rule}
*/
function createRuleByCssRule(cssRule, styleManager, opts) {
var Ctor = getRuleCtorByType(cssRule.type);
if (!opts) {
if (!Ctor.parseCssRuleToOpts) throw new _rulesRuleJs2['default']('Rule type ' + cssRule.type + ' not support create by cssRule');
opts = Ctor.parseCssRuleToOpts(cssRule);
}
return new Ctor(cssRule, opts, styleManager);
}
/**
* 根据一些自定义的参数创建 Rule,具体需要什么参数有具体的 Rule 自己实现
* @param {CSSRule.type} type
* @param {Object} opts
* @param {Number} insertIndex
* @param {StyleManager} styleManager
* @returns {Rule}
*/
function createRuleByOpts(type, opts, insertIndex, styleManager) {
var Ctor = getRuleCtorByType(type);
if (!Ctor.parseOptsToCssText) throw new Error('Rule type ' + type + ' not support create by opts.');
if (Ctor.validateUserOpts && !Ctor.validateUserOpts(opts)) throw new Error('Rule type ' + type + '\'s opts is invalid.');
if (Ctor.unifyUserOpts) opts = Ctor.unifyUserOpts(opts);
styleManager.insertCssText(Ctor.parseOptsToCssText(opts), insertIndex);
var cssRule = styleManager.getCssRules()[insertIndex];
return createRuleByCssRule(cssRule, styleManager, opts);
}
exports['default'] = {
Rule: _rulesRuleJs2['default'],
RuleCtors: RuleCtors,
getRuleCtorByType: getRuleCtorByType,
flatStyleSheetRules: flatStyleSheetRules,
createRuleByCssRule: createRuleByCssRule,
createRuleByOpts: createRuleByOpts
};
module.exports = exports['default'];
},{"./rules/FontFaceRule.js":3,"./rules/ImportRule.js":4,"./rules/KeyframesRule.js":5,"./rules/MediaRule.js":6,"./rules/Rule.js":7,"./rules/StyleRule.js":8}],2:[function(require,module,exports){
/*
* style-manager
* https://github.com/qiu8310/style-manager
*
* Copyright (c) 2015 Zhonglei Qiu
* Licensed under the MIT license.
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _util = require('./util');
var _util2 = _interopRequireDefault(_util);
var _RuleControl = require('./RuleControl');
var _RuleControl2 = _interopRequireDefault(_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
*/
var StyleManager = (function () {
/**
* @param {Node|String} style - Style or Link Node Element or ID
* @param {Document} pageDocument
*/
function StyleManager(style, pageDocument) {
_classCallCheck(this, StyleManager);
this.rc = _RuleControl2['default'];
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();
}
_createClass(StyleManager, [{
key: '_initSheet',
// 初始化获取 sheet
value: function _initSheet() {
var id = this.id;
var style = this.style;
if (!style) {
style = this.document.querySelector('#' + id);
if (!style) style = _util2['default'].createStyleElement(this.document, id);
this.style = style;
}
this.update();
}
/**
* 返回 rule 在当前 styleSheet 中的位置
* @param {Rule} rule
* @returns {Number}
*/
}, {
key: 'index',
value: function index(rule) {
return this.rules.indexOf(rule);
}
/**
* 返回指定位置上的 rule
* @param {Number} index
* @returns {Rule}
*/
}, {
key: 'get',
value: function get(index) {
return this.rules[index];
}
/**
* 返回 rules 的一个副本
* @returns {Array.<Rule>}
*/
}, {
key: 'getAll',
value: function getAll() {
return [].concat(this.rules);
}
/**
* 遍历当前所有的 rules
* @param {Function} fn
*/
}, {
key: 'each',
value: function each(fn) {
for (var i = 0; i < this.rules.length; i++) {
fn(this.rules[i], i, this.rules);
}
}
/**
* 在当前 Stylesheet 中插入 cssText
*
* @param {String} cssText
* @param {Number} index
* @returns {CssRule}
*/
}, {
key: 'insertCssText',
value: function insertCssText(cssText, index) {
this.sheet.insertRule(cssText, index);
return this.sheet.cssRules[index];
}
}, {
key: 'deleteCssRule',
value: function deleteCssRule(index) {
this.sheet.deleteRule(index);
}
/**
* 获取当前 Stylesheet 中的所有 CSSRule
* @returns {CssRule[]|CSSRule[]}
*/
}, {
key: 'getCssRules',
value: function getCssRules() {
return this.sheet.cssRules;
}
/**
*
* @param {CSSRule.type} ruleType
* @param {Object} ruleOpts
* @param {Number} [insertIndex] - 默认插入文档的最后
*/
}, {
key: 'create',
value: function create(ruleType, ruleOpts, insertIndex) {
if (insertIndex == null || insertIndex > this.rules.length) insertIndex = this.rules.length;
var 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}
*/
}, {
key: 'move',
value: function move(rule, index) {
var ruleIndex = this.index(rule);
var sheet = this.sheet;
var cssRule = undefined;
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
*/
}, {
key: 'remove',
value: function remove(rule) {
var 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 下面的所有样式
*/
}, {
key: 'empty',
value: function empty() {
for (var i = 0; i < this.rules.length; i++) {
this.sheet.deleteRule(0);
}this.rules.length = 0;
}
/**
* 重新从 sheet 中解析 rules
*
* 因为 sheet 可能会被其它外部程序改变
*/
}, {
key: 'update',
value: function update() {
this.rules.length = 0;
var sheet = this.sheet = this.style.sheet;
var rules = sheet.cssRules;
var rc = this.rc;
rc.flatStyleSheetRules(sheet);
for (var i = 0; i < rules.length; i++) {
this.rules.push(rc.createRuleByCssRule(rules[i], this));
}
}
/**
*
* @returns {String}
*/
}, {
key: 'getCssText',
value: function getCssText() {
var result = [];
this.each(function (rule) {
return result.push(rule.getCssText());
});
return result.join('\n');
}
}, {
key: 'length',
get: function get() {
return this.rules.length;
}
}]);
return StyleManager;
})();
exports['default'] = StyleManager;
module.exports = exports['default'];
},{"./RuleControl":1,"./util":10}],3:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Rule2 = require('./Rule');
var _Rule3 = _interopRequireDefault(_Rule2);
var FontFaceRule = (function (_Rule) {
_inherits(FontFaceRule, _Rule);
function FontFaceRule() {
_classCallCheck(this, FontFaceRule);
_get(Object.getPrototypeOf(FontFaceRule.prototype), 'constructor', this).apply(this, arguments);
}
return FontFaceRule;
})(_Rule3['default']);
exports['default'] = FontFaceRule;
FontFaceRule.type = CSSRule.FONT_FACE_RULE;
module.exports = exports['default'];
},{"./Rule":7}],4:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Rule2 = require('./Rule');
var _Rule3 = _interopRequireDefault(_Rule2);
var ImportRule = (function (_Rule) {
_inherits(ImportRule, _Rule);
function ImportRule() {
_classCallCheck(this, ImportRule);
_get(Object.getPrototypeOf(ImportRule.prototype), 'constructor', this).apply(this, arguments);
}
return ImportRule;
})(_Rule3['default']);
exports['default'] = ImportRule;
ImportRule.type = CSSRule.IMPORT_RULE;
module.exports = exports['default'];
},{"./Rule":7}],5:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Rule2 = require('./Rule');
var _Rule3 = _interopRequireDefault(_Rule2);
var KeyframesRule = (function (_Rule) {
_inherits(KeyframesRule, _Rule);
function KeyframesRule() {
_classCallCheck(this, KeyframesRule);
_get(Object.getPrototypeOf(KeyframesRule.prototype), 'constructor', this).apply(this, arguments);
}
return KeyframesRule;
})(_Rule3['default']);
exports['default'] = KeyframesRule;
KeyframesRule.type = CSSRule.KEYFRAMES_RULE;
module.exports = exports['default'];
},{"./Rule":7}],6:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Rule2 = require('./Rule');
var _Rule3 = _interopRequireDefault(_Rule2);
var _StyleRule = require('./StyleRule');
var _StyleRule2 = _interopRequireDefault(_StyleRule);
var _libsMedia = require('./libs/Media');
var _libsMedia2 = _interopRequireDefault(_libsMedia);
var _util = require('../util');
var MediaRule = (function (_Rule) {
_inherits(MediaRule, _Rule);
function MediaRule() {
_classCallCheck(this, MediaRule);
_get(Object.getPrototypeOf(MediaRule.prototype), 'constructor', this).apply(this, arguments);
}
_createClass(MediaRule, [{
key: 'getSelectorSpecificity',
value: function getSelectorSpecificity() {
return _Rule3['default'].calculateSelectorSpecificity(this.opts.selector);
}
}, {
key: 'getMediaText',
value: function getMediaText() {
return this.cssRule.media.mediaText;
}
}, {
key: 'toStyleRule',
value: function toStyleRule() {
var sm = this.sm;
var index = sm.index(this);
var rule = sm.create(CSSRule.STYLE_RULE, {
selector: this.opts.selector,
style: this.opts.style
}, index + 1);
sm.remove(this);
return rule;
}
/**
* 验证用户提供的 opts 是否合法
* @param {Object} opts
* @returns {Boolean}
*/
}], [{
key: 'validateUserOpts',
value: function validateUserOpts(opts) {
return _StyleRule2['default'].validateUserOpts(opts) && (0, _util.checkType)(opts.media, ['string', 'array', 'object']);
}
/**
*
* @param {Object} opts
* @returns {Object}
*/
}, {
key: 'unifyUserOpts',
value: function unifyUserOpts(opts) {
opts.media = new _libsMedia2['default'](opts.media);
return opts;
}
/**
* @param {Object} opts
* @param {Media} opts.media
* @param {String} opts.selector
* @param {Object} opts.style
* @returns {String}
*/
}, {
key: 'parseOptsToCssText',
value: function parseOptsToCssText(opts) {
return '@media ' + opts.media.toMediaText() + ' { ' + _StyleRule2['default'].parseOptsToCssText(opts) + ' }';
}
/**
* @param {CSSRule|CssRule} cssRule
* @returns {Object}
*/
}, {
key: 'parseCssRuleToOpts',
value: function parseCssRuleToOpts(cssRule) {
var opts = _StyleRule2['default'].parseCssRuleToOpts(cssRule.cssRules[0]);
opts.media = new _libsMedia2['default'](cssRule.media.mediaText);
return opts;
}
/**
* 1. 将 CSSStyleRule 中的多 selector 扁平成单一的 selector
* // NO: 2. 将 @media [only] all {} 中的 CSSStyleRule 放到全局中来(去掉,保留原有风格,或者此步放到压缩时候再做)
* 3. 将 CSSMediaRule 中的 CSSStyleRule 独立出来,即使得一个 CSSMediaRule 中只能含有一个 CSSStyleRule
*
* @param {CSSRule|CssRule|CSSMediaRule} cssMediaRule
* @param {Number} index
* @param {Stylesheet} sheet
* @returns {Number} - sheet 中下一个要处理的 rule
*/
}, {
key: 'flat',
value: function flat(cssMediaRule, index, sheet) {
var mediaText = cssMediaRule.media.mediaText;
var i = 0,
next = undefined,
rules = cssMediaRule.cssRules,
rulesLength = rules.length;
//// media 中没有定义任何的 styleRule,则直接删除这个 mediaRule
//if (!rulesLength) {
// sheet.deleteRule(index);
// return index;
//}
// 执行第 1 点
while (i < rulesLength) {
next = _StyleRule2['default'].flat(rules[i], i, cssMediaRule);
rulesLength += next - i - 1;
i = next;
}
//// 执行第 2 点
//if (mediaText === 'all' || mediaText === 'only all') {
// sheet.deleteRule(index);
// index--;
//
// for (i = 0; i < rulesLength; i++) {
// index += 1;
// sheet.insertRule(rules[i].cssText, index);
// }
//} else if (rulesLength > 1) {}
// 执行第 3 点 (如果执行了第 2 点就不能执行此点)
if (rulesLength > 1) {
for (i = 1; i < rulesLength; i++) {
index += 1;
sheet.insertRule('@media ' + mediaText + ' {' + rules[1].cssText + '}', index);
cssMediaRule.deleteRule(1);
}
}
return index + 1;
}
}]);
return MediaRule;
})(_Rule3['default']);
exports['default'] = MediaRule;
MediaRule.type = CSSRule.MEDIA_RULE;
MediaRule.Media = _libsMedia2['default'];
module.exports = exports['default'];
},{"../util":10,"./Rule":7,"./StyleRule":8,"./libs/Media":9}],7:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _util = require('../util');
var _util2 = _interopRequireDefault(_util);
var CSS_PSEUDO_ELEMENTS = ['after', 'before', 'first-line', 'first-letter', 'selection', 'placeholder'];
var Rule = (function () {
/**
* @param {CSSRule|CssRule} cssRule
* @param {Object} opts
* @param {StyleManager} sm
*/
function Rule(cssRule, opts, sm) {
_classCallCheck(this, Rule);
this.cssRule = cssRule;
this.opts = opts;
this.sm = sm;
this._nextTickId = null;
this._cbs = [];
}
/**
* 带 vendor 前缀的要放非带 vendor 前缀的前面
*
* @param {Array} styleKeys
* @returns {Array}
*/
_createClass(Rule, [{
key: 'remove',
value: function remove() {
this.sm.remove(this);
}
}, {
key: 'is',
value: function is(type) {
return this.constructor.type === type;
}
}, {
key: '_updateSheetLater',
value: function _updateSheetLater(cb) {
var _this = this;
if (typeof cb === 'function') this._cbs.push(cb);
if (this._nextTickId) clearTimeout(this._nextTickId);
this._nextTickId = setTimeout(function () {
var result = _this._updateSheet();
for (var i = 0; i < _this._cbs.length; i++) {
_this._cbs[i](result);
}_this._cbs.length = 0;
}, 0);
}
}, {
key: '_updateSheet',
value: function _updateSheet() {
var sm = this.sm;
var cssText = this.constructor.parseOptsToCssText(this.opts);
// 不用更新
if (cssText === this.getCssText()) return true;
console.debug('Transform opts %o to css text %o', this.opts, cssText);
var index = sm.index(this);
try {
this.cssRule = sm.insertCssText(cssText, index + 1);
} catch (e) {
console.warn('Update rule %o failed. insert cssText: %o, error: %o', this, cssText, e);
return false;
}
// 插入成功才删除之前的 cssRule
sm.deleteCssRule(index);
return true;
}
/**
* @param {Object} opts
* @param {Function} [cb]
*/
}, {
key: 'setOpts',
value: function setOpts(opts, cb) {
_util2['default'].assign(this.opts, opts);
this._updateSheetLater(cb);
}
/**
* @param {Object} opts
* @param {Function} [cb]
*/
}, {
key: 'replaceOpts',
value: function replaceOpts(opts, cb) {
this.opts = opts || {};
this._updateSheetLater(cb);
}
/**
* @param {Function} [cb]
* @returns {Boolean}
*/
}, {
key: 'forceUpdateSheet',
value: function forceUpdateSheet(cb) {
return this._updateSheetLater(cb);
}
}, {
key: 'getCssText',
value: function getCssText() {
return this.cssRule.cssText;
}
}, {
key: 'toString',
value: function toString() {
return this.cssRule.cssText;
}
//========== 工具函数:
/**
* 计算单个 CSS 选择器的权重
* @param {String} selector
*
* https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
*
* Elements and pseudo-elements: 1
* Classes, attributes and pseudo-classes: 10
* Id: 100
*
*/
}], [{
key: 'calculateSelectorSpecificity',
value: function calculateSelectorSpecificity(selector) {
var specificity = 0;
var parsed = selector.replace(/[\*\+>~]/g, ' ') // 去掉全局选择器和特殊分隔符号
.replace(/:not(\(.*?\))/g, '$1') // :not 只需要使用它内部的选择器即可
.replace(/\([^\)]*\)/g, '') // 去掉其它无用的括号
.replace(/\[[^\]]*\]/g, function () {
// 计算属性选择器的权重,并去掉它们
specificity += 10;
return '';
}).replace(/:?:([\w-]+)/g, function (_, m) {
// 计算伪类和伪元素的权值,计算完就把它们去掉
specificity += CSS_PSEUDO_ELEMENTS.indexOf(stripVendors(m)) < 0 ? 10 : 1;
return '';
}).replace(/([#\.]?)[\w-]+/g, function (_, m) {
specificity += ({ '#': 100, '.': 10 })[m] || 1;
return '';
});
if (parsed.trim() !== '') throw new Error('Selector \'' + selector + '\' is invalid, parse result \'' + parsed + '\'.');
return specificity;
}
/**
* @param {Object} style
* @returns {string}
*/
}, {
key: 'styleObjectToCssCode',
value: function styleObjectToCssCode(style) {
var keys = Object.keys(style).map(kebabCaseStyleKey);
if (!keys.length) return '';
keys = sortStyleVendorKeys(keys);
return keys.map(function (key) {
return key + ': ' + style[key] + ';';
}).join('\n');
}
/**
* @param {String} cssCode
* @returns {Object}
*/
}, {
key: 'cssCodeToStyleObject',
value: function cssCodeToStyleObject(cssCode) {
var rules = cssCode.trim().split(';').map(function (rule) {
return rule.trim();
}).filter(function (rule) {
return rule && rule.indexOf(':') > 0;
});
return rules.reduce(function (result, rule) {
var parts = rule.split(':');
result[parts.shift().trim()] = parts.join(':').trim();
return result;
}, {});
}
//========== 子类要必需实现的方法:
/**
* 此方法是给 RuleControl.createRuleByOpts 用的,根据用户输入的参数,解析得到 cssText,
* RuleControl 再将这 cssText 插入 Stylesheet 中,生成 cssRule 创建 Rule
* @param {Object} opts
*/
}, {
key: 'parseOptsToCssText',
value: function parseOptsToCssText(opts) {
return opts.cssText;
}
/**
* 此方法是用在 RuleControl.createRuleByCssRule 中的,当首次从一个文档中已经存在的 Stylesheet 中解析 rules 时,
* 具体的 Rule 应该用此方法将 cssRule 解析成 opts 并保存,方便之后取用
* @param {CSSRule|CssRule} cssRule
*/
}, {
key: 'parseCssRuleToOpts',
value: function parseCssRuleToOpts(cssRule) {
return { cssText: cssRule.cssText };
}
//=========== 子类可选择实现的方法
/**
* 此方法用在 RuleControl.createRuleByOpts,执行 parseOptsToCssText 并生成 cssRule 之前
* 用于对用户传来的 opts 做进一步加工成 Rule 可以直接使用的 opts
* @param {Object} opts
* @returns {Object}
*/
//static unifyUserOpts(opts) {}
/**
* 验证用户提供的 opts 是否合法
* @param {Object} opts
* @returns {Boolean}
*/
//static validateUserOpts(opts) {}
}]);
return Rule;
})();
exports['default'] = Rule;
function sortStyleVendorKeys(styleKeys) {
var vendorStyleKeys = styleKeys.filter(function (key) {
return key[0] === '-';
});
// 没有带 vendor 前缀的属性,直接返回
if (!vendorStyleKeys.length) return styleKeys;
var result = [];
var vendorStyleKeysMirror = vendorStyleKeys.map(function (key) {
return key.replace(/^-\w+-/, '');
});
styleKeys.forEach(function (key) {
if (key[0] !== '-') {
for (var i = 0, l = vendorStyleKeysMirror.length; i < l; i++) {
if (vendorStyleKeysMirror[i] === key) result.push(vendorStyleKeys[i]);
}
result.push(key);
}
});
return styleKeys;
}
/**
* 将 JS 中的驼峰式的样式 key 转成 中划线式的
* @param {String} styleKey
* @returns {String}
*
*/
function kebabCaseStyleKey(styleKey) {
styleKey = _util2['default'].kebabCase(styleKey);
if (['o', 'ms', 'moz', 'webkit'].some(function (k) {
return styleKey.indexOf(k + '-') === 0;
})) {
return '-' + styleKey;
}
return styleKey;
}
function stripVendors(str) {
return str.replace(/^-(o|ms|moz|webkit)-/, '');
}
module.exports = exports['default'];
},{"../util":10}],8:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Rule2 = require('./Rule');
var _Rule3 = _interopRequireDefault(_Rule2);
var _util = require('../util');
var StyleRule = (function (_Rule) {
_inherits(StyleRule, _Rule);
function StyleRule() {
_classCallCheck(this, StyleRule);
_get(Object.getPrototypeOf(StyleRule.prototype), 'constructor', this).apply(this, arguments);
}
_createClass(StyleRule, [{
key: 'getSelectorSpecificity',
value: function getSelectorSpecificity() {
return _Rule3['default'].calculateSelectorSpecificity(this.opts.selector);
}
}, {
key: 'toMediaRule',
value: function toMediaRule() {
var mediaOpts = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
var sm = this.sm;
var index = sm.index(this);
var rule = sm.create(CSSRule.MEDIA_RULE, {
selector: this.opts.selector,
style: this.opts.style,
media: mediaOpts || {}
}, index + 1);
sm.remove(this);
return rule;
}
/**
* 验证用户提供的 opts 是否合法
* @param {Object} opts
* @returns {Boolean}
*/
}], [{
key: 'validateUserOpts',
value: function validateUserOpts(opts) {
return (0, _util.checkType)(opts.selector, 'string') && (0, _util.checkType)(opts.style, 'object');
}
/**
* @param {Object} opts
* @param {String} opts.selector
* @param {Object} opts.style
*
* @returns {String}
*/
}, {
key: 'parseOptsToCssText',
value: function parseOptsToCssText(opts) {
return opts.selector + ' { ' + _Rule3['default'].styleObjectToCssCode(opts.style) + ' }';
}
/**
* @param {CSSRule|CssRule} cssRule
* @returns {Object}
*/
}, {
key: 'parseCssRuleToOpts',
value: function parseCssRuleToOpts(cssRule) {
var selector = cssRule.selectorText,
cssCode = cssRule.cssText.replace(selector, '');
cssCode = cssCode.replace(/^\s*\{|\s*\}$/g, '');
return { selector: selector, style: _Rule3['default'].cssCodeToStyleObject(cssCode) };
}
/**
* 将 CSSStyleRule 中的 多 selector 拆分成单个 selector,例如将一个: a, b {} 拆分成两个: a {} b {}
*
* @param {CSSRule|CssRule|CSSStyleRule} cssStyleRule
* @param {Number} index
* @param {Stylesheet|CSSRule|CssRule} sheet - 也有可能是 CSSMediaRule(需要将它下面的 CSSStyleRule 扁平化)
* @returns {Number} - sheet 中下一个要处理的 rule
*/
}, {
key: 'flat',
value: function flat(cssStyleRule, index, sheet) {
var selectorText = cssStyleRule.selectorText;
if (selectorText.indexOf(',') > 0) {
(function () {
var selectors = selectorText.split(',');
var code = cssStyleRule.cssText.replace(selectorText, '');
sheet.deleteRule(index);
index--;
selectors.forEach(function (selector) {
index += 1;
sheet.insertRule(selector + code, index);
});
})();
}
return index + 1;
}
}]);
return StyleRule;
})(_Rule3['default']);
exports['default'] = StyleRule;
StyleRule.type = CSSRule.STYLE_RULE;
module.exports = exports['default'];
},{"../util":10,"./Rule":7}],9:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _util = require('../../util');
var MEDIA_TYPES = ['all', 'print', 'screen', 'speech', 'aural', 'braille', 'handheld', 'projection', 'tty', 'tv', 'embossed'];
var MEDIA_FEATURES = ['width', 'min-width', 'max-width', 'height', 'min-height', 'max-height', 'aspect-ratio', 'min-aspect-ratio', 'max-aspect-ratio', 'device-width', 'min-device-width', 'max-device-width', 'device-height', 'min-device-height', 'max-device-height', 'device-aspect-ratio', 'min-device-aspect-ratio', 'max-device-aspect-ratio', 'color', 'min-color', 'max-color', 'color-index', 'min-color-index', 'max-color-index', 'monochrome', 'min-monochrome', 'max-monochrome', 'resolution', 'min-resolution', 'max-resolution', 'scan', // progressive, interlace
'grid', 'orientation' // portrait, landscape
];
var checkType = function checkType(type) {
if (MEDIA_TYPES.indexOf(type) < 0) throw new Error('Media type \'' + type + '\' is invalid, need one of ' + MEDIA_TYPES.join(', ') + '.');
};
var checkFeature = function checkFeature(feature) {
if (MEDIA_FEATURES.indexOf(feature) < 0) throw new Error('Media feature \'' + feature + '\' is invalid, need one of ' + MEDIA_FEATURES.join(', ') + '.');
};
/*
media_query_list: <media_query> [, <media_query> ]*
media_query: [[only | not]? <media_type> [ and <expression> ]*] | <expression> [ and <expression> ]*
expression: ( <media_feature> [: <value>]? )
media_type: all | print | projection | screen ...
media_feature: width | min-width | max-width ...
*/
// 注意,真正插入 Document 中,此函数返回的值可能会被浏览器优化或者改变 features 顺序,比如:
// @media all and (width: 300px), all and (height: 400px) 会被优化成
// @media all and (width: 300px), (height: 400px)
// 所以插入 Document 中后就需要用原生的 media.mediaText 去获取系统中的 mediaText,而不能以此函数为准
/**
* 将一个对象或用户自定的的mediaText 解析成一个统一的 mediaText,
* 保证得到的 mediaText 在 features 相同的情况下,它也是一致的
*
* Object opt structure:
* only: Boolean
* not: Boolean - only 和 not 最多只能有一个为 true
* type: String - one of MEDIA_TYPES
* features: Object
*
* @param {Object|Array<Object>|String} opts
* @returns {String}
*
* @example
*
* input: {type: 'all', features: {width: {min: '30px', max: '200px'}}}
* input: {type: 'all', features: {maxWidth: '200px', minWidth: '30px'}}
* input: 'all and (max-width: 200px) and (min-width: 30px)'
*
* all output: all and (min-width: 30px) and (max-width: 200px)
*
*/
var MediaQuery = (function () {
function MediaQuery(modifier, type, features) {
_classCallCheck(this, MediaQuery);
this.modifier = modifier;
this.type = type;
this.features = features;
}
_createClass(MediaQuery, [{
key: 'only',
value: function only() {
this.modifier = 'only';
}
}, {
key: 'reverse',
value: function reverse() {
this.modifier = 'not';
}
}, {
key: 'setType',
value: function setType(type) {
checkType(type);
this.type = type;
}
}, {
key: 'setFeatures',
value: function setFeatures(features) {
this.features = parseObjectFeaturesToArray(features);
}
}, {
key: 'appendFeatures',
value: function appendFeatures(features) {
var _features;
(_features = this.features).push.apply(_features, _toConsumableArray(parseObjectFeaturesToArray(features)));
}
}, {
key: 'toMediaText',
value: function toMediaText() {
var text = this.modifier;
var features = this.features;
var allFeatures = MEDIA_FEATURES;
text += (text ? ' ' : '') + this.type;
if (features.length) {
// 对 features 进行排序,保证输出的 text 的一致性
features = [].concat(features); // 克隆一份,保证原有顺序不变
features.sort(function (a, b) {
return allFeatures.indexOf(a.key) - allFeatures.indexOf(b.key);
});
text += ' and (' + features.map(function (f) {
return f.key + ('value' in f ? ': ' + f.value : '');
}).join(') and (') + ')';
}
return text;
}
}]);
return MediaQuery;
})();
var Media = (function () {
function Media(opts) {
var _this = this;
_classCallCheck(this, Media);
this.list = [];
if (!opts) opts = { type: 'all' };
var type = (0, _util.getType)(opts);
if (Array.isArray(opts)) {
opts.forEach(function (opt) {
return _this.addMediaQuery(opt);
});
} else if (typeof opts === 'string') {
opts.split(',').forEach(function (opt) {
return _this.addMediaQuery(opt);
});
} else {
this.addMediaQuery(opts);
}
}
_createClass(Media, [{
key: 'addMediaQuery',
value: function addMediaQuery(opt) {
var mq = undefined;
switch ((0, _util.getType)(opt)) {
case 'object':
mq = parseObjectOptToQuery(opt);
break;
case 'string':
mq = parseStringOptToQuery(opt);
break;
default:
throw new Error('opt ' + opt + ' can not parse to media query.');
}
this.list.push(mq);
return mq;
}
/**
* @param {Number} index
*/
}, {
key: 'get',
value: function get(index) {
return this.list[index];
}
}, {
key: 'toMediaText',
value: function toMediaText() {
return this.list.map(function (query) {
return query.toMediaText();
}).join(', ');
}
}, {
key: 'equals',
value: function equals(mediaQuery) {
return this.toMediaText() === mediaQuery.toMediaText();
}
}, {
key: 'length',
get: function get() {
return this.list.length;
}
}], [{
key: 'normalize',
value: function normalize(opts) {
return new Media(opts).toMediaText();
}
}]);
return Media;
})();
exports['default'] = Media;
Media.TYPES = MEDIA_TYPES;
Media.FEATURES = MEDIA_FEATURES;
// ============== 解析成统一的对象
function parseStringOptToQuery(opt) {
var parts = opt.trim().split(/\s+and\s+/);
var types = MEDIA_TYPES;
var type = 'all',
modifier = '',
features = {};
parts[0].replace(/^(?:(only|not)\s+)?([-\w]+)$/, function (_, m, t) {
if (types.indexOf(t) >= 0) {
if (m) modifier = m;
type = t;
parts.shift();
}
});
parts.forEach(function (part) {
var kv = part.replace(/^\(\s*(.*?)\s*\)$/, '$1').split(/\s*:\s*/);
if (kv.length === 1) features[kv[0]] = true;else if (kv.length === 2) features[kv[0]] = kv[1];else throw new Error('Parse media string error.');
});
features = parseObjectFeaturesToArray(features);
return