model-chain.js

  1. /**
  2. * @example
  3. * import { ModelChain } from "domodel"
  4. *
  5. * const MyModel = {
  6. * tagName: "div",
  7. * children: [{
  8. * tagName: "div",
  9. * identifier: "title"
  10. * }]
  11. *}
  12. *
  13. *const MyModelChain = new ModelChain(MyModel).after("title", {
  14. * tagName: "div",
  15. * textContent: "A description"
  16. *})
  17. *
  18. *Core.run(MyModelChain.definition, { target: document.body })
  19. * @class
  20. * Allows updating of models
  21. * @param {Model} definition
  22. */
  23. function ModelChain(definition) {
  24. /** @type {Model} */
  25. this.definition = structuredClone(definition)
  26. }
  27. /**
  28. * @param {string} [parentIdentifier]
  29. * @param {Model} definition
  30. * @returns {ModelChain}
  31. */
  32. ModelChain.prototype.prepend = function(parentIdentifier, definition) {
  33. if(parentIdentifier === null) {
  34. this.definition.children.unshift(definition)
  35. } else {
  36. const { object } = getObjectByIdentifier(parentIdentifier, this.definition)
  37. if(!object.children) {
  38. object.children = []
  39. }
  40. object.children.unshift(definition)
  41. }
  42. return this
  43. }
  44. /**
  45. * @param {string} [parentIdentifier]
  46. * @param {Model} definition
  47. * @returns {ModelChain}
  48. */
  49. ModelChain.prototype.append = function(parentIdentifier, definition) {
  50. if(parentIdentifier === null) {
  51. this.definition.children.push(definition)
  52. } else {
  53. const { object } = getObjectByIdentifier(parentIdentifier, this.definition)
  54. if(!object.children) {
  55. object.children = []
  56. }
  57. object.children.push(definition)
  58. }
  59. return this
  60. }
  61. /**
  62. * @param {string} identifier
  63. * @param {Model} definition
  64. * @returns {ModelChain}
  65. */
  66. ModelChain.prototype.replace = function(identifier, definition) {
  67. const { object } = getObjectByIdentifier(identifier, this.definition)
  68. for(const property in object) {
  69. delete object[property]
  70. }
  71. Object.assign(object, definition)
  72. return this
  73. }
  74. /**
  75. * @param {string} identifier
  76. * @param {Model} definition
  77. * @returns {ModelChain}
  78. */
  79. ModelChain.prototype.before = function(identifier, definition) {
  80. const { parent, object } = getObjectByIdentifier(identifier, this.definition)
  81. parent.children.splice(parent.children.indexOf(object), 0, definition)
  82. return this
  83. }
  84. /**
  85. * @param {string} identifier
  86. * @param {Model} definition
  87. * @returns {ModelChain}
  88. */
  89. ModelChain.prototype.after = function(identifier, definition) {
  90. const { parent, object } = getObjectByIdentifier(identifier, this.definition)
  91. parent.children.splice(parent.children.indexOf(object) + 1, 0, definition)
  92. return this
  93. }
  94. /**
  95. * @ignore
  96. * @param {string} identifier
  97. * @param {Model} definition
  98. * @returns {{object: object, parent: object}}
  99. */
  100. export function getObjectByIdentifier(identifier, definition) {
  101. /**
  102. *
  103. * @param {object} object
  104. * @param {object} parent
  105. * @returns {{object: object, parent: object}}
  106. */
  107. function walk(object, parent) {
  108. if(object.identifier === identifier) {
  109. return { object, parent }
  110. }
  111. if(object.children) {
  112. for(const child of object.children) {
  113. const obj = walk(child, object)
  114. if(obj) {
  115. return obj
  116. }
  117. }
  118. }
  119. }
  120. return walk(definition, null)
  121. }
  122. export default ModelChain
  123. /**
  124. * @typedef {import("./core.js").Model} Model
  125. */