diff --git a/apps/editor/src/js/domUtils.js b/apps/editor/src/js/domUtils.js index c7a1f69813..72f4489c67 100644 --- a/apps/editor/src/js/domUtils.js +++ b/apps/editor/src/js/domUtils.js @@ -25,19 +25,6 @@ var isElemNode = function(node) { return node && node.nodeType === Node.ELEMENT_NODE; }; -/** - * getChildNodeAt - * Get child node in given parent and index - * @param {HTMLElement} elem parent element - * @param {number} index node index - * @return {Node} child - */ -var getChildNodeAt = function(elem, index) { - if (elem.childNodes.length && index >= 0) { - return elem.childNodes[index]; - } -}; - /** * getNodeName * Get node name of node @@ -117,7 +104,7 @@ var getChildNodeByOffset = function(node, index) { if (isTextNode(node)) { currentNode = node; - } else { + } else if (node.childNodes.length && index >= 0) { currentNode = node.childNodes[index]; } @@ -249,8 +236,11 @@ var getNextTopBlockNode = function(node) { return getNodeWithDirectionUnderParent('next', node, 'BODY'); }; +var getTopBlockNode = function(node) { + return getParentUntil(node, 'BODY'); +}; + module.exports = { - getChildNodeAt: getChildNodeAt, getNodeName: getNodeName, isTextNode: isTextNode, isElemNode: isElemNode, @@ -261,5 +251,6 @@ module.exports = { getChildNodeByOffset: getChildNodeByOffset, getPrevTopBlockNode: getPrevTopBlockNode, getNextTopBlockNode: getNextTopBlockNode, - getParentUntil: getParentUntil + getParentUntil: getParentUntil, + getTopBlockNode: getTopBlockNode }; diff --git a/apps/editor/src/js/wwHrManager.js b/apps/editor/src/js/wwHrManager.js index f2bf582812..d28df97779 100644 --- a/apps/editor/src/js/wwHrManager.js +++ b/apps/editor/src/js/wwHrManager.js @@ -51,14 +51,16 @@ WwHrManager.prototype._initEvent = function() { WwHrManager.prototype._initKeyHandler = function() { var self = this; - this.wwe.addKeyEventHandler('ENTER', function(event, range) { - if (self._isInHr(range) || self._isNearHr(range)) { - return self._removeHrIfNeed(range, event); - } + this.wwe.addKeyEventHandler(function(ev, range, keyMap) { + return self._onTypedInHr(range, keyMap); + }); + + this.wwe.addKeyEventHandler('ENTER', function(ev, range) { + return self._removeHrOnEnter(range, ev); }); - this.wwe.addKeyEventHandler('BACK_SPACE', function(event, range) { - return self._removeHrIfNeed(range, event); + this.wwe.addKeyEventHandler('BACK_SPACE', function(ev, range) { + return self._removeHrOnBackspace(range, ev); }); }; @@ -79,42 +81,90 @@ WwHrManager.prototype._isInHr = function(range) { * @returns {boolean} result */ WwHrManager.prototype._isNearHr = function(range) { - var prevNode = domUtils.getChildNodeAt(range.startContainer, range.startOffset - 1); + var prevNode = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset - 1); return domUtils.getNodeName(prevNode) === 'HR'; }; +WwHrManager.prototype._onTypedInHr = function(range, keyMap) { + //HR위에서 테스트 컨텐츠 입력을 시도한경우 + if ((this._isInHr(range) || this._isNearHr(range)) + && (!keyMap.length || /^[A-Z0-9]$/.test(keyMap)) + ) { + this.wwe.breakToNewDefaultBlock(range, 'before'); + return false; + } +}; + /** - * _removeHrIfNeed - * Remove hr if need + * _removeHrOnEnter + * Remove hr if need on enter * @param {Range} range range - * @param {Event} event event + * @param {Event} ev event * @returns {boolean} return true if hr was removed */ -WwHrManager.prototype._removeHrIfNeed = function(range, event) { - var hrSuspect, cursorTarget; +WwHrManager.prototype._removeHrOnEnter = function(range, ev) { + var hrSuspect, blockPosition; + + if (!range.collapsed) { + return; + } if (this._isInHr(range)) { - hrSuspect = domUtils.getChildNodeAt(range.startContainer, range.startOffset); - } else if (range.startOffset === 0) { - hrSuspect = range.startContainer.previousSibling || range.startContainer.parentNode.previousSibling; + hrSuspect = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset); + } else if (this._isNearHr(range)) { + hrSuspect = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset - 1); + blockPosition = 'before'; + } - if (domUtils.getNodeName(hrSuspect) !== 'HR') { - hrSuspect = null; - } + return this._changeHrToNewDefaultBlock(hrSuspect, range, ev, blockPosition); +}; + +/** + * _removeHrOnBackspace + * Remove hr if need on backspace + * @param {Range} range range + * @param {Event} ev event + * @returns {boolean} return true if hr was removed + */ +WwHrManager.prototype._removeHrOnBackspace = function(range, ev) { + var hrSuspect, blockPosition; + + if (!range.collapsed) { + return; + } + + if (this._isInHr(range)) { + hrSuspect = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset); + } else if (range.startOffset === 0) { + hrSuspect = domUtils.getPrevTopBlockNode(range.startContainer); + blockPosition = 'none'; } else if (this._isNearHr(range)) { - hrSuspect = domUtils.getChildNodeAt(range.startContainer, range.startOffset - 1); + hrSuspect = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset - 1); + blockPosition = 'before'; } - if (hrSuspect) { - event.preventDefault(); + return this._changeHrToNewDefaultBlock(hrSuspect, range, ev, blockPosition); +}; + +/** + * _changeHrToNewDefaultBlock + * Remove hr and add new default block then set range to it + * @param {Node} hrSuspect Node could be hr + * @param {Range} range range + * @param {Event} ev event + * @param {strong} newBlockPosition new default block add position + * @returns {boolean} return true if hr was removed + */ +WwHrManager.prototype._changeHrToNewDefaultBlock = function(hrSuspect, range, ev, newBlockPosition) { + if (hrSuspect && domUtils.getNodeName(hrSuspect) === 'HR') { + ev.preventDefault(); + + if (newBlockPosition !== 'none') { + this.wwe.breakToNewDefaultBlock(range, newBlockPosition); + } - cursorTarget = hrSuspect.nextSibling; $(hrSuspect).remove(); - range.setStartBefore(cursorTarget); - range.collapse(true); - this.wwe.getEditor().setSelection(range); - return false; } }; diff --git a/apps/editor/src/js/wwTableManager.js b/apps/editor/src/js/wwTableManager.js index e2d745972a..98e6251fd0 100644 --- a/apps/editor/src/js/wwTableManager.js +++ b/apps/editor/src/js/wwTableManager.js @@ -132,7 +132,7 @@ WwTableManager.prototype._isInTable = function(range) { * @returns {boolean} result */ WwTableManager.prototype._isBeforeTable = function(range) { - return domUtils.getNodeName(domUtils.getChildNodeAt(range.startContainer, range.startOffset)) === 'TABLE'; + return domUtils.getNodeName(domUtils.getChildNodeByOffset(range.startContainer, range.startOffset)) === 'TABLE'; }; /** diff --git a/apps/editor/src/js/wwTaskManager.js b/apps/editor/src/js/wwTaskManager.js index 06b0bda3a3..a2ae8f57dc 100644 --- a/apps/editor/src/js/wwTaskManager.js +++ b/apps/editor/src/js/wwTaskManager.js @@ -232,14 +232,14 @@ WwTaskManager.prototype._unformatTaskIfNeedOnBackspace = function(range) { if (domUtils.isElemNode(startContainer)) { //태스크리스트의 제일 첫 오프셋인경우(인풋박스 바로 위) if (startOffset === 0) { - prevEl = domUtils.getChildNodeAt(startContainer, startOffset); + prevEl = domUtils.getChildNodeByOffset(startContainer, startOffset); //inputbox 오른편 어딘가에서 지워지는경우 } else { - prevEl = domUtils.getChildNodeAt(startContainer, startOffset - 1); + prevEl = domUtils.getChildNodeByOffset(startContainer, startOffset - 1); //지워질위치가 인풋스페이스 텍스트 영역으로 의심되는경우 그다음 엘리먼드로 prevEl을 지정해준다.(그다음이 input이면 지워지도록) if (domUtils.isTextNode(prevEl) && prevEl.nodeValue.length === 1 && FIND_TASK_SPACES_RX.test(prevEl.nodeValue)) { - prevEl = domUtils.getChildNodeAt(startContainer, startOffset - 2); + prevEl = domUtils.getChildNodeByOffset(startContainer, startOffset - 2); } } diff --git a/apps/editor/src/js/wysiwygCommands/hr.js b/apps/editor/src/js/wysiwygCommands/hr.js index 1f9acb20d8..3a793529f9 100644 --- a/apps/editor/src/js/wysiwygCommands/hr.js +++ b/apps/editor/src/js/wysiwygCommands/hr.js @@ -5,7 +5,8 @@ 'use strict'; -var CommandManager = require('../commandManager'); +var CommandManager = require('../commandManager'), + domUtils = require('../domUtils'); /** * HR @@ -22,29 +23,32 @@ var HR = CommandManager.command('wysiwyg', /** @lends HR */{ * @param {WysiwygEditor} wwe WYsiwygEditor instance */ exec: function(wwe) { - var sq = wwe.getEditor(); + var sq = wwe.getEditor(), + range = sq.getSelection(), + nextBlockNode; - if (!sq.getSelection().collapsed || sq.hasFormat('TABLE')) { + if (!range.collapsed || sq.hasFormat('TABLE')) { sq.focus(); return; } - sq.modifyBlocks(function(frag) { - /* - var block = sq.createElement('DIV'); - var newFrag = sq._doc.createDocumentFragment(); + nextBlockNode = domUtils.getNextTopBlockNode(domUtils.getChildNodeByOffset(range.startContainer, range.startOffset)); - newFrag.appendChild(frag); - newFrag.appendChild(block); + if (!nextBlockNode) { + nextBlockNode = sq.createDefaultBlock(); + wwe.get$Body().append(nextBlockNode); + } - block.appendChild(sq.createElement('BR')); - block.appendChild(sq.createElement('HR')); -*/ + sq.modifyBlocks(function(frag) { frag.appendChild(sq.createElement('HR')); - return frag; }); + range.selectNodeContents(nextBlockNode); + range.collapse(true); + + sq.setSelection(range); + sq.focus(); } }); diff --git a/apps/editor/src/js/wysiwygEditor.js b/apps/editor/src/js/wysiwygEditor.js index f560b9ec16..7c6bab6772 100644 --- a/apps/editor/src/js/wysiwygEditor.js +++ b/apps/editor/src/js/wysiwygEditor.js @@ -390,13 +390,6 @@ WysiwygEditor.prototype._onKeyDown = function(keyboardEvent) { WysiwygEditor.prototype._initDefaultKeyEventHandler = function() { var self = this; - this.addKeyEventHandler('ENTER', function(ev, range) { - if (self._isInOrphanText(range)) { - self._wrapDefaultBlockTo(range); - return false; - } - }); - this.addKeyEventHandler('BACK_SPACE', function(ev, range) { if (!range.collapsed) { self.postProcessForChange(); @@ -454,7 +447,7 @@ WysiwygEditor.prototype._wrapDefaultBlockTo = function(range) { block = this.getEditor().createDefaultBlock([range.startContainer]); //range for insert block - insertTargetNode = domUtils.getChildNodeAt(range.startContainer, range.startOffset); + insertTargetNode = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset); if (insertTargetNode) { range.setStartBefore(insertTargetNode); } else { @@ -814,7 +807,7 @@ WysiwygEditor.prototype.hasFormatWithRx = function(rx) { WysiwygEditor.prototype.breakToNewDefaultBlock = function(range, where) { var div, pathToBody, appendBefore, currentNode; - currentNode = domUtils.getChildNodeAt(range.startContainer, range.startOffset) || range.startContainer; + currentNode = domUtils.getChildNodeByOffset(range.startContainer, range.startOffset) || domUtils.getChildNodeByOffset(range.startContainer, range.startOffset - 1); pathToBody = $(currentNode).parentsUntil('body'); diff --git a/apps/editor/test/domUtils.spec.js b/apps/editor/test/domUtils.spec.js index b90824b923..ee55f7c58b 100644 --- a/apps/editor/test/domUtils.spec.js +++ b/apps/editor/test/domUtils.spec.js @@ -7,63 +7,6 @@ describe('domUtils', function() { $('body').empty(); }); - describe('getChildNodeAt()', function() { - it('returns childNodes at index', function() { - var result; - - $('body').html([ - '
hi
')[0])).toBe(2); expect(domUtils.getTextLength($('hi
')[0].firstChild)).toBe(2); @@ -149,7 +92,7 @@ describe('domUtils', function() { }); }); - describe('getNodeByOffset()', function() { + describe('getChildNodeByOffset()', function() { it('return node\'s childNode with index', function() { var node = $('textweafwae
'); expect(domUtils.getChildNodeByOffset(node[0], 1)).toBe(node[0].childNodes[1]); @@ -159,6 +102,60 @@ describe('domUtils', function() { var node = $('text
'); expect(domUtils.getChildNodeByOffset(node[0].childNodes[0], 1)).toBe(node[0].childNodes[0]); }); + it('returns childNodes at index', function() { + var result; + + $('body').html([ + 'text2
'); + expect(domUtils.getTopBlockNode($('em')[0].firstChild)).toBe($('div')[0]); + }); + }); + describe('getParentUntil', function() { it('return parent node of passed node until passed parent', function() { $('body').append('aweftext1