8
0

renderer.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import diff from '../utils-diff.js';
  7. import ViewText from './text.js';
  8. import ViewElement from './element.js';
  9. const ATTRIBUTES_NEED_UPDATE = 0;
  10. const CHILDREN_NEED_UPDATE = 1;
  11. const TEXT_NEEDS_UPDATE = 2;
  12. export default class Renderer {
  13. constructor( treeView ) {
  14. this.view = treeView.view;
  15. this.domRoot = treeView.domRoot;
  16. this.markedAttrs = new Set();
  17. this.markedChildren = new Set();
  18. this.markedTexts = new Set();
  19. }
  20. markToSync( node, type ) {
  21. if ( type === ATTRIBUTES_NEED_UPDATE ) {
  22. this.markedAttrs.push( node );
  23. } else if ( type === CHILDREN_NEED_UPDATE ) {
  24. this.markedChildren.push( node );
  25. } else if ( type === TEXT_NEEDS_UPDATE ) {
  26. this.markedTexts.push( node );
  27. }
  28. }
  29. render() {
  30. const domDocument = this.domRoot.ownerDocument;
  31. for ( let node of this.markedTexts ) {
  32. if ( !this.markedChildren.has( node.parent ) && node.parent.domElement ) {
  33. updateText( node );
  34. }
  35. }
  36. for ( let element of this.markedAttrs ) {
  37. updateAttrs( element );
  38. }
  39. for ( let element of this.markedChildren ) {
  40. updateChildren( element );
  41. }
  42. function updateText( viewText ) {
  43. const domText = viewText.getDomNode();
  44. if ( domText.data != viewText.getText() ) {
  45. domText.data = viewText.getText();
  46. }
  47. }
  48. function updateAttrs( viewElement ) {
  49. const domElement = viewElement.domElement;
  50. const domAttrKeys = domElement.attributes;
  51. const viewAttrKeys = viewElement.getAttrKeys();
  52. // Add or overwrite attributes.
  53. for ( let key of viewAttrKeys ) {
  54. domElement.setAttribute( key, viewElement.getAttr( key ) );
  55. }
  56. // Remove from DOM attributes which do not exists in the view.
  57. for ( let key of domAttrKeys ) {
  58. if ( !viewElement.hasAttr( key ) ) {
  59. domElement.removeAttribute( key );
  60. }
  61. }
  62. }
  63. function updateChildren( viewElement ) {
  64. const domElement = viewElement.domElement;
  65. const domChildren = domElement.childNodes;
  66. const viewChildren = viewElement.getChildren();
  67. const actions = diff( domChildren, viewChildren, compareNodes );
  68. let i = 0;
  69. for ( let action of actions ) {
  70. if ( action === diff.EQUAL ) {
  71. i++;
  72. } else if ( action === diff.INSERT ) {
  73. domElement.insertBefore( viewToDom( viewChildren[ i ] ), domChildren[ i ] || null );
  74. i++;
  75. } else if ( action === diff.DELETE ) {
  76. domElement.removeChild( domChildren[ i ] );
  77. }
  78. }
  79. }
  80. function compareNodes( domNode, viewNode ) {
  81. // Elements.
  82. if ( domNode instanceof HTMLElement && viewNode instanceof ViewElement ) {
  83. return domNode === viewNode.DOMElement;
  84. }
  85. // Texts.
  86. else if ( domNode instanceof Text && viewNode instanceof ViewText ) {
  87. return domNode.data === viewNode.getText();
  88. }
  89. // Not matching types.
  90. return false;
  91. }
  92. function viewToDom( view ) {
  93. if ( view.domElement ) {
  94. return view.domElement;
  95. }
  96. if ( view instanceof ViewText ) {
  97. return domDocument.createTextNode( view.getText() );
  98. } else {
  99. const domElement = domDocument.createElement( view.name );
  100. view.setDomElement( domElement );
  101. for ( let key of view.getAttrKeys() ) {
  102. domElement.setAttribute( key, view.getAttr( key ) );
  103. }
  104. for ( let childView of view.getChildren() ) {
  105. domElement.appendChild( viewToDom( childView ) );
  106. }
  107. return domElement;
  108. }
  109. }
  110. }
  111. }
  112. Renderer.ATTRIBUTES_NEED_UPDATE = ATTRIBUTES_NEED_UPDATE;
  113. Renderer.CHILDREN_NEED_UPDATE = CHILDREN_NEED_UPDATE;
  114. Renderer.TEXT_NEEDS_UPDATE = TEXT_NEEDS_UPDATE;