You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

260 lines
7.0 KiB

  1. /*********************************************************************
  2. * This is a fork from the CSS Style Declaration part of
  3. * https://github.com/NV/CSSOM
  4. ********************************************************************/
  5. 'use strict';
  6. var CSSOM = require('cssom');
  7. var allProperties = require('./allProperties');
  8. var allExtraProperties = require('./allExtraProperties');
  9. var implementedProperties = require('./implementedProperties');
  10. var { dashedToCamelCase } = require('./parsers');
  11. var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor');
  12. /**
  13. * @constructor
  14. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
  15. */
  16. var CSSStyleDeclaration = function CSSStyleDeclaration(onChangeCallback) {
  17. this._values = {};
  18. this._importants = {};
  19. this._length = 0;
  20. this._onChange =
  21. onChangeCallback ||
  22. function() {
  23. return;
  24. };
  25. };
  26. CSSStyleDeclaration.prototype = {
  27. constructor: CSSStyleDeclaration,
  28. /**
  29. *
  30. * @param {string} name
  31. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue
  32. * @return {string} the value of the property if it has been explicitly set for this declaration block.
  33. * Returns the empty string if the property has not been set.
  34. */
  35. getPropertyValue: function(name) {
  36. if (!this._values.hasOwnProperty(name)) {
  37. return '';
  38. }
  39. return this._values[name].toString();
  40. },
  41. /**
  42. *
  43. * @param {string} name
  44. * @param {string} value
  45. * @param {string} [priority=null] "important" or null
  46. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
  47. */
  48. setProperty: function(name, value, priority) {
  49. if (value === undefined) {
  50. return;
  51. }
  52. if (value === null || value === '') {
  53. this.removeProperty(name);
  54. return;
  55. }
  56. var isCustomProperty = name.indexOf('--') === 0;
  57. if (isCustomProperty) {
  58. this._setProperty(name, value, priority);
  59. return;
  60. }
  61. var lowercaseName = name.toLowerCase();
  62. if (!allProperties.has(lowercaseName) && !allExtraProperties.has(lowercaseName)) {
  63. return;
  64. }
  65. this[lowercaseName] = value;
  66. this._importants[lowercaseName] = priority;
  67. },
  68. _setProperty: function(name, value, priority) {
  69. if (value === undefined) {
  70. return;
  71. }
  72. if (value === null || value === '') {
  73. this.removeProperty(name);
  74. return;
  75. }
  76. if (this._values[name]) {
  77. // Property already exist. Overwrite it.
  78. var index = Array.prototype.indexOf.call(this, name);
  79. if (index < 0) {
  80. this[this._length] = name;
  81. this._length++;
  82. }
  83. } else {
  84. // New property.
  85. this[this._length] = name;
  86. this._length++;
  87. }
  88. this._values[name] = value;
  89. this._importants[name] = priority;
  90. this._onChange(this.cssText);
  91. },
  92. /**
  93. *
  94. * @param {string} name
  95. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty
  96. * @return {string} the value of the property if it has been explicitly set for this declaration block.
  97. * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
  98. */
  99. removeProperty: function(name) {
  100. if (!this._values.hasOwnProperty(name)) {
  101. return '';
  102. }
  103. var prevValue = this._values[name];
  104. delete this._values[name];
  105. delete this._importants[name];
  106. var index = Array.prototype.indexOf.call(this, name);
  107. if (index < 0) {
  108. return prevValue;
  109. }
  110. // That's what WebKit and Opera do
  111. Array.prototype.splice.call(this, index, 1);
  112. // That's what Firefox does
  113. //this[index] = ""
  114. this._onChange(this.cssText);
  115. return prevValue;
  116. },
  117. /**
  118. *
  119. * @param {String} name
  120. */
  121. getPropertyPriority: function(name) {
  122. return this._importants[name] || '';
  123. },
  124. getPropertyCSSValue: function() {
  125. //FIXME
  126. return;
  127. },
  128. /**
  129. * element.style.overflow = "auto"
  130. * element.style.getPropertyShorthand("overflow-x")
  131. * -> "overflow"
  132. */
  133. getPropertyShorthand: function() {
  134. //FIXME
  135. return;
  136. },
  137. isPropertyImplicit: function() {
  138. //FIXME
  139. return;
  140. },
  141. /**
  142. * http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item
  143. */
  144. item: function(index) {
  145. index = parseInt(index, 10);
  146. if (index < 0 || index >= this._length) {
  147. return '';
  148. }
  149. return this[index];
  150. },
  151. };
  152. Object.defineProperties(CSSStyleDeclaration.prototype, {
  153. cssText: {
  154. get: function() {
  155. var properties = [];
  156. var i;
  157. var name;
  158. var value;
  159. var priority;
  160. for (i = 0; i < this._length; i++) {
  161. name = this[i];
  162. value = this.getPropertyValue(name);
  163. priority = this.getPropertyPriority(name);
  164. if (priority !== '') {
  165. priority = ' !' + priority;
  166. }
  167. properties.push([name, ': ', value, priority, ';'].join(''));
  168. }
  169. return properties.join(' ');
  170. },
  171. set: function(value) {
  172. var i;
  173. this._values = {};
  174. Array.prototype.splice.call(this, 0, this._length);
  175. this._importants = {};
  176. var dummyRule;
  177. try {
  178. dummyRule = CSSOM.parse('#bogus{' + value + '}').cssRules[0].style;
  179. } catch (err) {
  180. // malformed css, just return
  181. return;
  182. }
  183. var rule_length = dummyRule.length;
  184. var name;
  185. for (i = 0; i < rule_length; ++i) {
  186. name = dummyRule[i];
  187. this.setProperty(
  188. dummyRule[i],
  189. dummyRule.getPropertyValue(name),
  190. dummyRule.getPropertyPriority(name)
  191. );
  192. }
  193. this._onChange(this.cssText);
  194. },
  195. enumerable: true,
  196. configurable: true,
  197. },
  198. parentRule: {
  199. get: function() {
  200. return null;
  201. },
  202. enumerable: true,
  203. configurable: true,
  204. },
  205. length: {
  206. get: function() {
  207. return this._length;
  208. },
  209. /**
  210. * This deletes indices if the new length is less then the current
  211. * length. If the new length is more, it does nothing, the new indices
  212. * will be undefined until set.
  213. **/
  214. set: function(value) {
  215. var i;
  216. for (i = value; i < this._length; i++) {
  217. delete this[i];
  218. }
  219. this._length = value;
  220. },
  221. enumerable: true,
  222. configurable: true,
  223. },
  224. });
  225. require('./properties')(CSSStyleDeclaration.prototype);
  226. allProperties.forEach(function(property) {
  227. if (!implementedProperties.has(property)) {
  228. var declaration = getBasicPropertyDescriptor(property);
  229. Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration);
  230. Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration);
  231. }
  232. });
  233. allExtraProperties.forEach(function(property) {
  234. if (!implementedProperties.has(property)) {
  235. var declaration = getBasicPropertyDescriptor(property);
  236. Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration);
  237. Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration);
  238. }
  239. });
  240. exports.CSSStyleDeclaration = CSSStyleDeclaration;