fontTool.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. Trex.I.FontTool = Trex.Mixin.create({
  2. initialize: function(editor, toolbar, config) {
  3. this.$super.initialize(editor, toolbar, config);
  4. },
  5. handler: function(data) {
  6. this.onBeforeHandler(data);
  7. this.doHandle(data);
  8. this.onAfterHandler(data);
  9. },
  10. onBeforeHandler: function() {
  11. },
  12. doHandle: function(data) {
  13. var self = this,
  14. range, newStyle = self.computeNewStyle(data);
  15. self.canvas.execute(function(processor) {
  16. var selectedCells = (processor.table) ? processor.table.getTdArr() : [];
  17. if (selectedCells.length > 0) {
  18. range = goog.dom.Range.createFromNodeContents(selectedCells[0]);
  19. processor.executeUsingCaret(function() {
  20. self.tableCellsExecutor(processor, newStyle, selectedCells);
  21. });
  22. } else {
  23. range = processor.createGoogRange();
  24. if (range) {
  25. self.rangeExecutor(processor, newStyle, range);
  26. }
  27. }
  28. });
  29. },
  30. onAfterHandler: function() {
  31. },
  32. tableCellsExecutor: function(processor, newStyle, cells) {
  33. var self = this;
  34. cells.each(function(cell) {
  35. var range = goog.dom.Range.createFromNodeContents(cell);
  36. range.select();
  37. self.rangeExecutor(processor, newStyle, range);
  38. });
  39. },
  40. findQueryingNode: function(goog_range) {
  41. if (goog_range) {
  42. var textNode;
  43. try {
  44. textNode = this.findFirst(goog_range.__iterator__(), function(node) {
  45. return node.nodeType == 3 && node.nodeValue.trim();
  46. });
  47. } catch (ignore4ie678) {}
  48. if (textNode) {
  49. return textNode.parentNode;
  50. } else { // fallback condition
  51. var startNode = goog_range.getStartNode();
  52. if (startNode && startNode.nodeType == 3) {
  53. return startNode.parentNode;
  54. }
  55. return startNode;
  56. }
  57. }
  58. },
  59. findFirst: function(iterator, condition) {
  60. try {
  61. return goog.iter.filter(iterator, condition).next();
  62. } catch(e) {
  63. return null;
  64. }
  65. }
  66. });
  67. Trex.I.WrappingSpanFontTool = Trex.Mixin.create({
  68. wrapTextAsStyledSpan: function(processor, newStyle, range) {
  69. var affectedNodes;
  70. if (processor.isCollapsed()) {
  71. var startNode = range.getStartNode();
  72. if (startNode.nodeType == 3) {
  73. startNode = startNode.parentNode;
  74. }
  75. var targetNode = this.findOrCreateDummySpan(startNode, processor, range);
  76. var wordJoiner = targetNode.firstChild;
  77. processor.createGoogRangeFromNodes(wordJoiner, wordJoiner.length, wordJoiner, wordJoiner.length).select();
  78. affectedNodes = [ targetNode ];
  79. } else {
  80. processor.executeUsingCaret(function(range, savedCaret) {
  81. var iterator = createTextRangeIterator(savedCaret);
  82. var textNodes = collectTextNodes(iterator);
  83. affectedNodes = collectTextOnlySpans(textNodes);
  84. });
  85. }
  86. processor.apply(affectedNodes, {
  87. style: newStyle
  88. });
  89. function createTextRangeIterator(savedCaret) {
  90. var startCaret = savedCaret.getCaret(_TRUE),
  91. endCaret = savedCaret.getCaret(_FALSE);
  92. return new goog.dom.TextRangeIterator(startCaret, 0, endCaret, 0);
  93. }
  94. // Known Issue : <p>&nbsp;</p>에 대해 p의 childNodes.length === 0 이라 적용이 안된다.
  95. function collectTextNodes(iterator) {
  96. var result = [];
  97. goog.iter.forEach(iterator, function(node) {
  98. // 잘못된 위치의 TextNode는 제외
  99. if (node.nodeType == 3 && !$tom.kindOf(node.parentNode, "table,thead,tbody,tr,ul,ol")) {
  100. result.push(node);
  101. }
  102. });
  103. return result;
  104. }
  105. function collectTextOnlySpans(textNodes) {
  106. var result = [];
  107. textNodes.each(function(node) {
  108. var parentNode = node.parentNode;
  109. if (parentNode.nodeName == "SPAN" && hasOnlyOneChild(parentNode)) {
  110. result.push(parentNode);
  111. } else {
  112. var newSpan = processor.create("span");
  113. $tom.wrap(newSpan, node);
  114. result.push(newSpan);
  115. }
  116. });
  117. return result;
  118. }
  119. function hasOnlyOneChild(node) {
  120. var childNodes = node.childNodes;
  121. var childCount = childNodes.length;
  122. if (childCount > 3) { // early return
  123. return _FALSE;
  124. }
  125. for (var i = 0, len = childCount; i < len; i++) {
  126. if ($tom.isGoogRangeCaret(childNodes[i])) {
  127. childCount = childCount - 1;
  128. }
  129. }
  130. return childCount == 1;
  131. }
  132. },
  133. /**
  134. * collapsed 일 때에 style을 적용할 수 있는 span을 찾거나, 새로 span을 만든다.
  135. */
  136. findOrCreateDummySpan: function(node, processor, goog_range) {
  137. var reuseExistNode = (node.tagName == "SPAN" && node.childNodes.length == 1 && node.firstChild.nodeType == 3 && node.firstChild.nodeValue == Trex.__WORD_JOINER);
  138. if (reuseExistNode) {
  139. return node;
  140. } else {
  141. return this.createDummySpan(node, processor, goog_range);
  142. }
  143. },
  144. createDummySpan: function (parentNode, processor, goog_range) {
  145. var newNode = null;
  146. if (parentNode.tagName == "SPAN") {
  147. newNode = $tom.clone(parentNode);
  148. } else {
  149. newNode = processor.create('span');
  150. }
  151. newNode.appendChild(processor.newDummy());
  152. newNode = goog_range.insertNode(newNode); // NOTE: IE에서는 return된 value를 사용해야 한다.
  153. // insertNode로 인해 빈 TextNode가 생긴 경우, 바로 삭제해준다.
  154. $tom.removeEmptyTextNode(newNode.previousSibling);
  155. $tom.removeEmptyTextNode(newNode.nextSibling);
  156. return newNode;
  157. }
  158. });
  159. Trex.I.WrappingDummyFontTool = Trex.Mixin.create({
  160. wrapDummy: function(processor, range) {
  161. var targetNode = this.createDummySpan(processor, range);
  162. var wordJoiner = targetNode.firstChild;
  163. $tom.unwrap(targetNode);
  164. processor.createGoogRangeFromNodes(wordJoiner, 0, wordJoiner, wordJoiner.length).select();
  165. return wordJoiner;
  166. },
  167. createDummySpan: function (processor, goog_range) {
  168. var newNode = null;
  169. newNode = processor.create('span');
  170. newNode.appendChild(processor.newDummy());
  171. newNode = goog_range.insertNode(newNode); // NOTE: IE에서는 return된 value를 사용해야 한다.
  172. // insertNode로 인해 빈 TextNode가 생긴 경우, 바로 삭제해준다.
  173. $tom.removeEmptyTextNode(newNode.previousSibling);
  174. $tom.removeEmptyTextNode(newNode.nextSibling);
  175. return newNode;
  176. }
  177. });