/********************************************************************* * 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;