271 lines
7.3 KiB
JavaScript
271 lines
7.3 KiB
JavaScript
/*********************************************************************
|
|
* This is a fork from the CSS Style Declaration part of
|
|
* https://github.com/NV/CSSOM
|
|
********************************************************************/
|
|
'use strict';
|
|
var CSSOM = require('rrweb-cssom');
|
|
var allProperties = require('./allProperties');
|
|
var allExtraProperties = require('./allExtraProperties');
|
|
var implementedProperties = require('./implementedProperties');
|
|
var { dashedToCamelCase } = require('./parsers');
|
|
var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor');
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
|
|
*/
|
|
var CSSStyleDeclaration = function CSSStyleDeclaration(onChangeCallback) {
|
|
this._values = {};
|
|
this._importants = {};
|
|
this._length = 0;
|
|
this._onChange = onChangeCallback;
|
|
this._setInProgress = false;
|
|
};
|
|
CSSStyleDeclaration.prototype = {
|
|
constructor: CSSStyleDeclaration,
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue
|
|
* @return {string} the value of the property if it has been explicitly set for this declaration block.
|
|
* Returns the empty string if the property has not been set.
|
|
*/
|
|
getPropertyValue: function (name) {
|
|
if (!this._values.hasOwnProperty(name)) {
|
|
return '';
|
|
}
|
|
return this._values[name].toString();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @param {string} value
|
|
* @param {string} [priority=null] "important" or null
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
|
|
*/
|
|
setProperty: function (name, value, priority) {
|
|
if (value === undefined) {
|
|
return;
|
|
}
|
|
if (value === null || value === '') {
|
|
this.removeProperty(name);
|
|
return;
|
|
}
|
|
var isCustomProperty = name.indexOf('--') === 0;
|
|
if (isCustomProperty) {
|
|
this._setProperty(name, value, priority);
|
|
return;
|
|
}
|
|
var lowercaseName = name.toLowerCase();
|
|
if (!allProperties.has(lowercaseName) && !allExtraProperties.has(lowercaseName)) {
|
|
return;
|
|
}
|
|
|
|
this[lowercaseName] = value;
|
|
this._importants[lowercaseName] = priority;
|
|
},
|
|
_setProperty: function (name, value, priority) {
|
|
if (value === undefined) {
|
|
return;
|
|
}
|
|
if (value === null || value === '') {
|
|
this.removeProperty(name);
|
|
return;
|
|
}
|
|
|
|
var originalText;
|
|
if (this._onChange) {
|
|
originalText = this.cssText;
|
|
}
|
|
|
|
if (this._values[name]) {
|
|
// Property already exist. Overwrite it.
|
|
var index = Array.prototype.indexOf.call(this, name);
|
|
if (index < 0) {
|
|
this[this._length] = name;
|
|
this._length++;
|
|
}
|
|
} else {
|
|
// New property.
|
|
this[this._length] = name;
|
|
this._length++;
|
|
}
|
|
this._values[name] = value;
|
|
this._importants[name] = priority;
|
|
if (this._onChange && this.cssText !== originalText && !this._setInProgress) {
|
|
this._onChange(this.cssText);
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty
|
|
* @return {string} the value of the property if it has been explicitly set for this declaration block.
|
|
* Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
|
|
*/
|
|
removeProperty: function (name) {
|
|
if (!this._values.hasOwnProperty(name)) {
|
|
return '';
|
|
}
|
|
|
|
var prevValue = this._values[name];
|
|
delete this._values[name];
|
|
delete this._importants[name];
|
|
|
|
var index = Array.prototype.indexOf.call(this, name);
|
|
if (index < 0) {
|
|
return prevValue;
|
|
}
|
|
|
|
// That's what WebKit and Opera do
|
|
Array.prototype.splice.call(this, index, 1);
|
|
|
|
// That's what Firefox does
|
|
//this[index] = ""
|
|
|
|
if (this._onChange) {
|
|
this._onChange(this.cssText);
|
|
}
|
|
return prevValue;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {String} name
|
|
*/
|
|
getPropertyPriority: function (name) {
|
|
return this._importants[name] || '';
|
|
},
|
|
|
|
getPropertyCSSValue: function () {
|
|
//FIXME
|
|
return;
|
|
},
|
|
|
|
/**
|
|
* element.style.overflow = "auto"
|
|
* element.style.getPropertyShorthand("overflow-x")
|
|
* -> "overflow"
|
|
*/
|
|
getPropertyShorthand: function () {
|
|
//FIXME
|
|
return;
|
|
},
|
|
|
|
isPropertyImplicit: function () {
|
|
//FIXME
|
|
return;
|
|
},
|
|
|
|
/**
|
|
* http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item
|
|
*/
|
|
item: function (index) {
|
|
index = parseInt(index, 10);
|
|
if (index < 0 || index >= this._length) {
|
|
return '';
|
|
}
|
|
return this[index];
|
|
},
|
|
};
|
|
|
|
Object.defineProperties(CSSStyleDeclaration.prototype, {
|
|
cssText: {
|
|
get: function () {
|
|
var properties = [];
|
|
var i;
|
|
var name;
|
|
var value;
|
|
var priority;
|
|
for (i = 0; i < this._length; i++) {
|
|
name = this[i];
|
|
value = this.getPropertyValue(name);
|
|
priority = this.getPropertyPriority(name);
|
|
if (priority !== '') {
|
|
priority = ' !' + priority;
|
|
}
|
|
properties.push([name, ': ', value, priority, ';'].join(''));
|
|
}
|
|
return properties.join(' ');
|
|
},
|
|
set: function (value) {
|
|
var i;
|
|
this._values = {};
|
|
Array.prototype.splice.call(this, 0, this._length);
|
|
this._importants = {};
|
|
var dummyRule;
|
|
try {
|
|
dummyRule = CSSOM.parse('#bogus{' + value + '}').cssRules[0].style;
|
|
} catch (err) {
|
|
// malformed css, just return
|
|
return;
|
|
}
|
|
this._setInProgress = true;
|
|
var rule_length = dummyRule.length;
|
|
var name;
|
|
for (i = 0; i < rule_length; ++i) {
|
|
name = dummyRule[i];
|
|
this.setProperty(
|
|
dummyRule[i],
|
|
dummyRule.getPropertyValue(name),
|
|
dummyRule.getPropertyPriority(name)
|
|
);
|
|
}
|
|
this._setInProgress = false;
|
|
if (this._onChange) {
|
|
this._onChange(this.cssText);
|
|
}
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
},
|
|
parentRule: {
|
|
get: function () {
|
|
return null;
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
},
|
|
length: {
|
|
get: function () {
|
|
return this._length;
|
|
},
|
|
/**
|
|
* This deletes indices if the new length is less then the current
|
|
* length. If the new length is more, it does nothing, the new indices
|
|
* will be undefined until set.
|
|
**/
|
|
set: function (value) {
|
|
var i;
|
|
for (i = value; i < this._length; i++) {
|
|
delete this[i];
|
|
}
|
|
this._length = value;
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
},
|
|
});
|
|
|
|
require('./properties')(CSSStyleDeclaration.prototype);
|
|
|
|
allProperties.forEach(function (property) {
|
|
if (!implementedProperties.has(property)) {
|
|
var declaration = getBasicPropertyDescriptor(property);
|
|
Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration);
|
|
Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration);
|
|
}
|
|
});
|
|
|
|
allExtraProperties.forEach(function (property) {
|
|
if (!implementedProperties.has(property)) {
|
|
var declaration = getBasicPropertyDescriptor(property);
|
|
Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration);
|
|
Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration);
|
|
}
|
|
});
|
|
|
|
exports.CSSStyleDeclaration = CSSStyleDeclaration;
|