css-class-list.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. 'use strict';
  2. var values = require('object.values');
  3. if (!Object.values) {
  4. values.shim();
  5. }
  6. var CSSClassList = function(node) {
  7. this.parentNode = node;
  8. this.classNames = new Set();
  9. this.classAttr = null;
  10. //this.classValue = null;
  11. };
  12. /**
  13. * Performs a deep clone of this object.
  14. *
  15. * @param parentNode the parentNode to assign to the cloned result
  16. */
  17. CSSClassList.prototype.clone = function(parentNode) {
  18. var node = this;
  19. var nodeData = {};
  20. Object.keys(node).forEach(function(key) {
  21. if (key !== 'parentNode') {
  22. nodeData[key] = node[key];
  23. }
  24. });
  25. // Deep-clone node data.
  26. nodeData = JSON.parse(JSON.stringify(nodeData));
  27. var clone = new CSSClassList(parentNode);
  28. Object.assign(clone, nodeData);
  29. return clone;
  30. };
  31. CSSClassList.prototype.hasClass = function() {
  32. this.classAttr = { // empty class attr
  33. 'name': 'class',
  34. 'value': null
  35. };
  36. this.addClassHandler();
  37. };
  38. // attr.class
  39. CSSClassList.prototype.addClassHandler = function() {
  40. Object.defineProperty(this.parentNode.attrs, 'class', {
  41. get: this.getClassAttr.bind(this),
  42. set: this.setClassAttr.bind(this),
  43. enumerable: true,
  44. configurable: true
  45. });
  46. this.addClassValueHandler();
  47. };
  48. // attr.class.value
  49. CSSClassList.prototype.addClassValueHandler = function() {
  50. Object.defineProperty(this.classAttr, 'value', {
  51. get: this.getClassValue.bind(this),
  52. set: this.setClassValue.bind(this),
  53. enumerable: true,
  54. configurable: true
  55. });
  56. };
  57. CSSClassList.prototype.getClassAttr = function() {
  58. return this.classAttr;
  59. };
  60. CSSClassList.prototype.setClassAttr = function(newClassAttr) {
  61. this.setClassValue(newClassAttr.value); // must before applying value handler!
  62. this.classAttr = newClassAttr;
  63. this.addClassValueHandler();
  64. };
  65. CSSClassList.prototype.getClassValue = function() {
  66. var arrClassNames = Array.from(this.classNames);
  67. return arrClassNames.join(' ');
  68. };
  69. CSSClassList.prototype.setClassValue = function(newValue) {
  70. if(typeof newValue === 'undefined') {
  71. this.classNames.clear();
  72. return;
  73. }
  74. var arrClassNames = newValue.split(' ');
  75. this.classNames = new Set(arrClassNames);
  76. };
  77. CSSClassList.prototype.add = function(/* variadic */) {
  78. this.hasClass();
  79. Object.values(arguments).forEach(this._addSingle.bind(this));
  80. };
  81. CSSClassList.prototype._addSingle = function(className) {
  82. this.classNames.add(className);
  83. };
  84. CSSClassList.prototype.remove = function(/* variadic */) {
  85. this.hasClass();
  86. Object.values(arguments).forEach(this._removeSingle.bind(this));
  87. };
  88. CSSClassList.prototype._removeSingle = function(className) {
  89. this.classNames.delete(className);
  90. };
  91. CSSClassList.prototype.item = function(index) {
  92. var arrClassNames = Array.from(this.classNames);
  93. return arrClassNames[index];
  94. };
  95. CSSClassList.prototype.toggle = function(className, force) {
  96. if(this.contains(className) || force === false) {
  97. this.classNames.delete(className);
  98. }
  99. this.classNames.add(className);
  100. };
  101. CSSClassList.prototype.contains = function(className) {
  102. return this.classNames.has(className);
  103. };
  104. module.exports = CSSClassList;