/* Copyright (C) NAVER corp. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ if(typeof window.nhn=='undefined'){window.nhn = {};} if (!nhn.husky){nhn.husky = {};} /** * @fileOverview This file contains Husky framework core * @name HuskyCore.js */ (function(){ var _rxMsgHandler = /^\$(LOCAL|BEFORE|ON|AFTER)_/, _rxMsgAppReady = /^\$(BEFORE|ON|AFTER)_MSG_APP_READY$/, _aHuskyCores = [], // HuskyCore instance list _htLoadedFile = {}; // lazy-loaded file list nhn.husky.HuskyCore = jindo.$Class({ name : "HuskyCore", aCallerStack : null, bMobile : jindo.$Agent().navigator().mobile || jindo.$Agent().navigator().msafari, $init : function(htOptions){ this.htOptions = htOptions||{}; _aHuskyCores.push(this); if( this.htOptions.oDebugger ){ nhn.husky.HuskyCore.getCore = function() { return _aHuskyCores; }; this.htOptions.oDebugger.setApp(this); } // To prevent processing a Husky message before all the plugins are registered and ready, // Queue up all the messages here until the application's status is changed to READY this.messageQueue = []; this.oMessageMap = {}; this.oDisabledMessage = {}; this.oLazyMessage = {}; this.aPlugins = []; this.appStatus = nhn.husky.APP_STATUS.NOT_READY; this.aCallerStack = []; this._fnWaitForPluginReady = jindo.$Fn(this._waitForPluginReady, this).bind(); // Register the core as a plugin so it can receive messages this.registerPlugin(this); }, setDebugger: function(oDebugger) { this.htOptions.oDebugger = oDebugger; oDebugger.setApp(this); }, exec : function(msg, args, oEvent){ // If the application is not yet ready just queue the message if(this.appStatus == nhn.husky.APP_STATUS.NOT_READY){ this.messageQueue[this.messageQueue.length] = {'msg':msg, 'args':args, 'event':oEvent}; return true; } this.exec = this._exec; this.exec(msg, args, oEvent); }, delayedExec : function(msg, args, nDelay, oEvent){ var fExec = jindo.$Fn(this.exec, this).bind(msg, args, oEvent); setTimeout(fExec, nDelay); }, _exec : function(msg, args, oEvent){ return (this._exec = this.htOptions.oDebugger?this._execWithDebugger:this._execWithoutDebugger).call(this, msg, args, oEvent); }, _execWithDebugger : function(msg, args, oEvent){ this.htOptions.oDebugger.log_MessageStart(msg, args); var bResult = this._doExec(msg, args, oEvent); this.htOptions.oDebugger.log_MessageEnd(msg, args); return bResult; }, _execWithoutDebugger : function(msg, args, oEvent){ return this._doExec(msg, args, oEvent); }, _doExec : function(msg, args, oEvent){ var bContinue = false; // Lazy메시지가 있으면 파일을 로딩한다. if(this.oLazyMessage[msg]){ var htLazyInfo = this.oLazyMessage[msg]; this._loadLazyFiles(msg, args, oEvent, htLazyInfo.aFilenames, 0); return false; } if(!this.oDisabledMessage[msg]){ var allArgs = []; if(args && args.length){ var iLen = args.length; for(var i=0; i= 0 && nhn.DOMFix.parentNode(aAllNodes[iChildIdx]) == aAllNodes[iCurIdx]){ iChildIdx = this._recurConstructClonedTree(aAllNodes, iChildIdx, oCurNodeCloneWithChildren); } // this may trigger an error message in IE when an erroneous script is inserted oClonedParentNode.insertBefore(oCurNodeCloneWithChildren, oClonedParentNode.firstChild); return iChildIdx; }; this._recurConstructClonedTree = _recurConstructClonedTree; aNodes[aNodes.length] = nhn.DOMFix.parentNode(aNodes[aNodes.length-1]); this._recurConstructClonedTree(aNodes, aNodes.length-1, oClonedParentNode); return {oStartContainer: oClonedStartContainer, oEndContainer: oClonedEndContainer}; }, cloneRange : function(){ return this._copyRange(new nhn.W3CDOMRange(this._window)); }, _copyRange : function(oClonedRange){ oClonedRange.collapsed = this.collapsed; oClonedRange.commonAncestorContainer = this.commonAncestorContainer; oClonedRange.endContainer = this.endContainer; oClonedRange.endOffset = this.endOffset; oClonedRange.startContainer = this.startContainer; oClonedRange.startOffset = this.startOffset; oClonedRange._document = this._document; return oClonedRange; }, collapse : function(toStart){ if(toStart){ this.endContainer = this.startContainer; this.endOffset = this.startOffset; }else{ this.startContainer = this.endContainer; this.startOffset = this.endOffset; } this._updateRangeInfo(); }, compareBoundaryPoints : function(how, sourceRange){ switch(how){ case nhn.W3CDOMRange.START_TO_START: return this._compareEndPoint(this.startContainer, this.startOffset, sourceRange.startContainer, sourceRange.startOffset); case nhn.W3CDOMRange.START_TO_END: return this._compareEndPoint(this.endContainer, this.endOffset, sourceRange.startContainer, sourceRange.startOffset); case nhn.W3CDOMRange.END_TO_END: return this._compareEndPoint(this.endContainer, this.endOffset, sourceRange.endContainer, sourceRange.endOffset); case nhn.W3CDOMRange.END_TO_START: return this._compareEndPoint(this.startContainer, this.startOffset, sourceRange.endContainer, sourceRange.endOffset); } }, _findBody : function(oNode){ if(!oNode){return null;} while(oNode){ if(oNode.tagName == "BODY"){return oNode;} oNode = nhn.DOMFix.parentNode(oNode); } return null; }, _compareEndPoint : function(oContainerA, iOffsetA, oContainerB, iOffsetB){ return this.oBrowserSelection.compareEndPoints(oContainerA, iOffsetA, oContainerB, iOffsetB); var iIdxA, iIdxB; if(!oContainerA || this._findBody(oContainerA) != this._document.body){ oContainerA = this._document.body; iOffsetA = 0; } if(!oContainerB || this._findBody(oContainerB) != this._document.body){ oContainerB = this._document.body; iOffsetB = 0; } var compareIdx = function(iIdxA, iIdxB){ // iIdxX == -1 when the node is the commonAncestorNode // if iIdxA == -1 // -> [[...]]... // if iIdxB == -1 // -> ...[[...]] if(iIdxB == -1){iIdxB = iIdxA+1;} if(iIdxA < iIdxB){return -1;} if(iIdxA == iIdxB){return 0;} return 1; }; var oCommonAncestor = this._getCommonAncestorContainer(oContainerA, oContainerB); // ================================================================================================================================================ // Move up both containers so that both containers are direct child nodes of the common ancestor node. From there, just compare the offset // Add 0.5 for each contaienrs that has "moved up" since the actual node is wrapped by 1 or more parent nodes and therefore its position is somewhere between idx & idx+1 // NODE1

NODE2

NODE3
// The position of NODE2 in COMMON_ANCESTOR is somewhere between after NODE1(idx1) and before NODE3(idx2), so we let that be 1.5 // container node A in common ancestor container var oNodeA = oContainerA; var oTmpNode = null; if(oNodeA != oCommonAncestor){ while((oTmpNode = nhn.DOMFix.parentNode(oNodeA)) != oCommonAncestor){oNodeA = oTmpNode;} iIdxA = this._getPosIdx(oNodeA)+0.5; }else{ iIdxA = iOffsetA; } // container node B in common ancestor container var oNodeB = oContainerB; if(oNodeB != oCommonAncestor){ while((oTmpNode = nhn.DOMFix.parentNode(oNodeB)) != oCommonAncestor){oNodeB = oTmpNode;} iIdxB = this._getPosIdx(oNodeB)+0.5; }else{ iIdxB = iOffsetB; } return compareIdx(iIdxA, iIdxB); }, _getCommonAncestorContainer : function(oNode1, oNode2){ oNode1 = oNode1 || this.startContainer; oNode2 = oNode2 || this.endContainer; var oComparingNode = oNode2; while(oNode1){ while(oComparingNode){ if(oNode1 == oComparingNode){return oNode1;} oComparingNode = nhn.DOMFix.parentNode(oComparingNode); } oComparingNode = oNode2; oNode1 = nhn.DOMFix.parentNode(oNode1); } return this._document.body; }, deleteContents : function(){ if(this.collapsed){return;} this._splitTextEndNodesOfTheRange(); var aNodes = this._getNodesInRange(); if(aNodes.length < 1){return;} var oPrevNode = aNodes[0].previousSibling; while(oPrevNode && this._isBlankTextNode(oPrevNode)){oPrevNode = oPrevNode.previousSibling;} var oNewStartContainer, iNewOffset = -1; if(!oPrevNode){ oNewStartContainer = nhn.DOMFix.parentNode(aNodes[0]); iNewOffset = 0; } for(var i=0; iA]B // -> []B // without these lines, the result would yeild to // -> []B if(oNewStartContainer == oNode && iNewOffset === 0){ iNewOffset = this._getPosIdx(oNewStartContainer); oNewStartContainer = nhn.DOMFix.parentNode(oNode); } } } if(!oPrevNode){ this.setStart(oNewStartContainer, iNewOffset, true, true); }else{ if(oPrevNode.tagName == "BODY"){ this.setStartBefore(oPrevNode, true); }else{ this.setStartAfter(oPrevNode, true); } } this.collapse(true); }, extractContents : function(){ var oClonedContents = this.cloneContents(); this.deleteContents(); return oClonedContents; }, getInsertBeforeNodes : function(){ var oFirstNode = null; var oParentContainer; if(this.startContainer.nodeType == "3"){ oParentContainer = nhn.DOMFix.parentNode(this.startContainer); if(this.startContainer.nodeValue.length <= this.startOffset){ oFirstNode = this.startContainer.nextSibling; }else{ oFirstNode = this.startContainer.splitText(this.startOffset); } }else{ oParentContainer = this.startContainer; oFirstNode = nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; } if(!oFirstNode || !nhn.DOMFix.parentNode(oFirstNode)){oFirstNode = null;} return {elParent: oParentContainer, elBefore: oFirstNode}; }, insertNode : function(newNode){ var oInsertBefore = this.getInsertBeforeNodes(); oInsertBefore.elParent.insertBefore(newNode, oInsertBefore.elBefore); this.setStartBefore(newNode); }, selectNode : function(refNode){ this.reset(this._window); this.setStartBefore(refNode); this.setEndAfter(refNode); }, selectNodeContents : function(refNode){ this.reset(this._window); this.setStart(refNode, 0, true); this.setEnd(refNode, nhn.DOMFix.childNodes(refNode).length); }, _endsNodeValidation : function(oNode, iOffset){ if(!oNode || this._findBody(oNode) != this._document.body){throw new Error("INVALID_NODE_TYPE_ERR oNode is not part of current document");} if(oNode.nodeType == 3){ if(iOffset > oNode.nodeValue.length){iOffset = oNode.nodeValue.length;} }else{ if(iOffset > nhn.DOMFix.childNodes(oNode).length){iOffset = nhn.DOMFix.childNodes(oNode).length;} } return iOffset; }, setEnd : function(refNode, offset, bSafe, bNoUpdate){ if(!bSafe){offset = this._endsNodeValidation(refNode, offset);} this.endContainer = refNode; this.endOffset = offset; if(!bNoUpdate){ if(!this.startContainer || this._compareEndPoint(this.startContainer, this.startOffset, this.endContainer, this.endOffset) != -1){ this.collapse(false); }else{ this._updateRangeInfo(); } } }, setEndAfter : function(refNode, bNoUpdate){ if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setEndAfter");} if(refNode.tagName == "BODY"){ this.setEnd(refNode, nhn.DOMFix.childNodes(refNode).length, true, bNoUpdate); return; } this.setEnd(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode)+1, true, bNoUpdate); }, setEndBefore : function(refNode, bNoUpdate){ if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setEndBefore");} if(refNode.tagName == "BODY"){ this.setEnd(refNode, 0, true, bNoUpdate); return; } this.setEnd(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode), true, bNoUpdate); }, setStart : function(refNode, offset, bSafe, bNoUpdate){ if(!bSafe){offset = this._endsNodeValidation(refNode, offset);} this.startContainer = refNode; this.startOffset = offset; if(!bNoUpdate){ if(!this.endContainer || this._compareEndPoint(this.startContainer, this.startOffset, this.endContainer, this.endOffset) != -1){ this.collapse(true); }else{ this._updateRangeInfo(); } } }, setStartAfter : function(refNode, bNoUpdate){ if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setStartAfter");} if(refNode.tagName == "BODY"){ this.setStart(refNode, nhn.DOMFix.childNodes(refNode).length, true, bNoUpdate); return; } this.setStart(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode)+1, true, bNoUpdate); }, setStartBefore : function(refNode, bNoUpdate){ if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setStartBefore");} if(refNode.tagName == "BODY"){ this.setStart(refNode, 0, true, bNoUpdate); return; } this.setStart(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode), true, bNoUpdate); }, surroundContents : function(newParent){ newParent.appendChild(this.extractContents()); this.insertNode(newParent); this.selectNode(newParent); }, toString : function(){ var oTmpContainer = this._document.createElement("DIV"); oTmpContainer.appendChild(this.cloneContents()); return oTmpContainer.textContent || oTmpContainer.innerText || ""; }, // this.oBrowserSelection.getCommonAncestorContainer which uses browser's built-in API runs faster but may return an incorrect value. // Call this function to fix the problem. // // In IE, the built-in API would return an incorrect value when, // 1. commonAncestorContainer is not selectable // AND // 2. The selected area will look the same when its child node is selected // eg) // when

TEST

is selected, TEST will be returned as commonAncestorContainer fixCommonAncestorContainer : function(){ if(!jindo.$Agent().navigator().ie){ return; } this.commonAncestorContainer = this._getCommonAncestorContainer(); }, _isBlankTextNode : function(oNode){ if(oNode.nodeType == 3 && oNode.nodeValue == ""){return true;} return false; }, _isAllChildBlankText : function(elNode){ for(var i=0, nLen=elNode.childNodes.length; i // // // // // // // // // // // // , it's easier to compare the position of B and D rather than C and F because they are siblings // // If the range were collapsed, oActualEndNode will precede oActualStartNode by doing this oActualStartNode = this._getNextNode(this._getPrevNode(oActualStartNode)); oActualEndNode = this._getPrevNode(this._getNextNode(oActualEndNode)); if(oActualStartNode && oActualEndNode && oActualEndNode.tagName != "BODY" && (this._getNextNode(oActualEndNode) == oActualStartNode || (oActualEndNode == oActualStartNode && this._isBlankTextNode(oActualEndNode))) ){ bCollapsed = true; } } return bCollapsed; }, _splitTextEndNodesOfTheRange : function(){ var oEndPoints = this._splitTextEndNodes({oStartContainer: this.startContainer, iStartOffset: this.startOffset, oEndContainer: this.endContainer, iEndOffset: this.endOffset}); this.startContainer = oEndPoints.oStartContainer; this.startOffset = oEndPoints.iStartOffset; this.endContainer = oEndPoints.oEndContainer; this.endOffset = oEndPoints.iEndOffset; }, _splitTextEndNodes : function(oEndPoints){ oEndPoints = this._splitStartTextNode(oEndPoints); oEndPoints = this._splitEndTextNode(oEndPoints); return oEndPoints; }, _splitStartTextNode : function(oEndPoints){ var oStartContainer = oEndPoints.oStartContainer; var iStartOffset = oEndPoints.iStartOffset; var oEndContainer = oEndPoints.oEndContainer; var iEndOffset = oEndPoints.iEndOffset; if(!oStartContainer){return oEndPoints;} if(oStartContainer.nodeType != 3){return oEndPoints;} if(iStartOffset === 0){return oEndPoints;} if(oStartContainer.nodeValue.length <= iStartOffset){return oEndPoints;} var oLastPart = oStartContainer.splitText(iStartOffset); if(oStartContainer == oEndContainer){ iEndOffset -= iStartOffset; oEndContainer = oLastPart; } oStartContainer = oLastPart; iStartOffset = 0; return {oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset}; }, _splitEndTextNode : function(oEndPoints){ var oStartContainer = oEndPoints.oStartContainer; var iStartOffset = oEndPoints.iStartOffset; var oEndContainer = oEndPoints.oEndContainer; var iEndOffset = oEndPoints.iEndOffset; if(!oEndContainer){return oEndPoints;} if(oEndContainer.nodeType != 3){return oEndPoints;} if(iEndOffset >= oEndContainer.nodeValue.length){return oEndPoints;} if(iEndOffset === 0){return oEndPoints;} oEndContainer.splitText(iEndOffset); return {oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset}; }, _getNodesInRange : function(){ if(this.collapsed){return [];} var oStartNode = this._getActualStartNode(this.startContainer, this.startOffset); var oEndNode = this._getActualEndNode(this.endContainer, this.endOffset); return this._getNodesBetween(oStartNode, oEndNode); }, _getActualStartNode : function(oStartContainer, iStartOffset){ var oStartNode = oStartContainer; if(oStartContainer.nodeType == 3){ if(iStartOffset >= oStartContainer.nodeValue.length){ oStartNode = this._getNextNode(oStartContainer); if(oStartNode.tagName == "BODY"){oStartNode = null;} }else{ oStartNode = oStartContainer; } }else{ if(iStartOffset < nhn.DOMFix.childNodes(oStartContainer).length){ oStartNode = nhn.DOMFix.childNodes(oStartContainer)[iStartOffset]; }else{ oStartNode = this._getNextNode(oStartContainer); if(oStartNode.tagName == "BODY"){oStartNode = null;} } } return oStartNode; }, _getActualEndNode : function(oEndContainer, iEndOffset){ var oEndNode = oEndContainer; if(iEndOffset === 0){ oEndNode = this._getPrevNode(oEndContainer); if(oEndNode.tagName == "BODY"){oEndNode = null;} }else if(oEndContainer.nodeType == 3){ oEndNode = oEndContainer; }else{ oEndNode = nhn.DOMFix.childNodes(oEndContainer)[iEndOffset-1]; } return oEndNode; }, _getNextNode : function(oNode){ if(!oNode || oNode.tagName == "BODY"){return this._document.body;} if(oNode.nextSibling){return oNode.nextSibling;} return this._getNextNode(nhn.DOMFix.parentNode(oNode)); }, _getPrevNode : function(oNode){ if(!oNode || oNode.tagName == "BODY"){return this._document.body;} if(oNode.previousSibling){return oNode.previousSibling;} return this._getPrevNode(nhn.DOMFix.parentNode(oNode)); }, // includes partially selected // for
, _getNodesBetween(b, c) will yield to b, "a" and c _getNodesBetween : function(oStartNode, oEndNode){ var aNodesBetween = []; this._nNodesBetweenLen = 0; if(!oStartNode || !oEndNode){return aNodesBetween;} // IE may throw an exception on "oCurNode = oCurNode.nextSibling;" when oCurNode is 'invalid', not null or undefined but somehow 'invalid'. // It happened during browser's build-in UNDO with control range selected(table). try{ this._recurGetNextNodesUntil(oStartNode, oEndNode, aNodesBetween); }catch(e){ return []; } return aNodesBetween; }, _recurGetNextNodesUntil : function(oNode, oEndNode, aNodesBetween){ if(!oNode){return false;} if(!this._recurGetChildNodesUntil(oNode, oEndNode, aNodesBetween)){return false;} var oNextToChk = oNode.nextSibling; while(!oNextToChk){ if(!(oNode = nhn.DOMFix.parentNode(oNode))){return false;} aNodesBetween[this._nNodesBetweenLen++] = oNode; if(oNode == oEndNode){return false;} oNextToChk = oNode.nextSibling; } return this._recurGetNextNodesUntil(oNextToChk, oEndNode, aNodesBetween); }, _recurGetChildNodesUntil : function(oNode, oEndNode, aNodesBetween){ if(!oNode){return false;} var bEndFound = false; var oCurNode = oNode; if(oCurNode.firstChild){ oCurNode = oCurNode.firstChild; while(oCurNode){ if(!this._recurGetChildNodesUntil(oCurNode, oEndNode, aNodesBetween)){ bEndFound = true; break; } oCurNode = oCurNode.nextSibling; } } aNodesBetween[this._nNodesBetweenLen++] = oNode; if(bEndFound){return false;} if(oNode == oEndNode){return false;} return true; } }); nhn.W3CDOMRange.START_TO_START = 0; nhn.W3CDOMRange.START_TO_END = 1; nhn.W3CDOMRange.END_TO_END = 2; nhn.W3CDOMRange.END_TO_START = 3; /** * @fileOverview This file contains a cross-browser function that implements all of the W3C's DOM Range specification and some more * @name HuskyRange.js */ nhn.HuskyRange = jindo.$Class({ _rxCursorHolder : /^(?:\uFEFF|\u00A0|\u200B|
)$/i, _rxTextAlign : /text-align:[^"';]*;?/i, setWindow : function(win){ this.reset(win || window); }, $init : function(win){ this.HUSKY_BOOMARK_START_ID_PREFIX = "husky_bookmark_start_"; this.HUSKY_BOOMARK_END_ID_PREFIX = "husky_bookmark_end_"; this.sBlockElement = "P|DIV|LI|H[1-6]|PRE"; this.sBlockContainer = "BODY|TABLE|TH|TR|TD|UL|OL|BLOCKQUOTE|FORM"; this.rxBlockElement = new RegExp("^("+this.sBlockElement+")$"); this.rxBlockContainer = new RegExp("^("+this.sBlockContainer+")$"); this.rxLineBreaker = new RegExp("^("+this.sBlockElement+"|"+this.sBlockContainer+")$"); this.rxHasBlock = new RegExp("(?:<(?:"+this.sBlockElement+"|"+this.sBlockContainer+").*?>|style=[\"']?[^>]*?(?:display\\s?:\\s?block)[^>]*?[\"']?)", "i"); this.setWindow(win); }, select : function(){ try{ this.oBrowserSelection.selectRange(this); }catch(e){} }, setFromSelection : function(iNum){ this.setRange(this.oBrowserSelection.getRangeAt(iNum), true); }, setRange : function(oW3CRange, bSafe){ this.reset(this._window); this.setStart(oW3CRange.startContainer, oW3CRange.startOffset, bSafe, true); this.setEnd(oW3CRange.endContainer, oW3CRange.endOffset, bSafe); }, setEndNodes : function(oSNode, oENode){ this.reset(this._window); this.setEndAfter(oENode, true); this.setStartBefore(oSNode); }, splitTextAtBothEnds : function(){ this._splitTextEndNodesOfTheRange(); }, getStartNode : function(){ if(this.collapsed){ if(this.startContainer.nodeType == 3){ if(this.startOffset === 0){return null;} if(this.startContainer.nodeValue.length <= this.startOffset){return null;} return this.startContainer; } return null; } if(this.startContainer.nodeType == 3){ if(this.startOffset >= this.startContainer.nodeValue.length){return this._getNextNode(this.startContainer);} return this.startContainer; }else{ if(this.startOffset >= nhn.DOMFix.childNodes(this.startContainer).length){return this._getNextNode(this.startContainer);} return nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; } }, getEndNode : function(){ if(this.collapsed){return this.getStartNode();} if(this.endContainer.nodeType == 3){ if(this.endOffset === 0){return this._getPrevNode(this.endContainer);} return this.endContainer; }else{ if(this.endOffset === 0){return this._getPrevNode(this.endContainer);} return nhn.DOMFix.childNodes(this.endContainer)[this.endOffset-1]; } }, getNodeAroundRange : function(bBefore, bStrict){ if(!this.collapsed){return this.getStartNode();} if(this.startContainer && this.startContainer.nodeType == 3){return this.startContainer;} //if(this.collapsed && this.startContainer && this.startContainer.nodeType == 3) return this.startContainer; //if(!this.collapsed || (this.startContainer && this.startContainer.nodeType == 3)) return this.getStartNode(); var oBeforeRange, oAfterRange, oResult; if(this.startOffset >= nhn.DOMFix.childNodes(this.startContainer).length){ oAfterRange = this._getNextNode(this.startContainer); }else{ oAfterRange = nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; } if(this.endOffset === 0){ oBeforeRange = this._getPrevNode(this.endContainer); }else{ oBeforeRange = nhn.DOMFix.childNodes(this.endContainer)[this.endOffset-1]; } if(bBefore){ oResult = oBeforeRange; if(!oResult && !bStrict){oResult = oAfterRange;} }else{ oResult = oAfterRange; if(!oResult && !bStrict){oResult = oBeforeRange;} } return oResult; }, _getXPath : function(elNode){ var sXPath = ""; while(elNode && elNode.nodeType == 1){ sXPath = "/" + elNode.tagName+"["+this._getPosIdx4XPath(elNode)+"]" + sXPath; elNode = nhn.DOMFix.parentNode(elNode); } return sXPath; }, _getPosIdx4XPath : function(refNode){ var idx = 0; for(var node = refNode.previousSibling; node; node = node.previousSibling){ if(node.tagName == refNode.tagName){idx++;} } return idx; }, // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes _evaluateXPath : function(sXPath, oDoc){ sXPath = sXPath.substring(1, sXPath.length-1); var aXPath = sXPath.split(/\//); var elNode = oDoc.body; for(var i=2; i -1 && elContainer){ var aChildNodes = nhn.DOMFix.childNodes(elContainer); var elNode = null; var nIdx = nTextNodeIdx; var nOffsetLeft = nOffset; while((elNode = aChildNodes[nIdx]) && elNode.nodeType == 3 && elNode.nodeValue.length < nOffsetLeft){ nOffsetLeft -= elNode.nodeValue.length; nIdx++; } elContainer = nhn.DOMFix.childNodes(elContainer)[nIdx]; nOffset = nOffsetLeft; } if(!elContainer){ elContainer = this._document.body; nOffset = 0; } return {elContainer: elContainer, nOffset: nOffset}; }, // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes getXPathBookmark : function(){ var nTextNodeIdx1 = -1; var htEndPt1 = {elContainer: this.startContainer, nOffset: this.startOffset}; var elNode1 = this.startContainer; if(elNode1.nodeType == 3){ htEndPt1 = this._getFixedStartTextNode(); nTextNodeIdx1 = this._getPosIdx(htEndPt1.elContainer); elNode1 = nhn.DOMFix.parentNode(elNode1); } var sXPathNode1 = this._getXPath(elNode1); var oBookmark1 = {sXPath:sXPathNode1, nTextNodeIdx:nTextNodeIdx1, nOffset: htEndPt1.nOffset}; if(this.collapsed){ var oBookmark2 = {sXPath:sXPathNode1, nTextNodeIdx:nTextNodeIdx1, nOffset: htEndPt1.nOffset}; }else{ var nTextNodeIdx2 = -1; var htEndPt2 = {elContainer: this.endContainer, nOffset: this.endOffset}; var elNode2 = this.endContainer; if(elNode2.nodeType == 3){ htEndPt2 = this._getFixedEndTextNode(); nTextNodeIdx2 = this._getPosIdx(htEndPt2.elContainer); elNode2 = nhn.DOMFix.parentNode(elNode2); } var sXPathNode2 = this._getXPath(elNode2); var oBookmark2 = {sXPath:sXPathNode2, nTextNodeIdx:nTextNodeIdx2, nOffset: htEndPt2.nOffset}; } return [oBookmark1, oBookmark2]; }, moveToXPathBookmark : function(aBookmark){ if(!aBookmark){return false;} var oBookmarkInfo1 = this._evaluateXPathBookmark(aBookmark[0]); var oBookmarkInfo2 = this._evaluateXPathBookmark(aBookmark[1]); if(!oBookmarkInfo1["elContainer"] || !oBookmarkInfo2["elContainer"]){return;} this.startContainer = oBookmarkInfo1["elContainer"]; this.startOffset = oBookmarkInfo1["nOffset"]; this.endContainer = oBookmarkInfo2["elContainer"]; this.endOffset = oBookmarkInfo2["nOffset"]; return true; }, _getFixedTextContainer : function(elNode, nOffset){ while(elNode && elNode.nodeType == 3 && elNode.previousSibling && elNode.previousSibling.nodeType == 3){ nOffset += elNode.previousSibling.nodeValue.length; elNode = elNode.previousSibling; } return {elContainer:elNode, nOffset:nOffset}; }, _getFixedStartTextNode : function(){ return this._getFixedTextContainer(this.startContainer, this.startOffset); }, _getFixedEndTextNode : function(){ return this._getFixedTextContainer(this.endContainer, this.endOffset); }, placeStringBookmark : function(){ if(this.collapsed || jindo.$Agent().navigator().ie || jindo.$Agent().navigator().firefox){ return this.placeStringBookmark_NonWebkit(); }else{ return this.placeStringBookmark_Webkit(); } }, placeStringBookmark_NonWebkit : function(){ var sTmpId = (new Date()).getTime(); var oInsertionPoint = this.cloneRange(); oInsertionPoint.collapseToEnd(); var oEndMarker = this._document.createElement("SPAN"); oEndMarker.id = this.HUSKY_BOOMARK_END_ID_PREFIX+sTmpId; oInsertionPoint.insertNode(oEndMarker); var oInsertionPoint = this.cloneRange(); oInsertionPoint.collapseToStart(); var oStartMarker = this._document.createElement("SPAN"); oStartMarker.id = this.HUSKY_BOOMARK_START_ID_PREFIX+sTmpId; oInsertionPoint.insertNode(oStartMarker); // IE에서 빈 SPAN의 앞뒤로 커서가 이동하지 않아 문제가 발생 할 수 있어, 보이지 않는 특수 문자를 임시로 넣어 줌. if(jindo.$Agent().navigator().ie){ // SPAN의 위치가 TD와 TD 사이에 있을 경우, 텍스트 삽입 시 알수 없는 오류가 발생한다. // TD와 TD사이에서는 텍스트 삽입이 필요 없음으로 그냥 try/catch로 처리 try{ oStartMarker.innerHTML = unescape("%uFEFF"); }catch(e){} try{ oEndMarker.innerHTML = unescape("%uFEFF"); }catch(e){} } this.moveToBookmark(sTmpId); return sTmpId; }, placeStringBookmark_Webkit : function(){ var sTmpId = (new Date()).getTime(); var elInsertBefore, elInsertParent; // Do not insert the bookmarks between TDs as it will break the rendering in Chrome/Safari // -> modify the insertion position from [abc]abc to [abc]abc var oInsertionPoint = this.cloneRange(); oInsertionPoint.collapseToEnd(); elInsertBefore = this._document.createTextNode(""); oInsertionPoint.insertNode(elInsertBefore); elInsertParent = elInsertBefore.parentNode; if(elInsertBefore.previousSibling && elInsertBefore.previousSibling.tagName == "TD"){ elInsertParent = elInsertBefore.previousSibling; elInsertBefore = null; } var oEndMarker = this._document.createElement("SPAN"); oEndMarker.id = this.HUSKY_BOOMARK_END_ID_PREFIX+sTmpId; elInsertParent.insertBefore(oEndMarker, elInsertBefore); var oInsertionPoint = this.cloneRange(); oInsertionPoint.collapseToStart(); elInsertBefore = this._document.createTextNode(""); oInsertionPoint.insertNode(elInsertBefore); elInsertParent = elInsertBefore.parentNode; if(elInsertBefore.nextSibling && elInsertBefore.nextSibling.tagName == "TD"){ elInsertParent = elInsertBefore.nextSibling; elInsertBefore = elInsertParent.firstChild; } var oStartMarker = this._document.createElement("SPAN"); oStartMarker.id = this.HUSKY_BOOMARK_START_ID_PREFIX+sTmpId; elInsertParent.insertBefore(oStartMarker, elInsertBefore); //elInsertBefore.parentNode.removeChild(elInsertBefore); this.moveToBookmark(sTmpId); return sTmpId; }, cloneRange : function(){ return this._copyRange(new nhn.HuskyRange(this._window)); }, moveToBookmark : function(vBookmark){ if(typeof(vBookmark) != "object"){ return this.moveToStringBookmark(vBookmark); }else{ return this.moveToXPathBookmark(vBookmark); } }, getStringBookmark : function(sBookmarkID, bEndBookmark){ if(bEndBookmark){ return this._document.getElementById(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); }else{ return this._document.getElementById(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); } }, moveToStringBookmark : function(sBookmarkID, bIncludeBookmark){ var oStartMarker = this.getStringBookmark(sBookmarkID); var oEndMarker = this.getStringBookmark(sBookmarkID, true); if(!oStartMarker || !oEndMarker){return false;} this.reset(this._window); if(bIncludeBookmark){ this.setEndAfter(oEndMarker); this.setStartBefore(oStartMarker); }else{ this.setEndBefore(oEndMarker); this.setStartAfter(oStartMarker); } return true; }, removeStringBookmark : function(sBookmarkID){ /* var oStartMarker = this._document.getElementById(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); var oEndMarker = this._document.getElementById(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); if(oStartMarker) nhn.DOMFix.parentNode(oStartMarker).removeChild(oStartMarker); if(oEndMarker) nhn.DOMFix.parentNode(oEndMarker).removeChild(oEndMarker); */ this._removeAll(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); this._removeAll(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); }, _removeAll : function(sID){ var elNode; while((elNode = this._document.getElementById(sID))){ elNode.parentNode.removeChild(elNode); } }, collapseToStart : function(){ this.collapse(true); }, collapseToEnd : function(){ this.collapse(false); }, createAndInsertNode : function(sTagName){ var tmpNode = this._document.createElement(sTagName); this.insertNode(tmpNode); return tmpNode; }, getNodes : function(bSplitTextEndNodes, fnFilter){ if(bSplitTextEndNodes){this._splitTextEndNodesOfTheRange();} var aAllNodes = this._getNodesInRange(); var aFilteredNodes = []; if(!fnFilter){return aAllNodes;} for(var i=0; i= 0){return true;} if(bIncludePartlySelected){ if(startToEnd == 1){return false;} if(endToStart == -1){return false;} return true; } return false; }, isNodeInRange : function(oNode, bIncludePartlySelected, bContentOnly){ var oTmpRange = new nhn.HuskyRange(this._window); if(bContentOnly && oNode.firstChild){ oTmpRange.setStartBefore(oNode.firstChild); oTmpRange.setEndAfter(oNode.lastChild); }else{ oTmpRange.selectNode(oNode); } return this.isRangeInRange(oTmpRange, bIncludePartlySelected); }, pasteText : function(sText){ this.pasteHTML(sText.replace(/&/g, "&").replace(//g, ">").replace(/ /g, " ").replace(/"/g, """)); }, /** * TODO: 왜 clone 으로 조작할까? * @param {String} sHTML 삽입할 HTML * @param {Boolean} bBlock HTML 삽입시 강제로 block 요소 처리할지 여부(true 이면 P태그 안에 삽입될 경우, P태그를 무조건 쪼개고 그 사이에 DIV태그로 감싸서 삽입한다.) */ pasteHTML : function(sHTML, bBlock){ var oTmpDiv = this._document.createElement("DIV"); oTmpDiv.innerHTML = sHTML; if(!oTmpDiv.firstChild){ this.deleteContents(); return; } // getLineInfo 전에 북마크를 삽입하지 않으면 IE에서 oLineBreaker가 P태그 바깥으로 잡히는 경우가 있음(P태그에 아무것도 없을때) var clone = this.cloneRange(); var sBM = clone.placeStringBookmark(); // [SMARTEDITORSUS-1960] PrivateTag, 템플릿삽입등 P태그안에 block 요소 삽입과 관련된 처리 // P태그인 경우, block요소가 들어오면 안된다. // 때문에 현재 위치의 컨테이너가 P태그이고 컨텐츠 내용이 block 요소인 경우 P태그를 쪼개고 그 사이에 컨텐츠를 DIV로 감싸서 넣도록 처리한다. // [SMARTEDITORSUS-2026][SMARTEDITORSUS-2061] bBlock = true 이면 삽입되는 HTML 이 block 요소가 아니더라도 무조건 P태그를 쪼개서 DIV로 감싸도록 한다. var oLineInfo = clone.getLineInfo(), oStart = oLineInfo.oStart, oEnd = oLineInfo.oEnd; if(oStart.oLineBreaker && oStart.oLineBreaker.nodeName === "P" && (bBlock || clone.rxHasBlock.test(sHTML))){ // [SMARTEDITORSUS-2169] 선택영역 삭제시 oStart.oLineBreaker도 DOM 트리에서 제거될 수 있기 때문에 필요한 노드를 미리 참조해둔다. var oParentNode = oStart.oLineBreaker.parentNode, oNextSibling = oStart.oLineBreaker.nextSibling; // 선택영역을 조작해야 하므로 현재 선택된 요소들을 제거한다. clone.deleteContents(); // 동일한 라인에 있으면 뒷부분은 쪼개서 다음 라인으로 삽입한다. if(oStart.oLineBreaker === oEnd.oLineBreaker){ var elBM = clone.getStringBookmark(sBM); clone.setEndNodes(elBM, oEnd.oLineBreaker); var oNextContents = clone.extractContents(), oNextFirst = oNextContents.firstChild; // oNextSibling 을 교체하기 위해 쪼개진 요소 첫번째 노드를 미리 참조해둔다. // 쪼갠 부분을 삽입하고 if(oNextSibling){ oParentNode.insertBefore(oNextContents, oNextSibling); }else{ oParentNode.appendChild(oNextContents); } // [SMARTEDITORSUS-2145] oNextSibling 을 쪼갠 부분의 첫번째 노드로 교체한다. oNextSibling = oNextFirst; } // 선택영역 앞쪽이 속한 P태그에서 style과 align 정보를 복사한다. // 크롬의 경우 div의 style 에 text-align 이 있으면 align 속성은 무시되는데 // div 안의 block 요소는 text-align 의 대상이 아니라 정렬되지 않는 문제가 있기 때문에 // style 복사할 때 text-align 속성은 제외한다. oTmpDiv.style.cssText = oStart.oLineBreaker.style.cssText.replace(this._rxTextAlign, ''); // text-align 제외 oTmpDiv.align = oStart.oLineBreaker.align; // align 복사 // 컨텐츠 삽입 if(oNextSibling){ oParentNode.insertBefore(oTmpDiv, oNextSibling); }else{ oParentNode.appendChild(oTmpDiv); } // 컨텐츠 삽입 후에 북마크를 지운다. // 컨텐츠 삽입 전에 지우면 컨텐츠 삽입시 oNextSibling 가 북마크로 잡히는 경우 에러가 발생할 수 있음 clone.removeStringBookmark(sBM); // 컨텐츠 삽입 후 윗라인 P태그에 아무런 내용이 없으면 제거한다. this._removeEmptyP(this._getPrevElement(oTmpDiv)); // 아래 라인 P태그에 아무런 내용이 없는 경우는 그 다음 아래 라인이 있을때만 제거한다. // 아래 라인이 아예없으면 IE에서 커서가 들어가지 않기 때문에 라인을 추가해준다. var elNextLine = this._getNextElement(oTmpDiv); if(elNextLine){ var elAfterNext = this._getNextElement(elNextLine); if(elAfterNext && this._removeEmptyP(elNextLine)){ elNextLine = elAfterNext; // 제거되었을 경우만 elNextLine 재할당 } }else{ // 아래 라인이 없으면 윗 라인 스타일을 복사하여 추가해준다. elNextLine = this._document.createElement("P"); elNextLine.style.cssText = oStart.oLineBreaker.style.cssText; elNextLine.align = oStart.oLineBreaker.align; oParentNode.appendChild(elNextLine); } // 커서를 다음라인으로 앞쪽으로 위치시킨다. if(elNextLine.innerHTML === ""){ // 크롬에서 빈

를 선택해서 collapseToStart 하면 윗라인으로 이동하기 때문에 비어있으면 \uFEFF 를 넣어준다. elNextLine.innerHTML = (jindo.$Agent().navigator().ie && jindo.$Agent().navigator().version > 8) ? "\u200B" : "\uFEFF"; } this.selectNodeContents(elNextLine); this.collapseToStart(); // IE7에서 커서가 다음라인 p태그 앞쪽이 아닌 div태그 끝쪽으로 자동으로 옮겨가는 경우가 있어서 // 커서가 멋대로 이동하지 않도록 임시북마크를 넣었다가 바로 빼준다. // (주의) 북마크를 넣었다 빼면 IE10은 다음라인 p태그 끝쪽으로 이동되기 때문에 IE7인 경우만 넣어줌 // [SMARTEDITORSUS-2043] SE_EditingArea_WYSIWYG.$ON_PASTE_HTML 에서 IE8의 경우만 삽입시 뒤에 \uFEFF가 추가로 붙어서 들어오는데 // 이로 인해 템플릿과 커서사이가 한줄 벌어지는 문제가 있어서 \uFEFF 추가하는 부분을 삭제하니 커서가 IE7과 동일하게 동작하여 IE8도 임시북마크처리함 if(jindo.$Agent().navigator().ie && jindo.$Agent().navigator().version < 9){ sBM = this.placeStringBookmark(); this.removeStringBookmark(sBM); } }else{ var oFirstNode = oTmpDiv.firstChild; var oLastNode = oTmpDiv.lastChild; this.collapseToStart(); while(oTmpDiv.lastChild){this.insertNode(oTmpDiv.lastChild);} this.setEndNodes(oFirstNode, oLastNode); // delete the content later as deleting it first may mass up the insertion point // eg)

[A]BCD

---paste O---> O

BCD

clone.moveToBookmark(sBM); clone.deleteContents(); clone.removeStringBookmark(sBM); } }, /** * 비어있는 P태그이면 제거한다. * @param {Element} el 검사할 Element * @returns {Boolean} 제거되었다면 true 를 반환한다. */ _removeEmptyP : function(el){ if(el && el.nodeName === "P"){ var sInner = el.innerHTML; if(sInner === "" || this._rxCursorHolder.test(sInner)){ el.parentNode.removeChild(el); return true; } } }, /** * 인접한 Element 노드를 찾는다. * @param {Node} oNode 기준 노드 * @param {Boolean} bPrev 앞뒤여부(true면 앞, false면 뒤) * @return {Element} 인접한 Element, 없으면 null 반환 */ _getSiblingElement : function(oNode, bPrev){ if(!oNode){ return null; } var oSibling = oNode[bPrev ? "previousSibling" : "nextSibling"]; if(oSibling && oSibling.nodeType === 1){ return oSibling; }else{ return arguments.callee(oSibling, bPrev); } }, /** * 앞쪽 인접한 Element 노드를 찾는다. * @param {Node} oNode 기준 노드 * @return {Element} 인접한 Element, 없으면 null 반환 */ _getPrevElement : function(oNode){ return this._getSiblingElement(oNode, true); }, /** * 뒤쪽 인접한 Element 노드를 찾는다. * @param {Node} oNode 기준 노드 * @return {Element} 인접한 Element, 없으면 null 반환 */ _getNextElement : function(oNode){ return this._getSiblingElement(oNode, false); }, toString : function(){ this.toString = nhn.W3CDOMRange.prototype.toString; return this.toString(); }, toHTMLString : function(){ var oTmpContainer = this._document.createElement("DIV"); oTmpContainer.appendChild(this.cloneContents()); return oTmpContainer.innerHTML; }, findAncestorByTagName : function(sTagName){ var oNode = this.commonAncestorContainer; while(oNode && oNode.tagName != sTagName){oNode = nhn.DOMFix.parentNode(oNode);} return oNode; }, selectNodeContents : function(oNode){ if(!oNode){return;} var oFirstNode = oNode.firstChild?oNode.firstChild:oNode; var oLastNode = oNode.lastChild?oNode.lastChild:oNode; this.reset(this._window); if(oFirstNode.nodeType == 3){ this.setStart(oFirstNode, 0, true); }else{ this.setStartBefore(oFirstNode); } if(oLastNode.nodeType == 3){ this.setEnd(oLastNode, oLastNode.nodeValue.length, true); }else{ this.setEndAfter(oLastNode); } }, /** * 노드의 취소선/밑줄 정보를 확인한다 * 관련 BTS [SMARTEDITORSUS-26] * @param {Node} oNode 취소선/밑줄을 확인할 노드 * @param {String} sValue textDecoration 정보 * @see nhn.HuskyRange#_checkTextDecoration */ _hasTextDecoration : function(oNode, sValue){ if(!oNode || !oNode.style){ return false; } if(oNode.style.textDecoration.indexOf(sValue) > -1){ return true; } if(sValue === "underline" && oNode.tagName === "U"){ return true; } if(sValue === "line-through" && (oNode.tagName === "S" || oNode.tagName === "STRIKE")){ return true; } return false; }, /** * 노드에 취소선/밑줄을 적용한다 * 관련 BTS [SMARTEDITORSUS-26] * [FF] 노드의 Style 에 textDecoration 을 추가한다 * [FF 외] U/STRIKE 태그를 추가한다 * @param {Node} oNode 취소선/밑줄을 적용할 노드 * @param {String} sValue textDecoration 정보 * @see nhn.HuskyRange#_checkTextDecoration */ _setTextDecoration : function(oNode, sValue){ if (jindo.$Agent().navigator().firefox) { // FF oNode.style.textDecoration = (oNode.style.textDecoration) ? oNode.style.textDecoration + " " + sValue : sValue; } else{ if(sValue === "underline"){ oNode.innerHTML = "" + oNode.innerHTML + "" }else if(sValue === "line-through"){ oNode.innerHTML = "" + oNode.innerHTML + "" } } }, /** * 인자로 전달받은 노드 상위의 취소선/밑줄 정보를 확인하여 노드에 적용한다 * 관련 BTS [SMARTEDITORSUS-26] * @param {Node} oNode 취소선/밑줄을 적용할 노드 */ _checkTextDecoration : function(oNode){ if(oNode.tagName !== "SPAN"){ return; } var bUnderline = false, bLineThrough = false, sTextDecoration = "", oParentNode = null; oChildNode = oNode.firstChild; /* check child */ while(oChildNode){ if(oChildNode.nodeType === 1){ bUnderline = (bUnderline || oChildNode.tagName === "U"); bLineThrough = (bLineThrough || oChildNode.tagName === "S" || oChildNode.tagName === "STRIKE"); } if(bUnderline && bLineThrough){ return; } oChildNode = oChildNode.nextSibling; } oParentNode = nhn.DOMFix.parentNode(oNode); /* check parent */ while(oParentNode && oParentNode.tagName !== "BODY"){ if(oParentNode.nodeType !== 1){ oParentNode = nhn.DOMFix.parentNode(oParentNode); continue; } if(!bUnderline && this._hasTextDecoration(oParentNode, "underline")){ bUnderline = true; this._setTextDecoration(oNode, "underline"); // set underline } if(!bLineThrough && this._hasTextDecoration(oParentNode, "line-through")){ bLineThrough = true; this._setTextDecoration(oNode, "line-through"); // set line-through } if(bUnderline && bLineThrough){ return; } oParentNode = nhn.DOMFix.parentNode(oParentNode); } }, /** * Range에 속한 노드들에 스타일을 적용한다 * @param {Object} oStyle 적용할 스타일을 가지는 Object (예) 글꼴 색 적용의 경우 { color : "#0075c8" } * @param {Object} [oAttribute] 적용할 속성을 가지는 Object (예) 맞춤범 검사의 경우 { _sm2_spchk: "강남콩", class: "se2_check_spell" } * @param {String} [sNewSpanMarker] 새로 추가된 SPAN 노드를 나중에 따로 처리해야하는 경우 마킹을 위해 사용하는 문자열 * @param {Boolean} [bIncludeLI] LI 도 스타일 적용에 포함할 것인지의 여부 [COM-1051] _getStyleParentNodes 메서드 참고하기 * @param {Boolean} [bCheckTextDecoration] 취소선/밑줄 처리를 적용할 것인지 여부 [SMARTEDITORSUS-26] _setTextDecoration 메서드 참고하기 */ styleRange : function(oStyle, oAttribute, sNewSpanMarker, bIncludeLI, bCheckTextDecoration){ var aStyleParents = this.aStyleParents = this._getStyleParentNodes(sNewSpanMarker, bIncludeLI); if(aStyleParents.length < 1){return;} var sName, sValue; for(var i=0; i 1){ // 싱글노드가 아니면 더이상 찾지 않고 null 반환 return null; } } if(oNode.nodeName === "SPAN"){ return oNode; }else{ return this._findParentSingleSpan(oNode.parentNode); } }, /** * 컨테이너 엘리먼트(elContainer)의 모든 자식노드가 노드 배열(waAllNodes)에 속하는지 확인한다 * 첫 번째 자식 노드와 마지막 자식 노드가 노드 배열에 속하는지를 확인한다 * @param {Element} elContainer 컨테이너 엘리먼트 * @param {jindo.$A} waAllNodes Node 의 $A 배열 * @param {Node} [oNode] 성능을 위한 옵션 노드로 컨테이너의 첫 번째 혹은 마지막 자식 노드와 같으면 indexOf 함수 사용을 줄일 수 있음 * @return {Array} Style 을 적용할 노드 배열 */ // check if all the child nodes of elContainer are in waAllNodes _isFullyContained : function(elContainer, waAllNodes, oNode){ var nSIdx, nEIdx; var oTmpNode = this._getVeryFirstRealChild(elContainer); // do quick checks before trying indexOf() because indexOf() function is very slow // oNode is optional if(oNode && oTmpNode == oNode){ nSIdx = 1; }else{ nSIdx = waAllNodes.indexOf(oTmpNode); } if(nSIdx != -1){ oTmpNode = this._getVeryLastRealChild(elContainer); if(oNode && oTmpNode == oNode){ nEIdx = 1; }else{ nEIdx = waAllNodes.indexOf(oTmpNode); } } return (nSIdx != -1 && nEIdx != -1); }, _getVeryFirstChild : function(oNode){ if(oNode.firstChild){return this._getVeryFirstChild(oNode.firstChild);} return oNode; }, _getVeryLastChild : function(oNode){ if(oNode.lastChild){return this._getVeryLastChild(oNode.lastChild);} return oNode; }, _getFirstRealChild : function(oNode){ var oFirstNode = oNode.firstChild; while(oFirstNode && oFirstNode.nodeType == 3 && oFirstNode.nodeValue == ""){oFirstNode = oFirstNode.nextSibling;} return oFirstNode; }, _getLastRealChild : function(oNode){ var oLastNode = oNode.lastChild; while(oLastNode && oLastNode.nodeType == 3 && oLastNode.nodeValue == ""){oLastNode = oLastNode.previousSibling;} return oLastNode; }, _getVeryFirstRealChild : function(oNode){ var oFirstNode = this._getFirstRealChild(oNode); if(oFirstNode){return this._getVeryFirstRealChild(oFirstNode);} return oNode; }, _getVeryLastRealChild : function(oNode){ var oLastNode = this._getLastRealChild(oNode); if(oLastNode){return this._getVeryLastChild(oLastNode);} return oNode; }, _getLineStartInfo : function(node){ var frontEndFinal = null; var frontEnd = node; var lineBreaker = node; var bParentBreak = false; var rxLineBreaker = this.rxLineBreaker; // vertical(parent) search function getLineStart(node){ if(!node){return;} if(frontEndFinal){return;} if(rxLineBreaker.test(node.tagName)){ lineBreaker = node; frontEndFinal = frontEnd; bParentBreak = true; return; }else{ frontEnd = node; } getFrontEnd(node.previousSibling); if(frontEndFinal){return;} getLineStart(nhn.DOMFix.parentNode(node)); } // horizontal(sibling) search function getFrontEnd(node){ if(!node){return;} if(frontEndFinal){return;} if(rxLineBreaker.test(node.tagName)){ lineBreaker = node; frontEndFinal = frontEnd; bParentBreak = false; return; } if(node.firstChild && node.tagName != "TABLE"){ var curNode = node.lastChild; while(curNode && !frontEndFinal){ getFrontEnd(curNode); curNode = curNode.previousSibling; } }else{ frontEnd = node; } if(!frontEndFinal){ getFrontEnd(node.previousSibling); } } if(rxLineBreaker.test(node.tagName)){ frontEndFinal = node; }else{ getLineStart(node); } return {oNode: frontEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak}; }, _getLineEndInfo : function(node){ var backEndFinal = null; var backEnd = node; var lineBreaker = node; var bParentBreak = false; var rxLineBreaker = this.rxLineBreaker; // vertical(parent) search function getLineEnd(node){ if(!node){return;} if(backEndFinal){return;} if(rxLineBreaker.test(node.tagName)){ lineBreaker = node; backEndFinal = backEnd; bParentBreak = true; return; }else{ backEnd = node; } getBackEnd(node.nextSibling); if(backEndFinal){return;} getLineEnd(nhn.DOMFix.parentNode(node)); } // horizontal(sibling) search function getBackEnd(node){ if(!node){return;} if(backEndFinal){return;} if(rxLineBreaker.test(node.tagName)){ lineBreaker = node; backEndFinal = backEnd; bParentBreak = false; return; } if(node.firstChild && node.tagName != "TABLE"){ var curNode = node.firstChild; while(curNode && !backEndFinal){ getBackEnd(curNode); curNode = curNode.nextSibling; } }else{ backEnd = node; } if(!backEndFinal){ getBackEnd(node.nextSibling); } } if(rxLineBreaker.test(node.tagName)){ backEndFinal = node; }else{ getLineEnd(node); } return {oNode: backEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak}; }, getLineInfo : function(bAfter){ var bAfter = bAfter || false; var oSNode = this.getStartNode(); var oENode = this.getEndNode(); // oSNode && oENode will be null if the range is currently collapsed and the cursor is not located in the middle of a text node. if(!oSNode){oSNode = this.getNodeAroundRange(!bAfter, true);} if(!oENode){oENode = this.getNodeAroundRange(!bAfter, true);} var oStart = this._getLineStartInfo(oSNode); var oStartNode = oStart.oNode; var oEnd = this._getLineEndInfo(oENode); var oEndNode = oEnd.oNode; if(oSNode != oStartNode || oENode != oEndNode){ // check if the start node is positioned after the range's ending point // or // if the end node is positioned before the range's starting point var iRelativeStartPos = this._compareEndPoint(nhn.DOMFix.parentNode(oStartNode), this._getPosIdx(oStartNode), this.endContainer, this.endOffset); var iRelativeEndPos = this._compareEndPoint(nhn.DOMFix.parentNode(oEndNode), this._getPosIdx(oEndNode)+1, this.startContainer, this.startOffset); if(!(iRelativeStartPos <= 0 && iRelativeEndPos >= 0)){ oSNode = this.getNodeAroundRange(false, true); oENode = this.getNodeAroundRange(false, true); oStart = this._getLineStartInfo(oSNode); oEnd = this._getLineEndInfo(oENode); } } return {oStart: oStart, oEnd: oEnd}; }, /** * 커서홀더나 공백을 제외한 child 노드가 하나만 있는 경우만 node 를 반환한다. * @param {Node} oNode 확인할 노드 * @return {Node} single child node를 반환한다. 없거나 두개 이상이면 null 을 반환 */ _findSingleChild : function(oNode){ if(!oNode){ return null; } var oSingleChild = null; // ZWNBSP 문자가 같이 있는 경우도 있기 때문에 실제 노드를 카운팅해야 함 for(var i = 0, nCnt = 0, sValue, oChild, aChildNodes = oNode.childNodes; (oChild = aChildNodes[i]); i++){ sValue = oChild.nodeValue; if(this._rxCursorHolder.test(sValue)){ continue; }else{ oSingleChild = oChild; nCnt++; } if(nCnt > 1){ // 싱글노드가 아니면 더이상 찾지 않고 null 반환 return null; } } return oSingleChild; }, /** * 해당요소의 최하위까지 검색해 커서홀더만 감싸고 있는지 여부를 반환 * @param {Node} oNode 확인할 노드 * @return {Boolean} 커서홀더만 있는 경우 true 반환 */ _hasCursorHolderOnly : function(oNode){ if(!oNode || oNode.nodeType !== 1){ return false; } if(this._rxCursorHolder.test(oNode.innerHTML)){ return true; }else{ return this._hasCursorHolderOnly(this._findSingleChild(oNode)); } } }).extend(nhn.W3CDOMRange); /** * @fileOverview This file contains cross-browser selection function * @name BrowserSelection.js */ nhn.BrowserSelection = function(win){ this.init = function(win){ this._window = win || window; this._document = this._window.document; }; this.init(win); // [SMARTEDITORSUS-888] IE9 이후로 document.createRange 를 지원 /* var oAgentInfo = jindo.$Agent().navigator(); if(oAgentInfo.ie){ nhn.BrowserSelectionImpl_IE.apply(this); }else{ nhn.BrowserSelectionImpl_FF.apply(this); }*/ if(!!this._document.createRange){ nhn.BrowserSelectionImpl_FF.apply(this); }else{ nhn.BrowserSelectionImpl_IE.apply(this); } this.selectRange = function(oRng){ this.selectNone(); this.addRange(oRng); }; this.selectionLoaded = true; if(!this._oSelection){this.selectionLoaded = false;} }; nhn.BrowserSelectionImpl_FF = function(){ this._oSelection = this._window.getSelection(); this.getRangeAt = function(iNum){ iNum = iNum || 0; try{ var oFFRange = this._oSelection.getRangeAt(iNum); }catch(e){return new nhn.W3CDOMRange(this._window);} return this._FFRange2W3CRange(oFFRange); }; this.addRange = function(oW3CRange){ var oFFRange = this._W3CRange2FFRange(oW3CRange); this._oSelection.addRange(oFFRange); }; this.selectNone = function(){ this._oSelection.removeAllRanges(); }; this.getCommonAncestorContainer = function(oW3CRange){ var oFFRange = this._W3CRange2FFRange(oW3CRange); return oFFRange.commonAncestorContainer; }; this.isCollapsed = function(oW3CRange){ var oFFRange = this._W3CRange2FFRange(oW3CRange); return oFFRange.collapsed; }; this.compareEndPoints = function(elContainerA, nOffsetA, elContainerB, nOffsetB){ var oFFRangeA = this._document.createRange(); var oFFRangeB = this._document.createRange(); oFFRangeA.setStart(elContainerA, nOffsetA); oFFRangeB.setStart(elContainerB, nOffsetB); oFFRangeA.collapse(true); oFFRangeB.collapse(true); try{ return oFFRangeA.compareBoundaryPoints(1, oFFRangeB); }catch(e){ return 1; } }; this._FFRange2W3CRange = function(oFFRange){ var oW3CRange = new nhn.W3CDOMRange(this._window); oW3CRange.setStart(oFFRange.startContainer, oFFRange.startOffset, true); oW3CRange.setEnd(oFFRange.endContainer, oFFRange.endOffset, true); return oW3CRange; }; this._W3CRange2FFRange = function(oW3CRange){ var oFFRange = this._document.createRange(); oFFRange.setStart(oW3CRange.startContainer, oW3CRange.startOffset); oFFRange.setEnd(oW3CRange.endContainer, oW3CRange.endOffset); return oFFRange; }; }; nhn.BrowserSelectionImpl_IE = function(){ this._oSelection = this._document.selection; this.oLastRange = { oBrowserRange : null, elStartContainer : null, nStartOffset : -1, elEndContainer : null, nEndOffset : -1 }; this._updateLastRange = function(oBrowserRange, oW3CRange){ this.oLastRange.oBrowserRange = oBrowserRange; this.oLastRange.elStartContainer = oW3CRange.startContainer; this.oLastRange.nStartOffset = oW3CRange.startOffset; this.oLastRange.elEndContainer = oW3CRange.endContainer; this.oLastRange.nEndOffset = oW3CRange.endOffset; }; this.getRangeAt = function(iNum){ iNum = iNum || 0; var oW3CRange, oBrowserRange; if(this._oSelection.type == "Control"){ oW3CRange = new nhn.W3CDOMRange(this._window); var oSelectedNode = this._oSelection.createRange().item(iNum); // if the selction occurs in a different document, ignore if(!oSelectedNode || oSelectedNode.ownerDocument != this._document){return oW3CRange;} oW3CRange.selectNode(oSelectedNode); return oW3CRange; }else{ //oBrowserRange = this._oSelection.createRangeCollection().item(iNum); oBrowserRange = this._oSelection.createRange(); var oSelectedNode = oBrowserRange.parentElement(); // if the selction occurs in a different document, ignore if(!oSelectedNode || oSelectedNode.ownerDocument != this._document){ oW3CRange = new nhn.W3CDOMRange(this._window); return oW3CRange; } oW3CRange = this._IERange2W3CRange(oBrowserRange); return oW3CRange; } }; this.addRange = function(oW3CRange){ var oIERange = this._W3CRange2IERange(oW3CRange); oIERange.select(); }; this.selectNone = function(){ this._oSelection.empty(); }; this.getCommonAncestorContainer = function(oW3CRange){ return this._W3CRange2IERange(oW3CRange).parentElement(); }; this.isCollapsed = function(oW3CRange){ var oRange = this._W3CRange2IERange(oW3CRange); var oRange2 = oRange.duplicate(); oRange2.collapse(); return oRange.isEqual(oRange2); }; this.compareEndPoints = function(elContainerA, nOffsetA, elContainerB, nOffsetB){ var oIERangeA, oIERangeB; if(elContainerA === this.oLastRange.elStartContainer && nOffsetA === this.oLastRange.nStartOffset){ oIERangeA = this.oLastRange.oBrowserRange.duplicate(); oIERangeA.collapse(true); }else{ if(elContainerA === this.oLastRange.elEndContainer && nOffsetA === this.oLastRange.nEndOffset){ oIERangeA = this.oLastRange.oBrowserRange.duplicate(); oIERangeA.collapse(false); }else{ oIERangeA = this._getIERangeAt(elContainerA, nOffsetA); } } if(elContainerB === this.oLastRange.elStartContainer && nOffsetB === this.oLastRange.nStartOffset){ oIERangeB = this.oLastRange.oBrowserRange.duplicate(); oIERangeB.collapse(true); }else{ if(elContainerB === this.oLastRange.elEndContainer && nOffsetB === this.oLastRange.nEndOffset){ oIERangeB = this.oLastRange.oBrowserRange.duplicate(); oIERangeB.collapse(false); }else{ oIERangeB = this._getIERangeAt(elContainerB, nOffsetB); } } return oIERangeA.compareEndPoints("StartToStart", oIERangeB); }; this._W3CRange2IERange = function(oW3CRange){ if(this.oLastRange.elStartContainer === oW3CRange.startContainer && this.oLastRange.nStartOffset === oW3CRange.startOffset && this.oLastRange.elEndContainer === oW3CRange.endContainer && this.oLastRange.nEndOffset === oW3CRange.endOffset){ return this.oLastRange.oBrowserRange; } var oStartIERange = this._getIERangeAt(oW3CRange.startContainer, oW3CRange.startOffset); var oEndIERange = this._getIERangeAt(oW3CRange.endContainer, oW3CRange.endOffset); oStartIERange.setEndPoint("EndToEnd", oEndIERange); this._updateLastRange(oStartIERange, oW3CRange); return oStartIERange; }; this._getIERangeAt = function(oW3CContainer, iW3COffset){ var oIERange = this._document.body.createTextRange(); var oEndPointInfoForIERange = this._getSelectableNodeAndOffsetForIE(oW3CContainer, iW3COffset); var oSelectableNode = oEndPointInfoForIERange.oSelectableNodeForIE; var iIEOffset = oEndPointInfoForIERange.iOffsetForIE; oIERange.moveToElementText(oSelectableNode); oIERange.collapse(oEndPointInfoForIERange.bCollapseToStart); oIERange.moveStart("character", iIEOffset); return oIERange; }; this._getSelectableNodeAndOffsetForIE = function(oW3CContainer, iW3COffset){ // var oIERange = this._document.body.createTextRange(); var oNonTextNode = null; var aChildNodes = null; var iNumOfLeftNodesToCount = 0; if(oW3CContainer.nodeType == 3){ oNonTextNode = nhn.DOMFix.parentNode(oW3CContainer); aChildNodes = nhn.DOMFix.childNodes(oNonTextNode); iNumOfLeftNodesToCount = aChildNodes.length; }else{ oNonTextNode = oW3CContainer; aChildNodes = nhn.DOMFix.childNodes(oNonTextNode); //iNumOfLeftNodesToCount = iW3COffset; iNumOfLeftNodesToCount = (iW3COffset=0){break;} oPrevNonTextNode = aChildNodes[i]; } var pointRangeIdx = i; if(pointRangeIdx !== 0 && aChildNodes[pointRangeIdx-1].nodeType == 3){ var oRgTextStart = this._document.body.createTextRange(); var oCurTextNode = null; if(oPrevNonTextNode){ oRgTextStart.moveToElementText(oPrevNonTextNode); oRgTextStart.collapse(false); oCurTextNode = oPrevNonTextNode.nextSibling; }else{ oRgTextStart.moveToElementText(oContainer); oRgTextStart.collapse(true); oCurTextNode = oContainer.firstChild; } var oRgTextsUpToThePoint = oRgOrigPoint.duplicate(); oRgTextsUpToThePoint.setEndPoint("StartToStart", oRgTextStart); var textCount = oRgTextsUpToThePoint.text.replace(/[\r\n]/g,"").length; while(textCount > oCurTextNode.nodeValue.length && oCurTextNode.nextSibling){ textCount -= oCurTextNode.nodeValue.length; oCurTextNode = oCurTextNode.nextSibling; } // this will enforce IE to re-reference oCurTextNode var oTmp = oCurTextNode.nodeValue; if(bStartPt && oCurTextNode.nextSibling && oCurTextNode.nextSibling.nodeType == 3 && textCount == oCurTextNode.nodeValue.length){ textCount -= oCurTextNode.nodeValue.length; oCurTextNode = oCurTextNode.nextSibling; } oContainer = oCurTextNode; offset = textCount; }else{ oContainer = oRgOrigPoint.parentElement(); offset = pointRangeIdx; } return {"oContainer" : oContainer, "iOffset" : offset}; }; }; nhn.DOMFix = new (jindo.$Class({ $init : function(){ if(jindo.$Agent().navigator().ie || jindo.$Agent().navigator().opera){ this.childNodes = this._childNodes_Fix; this.parentNode = this._parentNode_Fix; }else{ this.childNodes = this._childNodes_Native; this.parentNode = this._parentNode_Native; } }, _parentNode_Native : function(elNode){ return elNode.parentNode; }, _parentNode_Fix : function(elNode){ if(!elNode){return elNode;} while(elNode.previousSibling){elNode = elNode.previousSibling;} return elNode.parentNode; }, _childNodes_Native : function(elNode){ return elNode.childNodes; }, _childNodes_Fix : function(elNode){ var aResult = null; var nCount = 0; if(elNode){ var aResult = []; elNode = elNode.firstChild; while(elNode){ aResult[nCount++] = elNode; elNode=elNode.nextSibling; } } return aResult; } }))(); /*[ * ADD_APP_PROPERTY * * 주요 오브젝트를 모든 플러그인에서 this.oApp를 통해서 직접 접근 가능 하도록 등록한다. * * sPropertyName string 등록명 * oProperty object 등록시킬 오브젝트 * ---------------------------------------------------------------------------]*/ /*[ * REGISTER_BROWSER_EVENT * * 특정 브라우저 이벤트가 발생 했을때 Husky 메시지를 발생 시킨다. * * obj HTMLElement 브라우저 이벤트를 발생 시킬 HTML 엘리먼트 * sEvent string 발생 대기 할 브라우저 이벤트 * sMsg string 발생 할 Husky 메시지 * aParams array 메시지에 넘길 파라미터 * nDelay number 브라우저 이벤트 발생 후 Husky 메시지 발생 사이에 딜레이를 주고 싶을 경우 설정. (1/1000초 단위) * ---------------------------------------------------------------------------]*/ /*[ * DISABLE_MESSAGE * * 특정 메시지를 코어에서 무시하고 라우팅 하지 않도록 비활성화 한다. * * sMsg string 비활성화 시킬 메시지 * ---------------------------------------------------------------------------]*/ /*[ * ENABLE_MESSAGE * * 무시하도록 설정된 메시지를 무시하지 않도록 활성화 한다. * * sMsg string 활성화 시킬 메시지 * ---------------------------------------------------------------------------]*/ /*[ * EXEC_ON_READY_FUNCTION * * oApp.run({fnOnAppReady:fnOnAppReady})와 같이 run 호출 시점에 지정된 함수가 있을 경우 이를 MSG_APP_READY 시점에 실행 시킨다. * 코어에서 자동으로 발생시키는 메시지로 직접 발생시키지는 않도록 한다. * * none * ---------------------------------------------------------------------------]*/ /** * @pluginDesc Husky Framework에서 자주 사용되는 메시지를 처리하는 플러그인 */ nhn.husky.CorePlugin = jindo.$Class({ name : "CorePlugin", // nStatus = 0(request not sent), 1(request sent), 2(response received) // sContents = response htLazyLoadRequest_plugins : {}, htLazyLoadRequest_allFiles : {}, htHTMLLoaded : {}, $AFTER_MSG_APP_READY : function(){ this.oApp.exec("EXEC_ON_READY_FUNCTION", []); }, $ON_ADD_APP_PROPERTY : function(sPropertyName, oProperty){ this.oApp[sPropertyName] = oProperty; }, $ON_REGISTER_BROWSER_EVENT : function(obj, sEvent, sMsg, aParams, nDelay){ this.oApp.registerBrowserEvent(obj, sEvent, sMsg, aParams, nDelay); }, $ON_DISABLE_MESSAGE : function(sMsg){ this.oApp.disableMessage(sMsg, true); }, $ON_ENABLE_MESSAGE : function(sMsg){ this.oApp.disableMessage(sMsg, false); }, $ON_LOAD_FULL_PLUGIN : function(aFilenames, sClassName, sMsgName, oThisRef, oArguments){ var oPluginRef = oThisRef.$this || oThisRef; // var nIdx = _nIdx||0; var sFilename = aFilenames[0]; if(!this.htLazyLoadRequest_plugins[sFilename]){ this.htLazyLoadRequest_plugins[sFilename] = {nStatus:1, sContents:""}; } if(this.htLazyLoadRequest_plugins[sFilename].nStatus === 2){ //this.oApp.delayedExec("MSG_FULL_PLUGIN_LOADED", [sFilename, sClassName, sMsgName, oThisRef, oArguments, false], 0); this.oApp.exec("MSG_FULL_PLUGIN_LOADED", [sFilename, sClassName, sMsgName, oThisRef, oArguments, false]); }else{ this._loadFullPlugin(aFilenames, sClassName, sMsgName, oThisRef, oArguments, 0); } }, _loadFullPlugin : function(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx){ jindo.LazyLoading.load(nhn.husky.SE2M_Configuration.LazyLoad.sJsBaseURI+"/"+aFilenames[nIdx], jindo.$Fn(function(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx){ var sCurFilename = aFilenames[nIdx]; // plugin filename var sFilename = aFilenames[0]; if(nIdx == aFilenames.length-1){ this.htLazyLoadRequest_plugins[sFilename].nStatus=2; this.oApp.exec("MSG_FULL_PLUGIN_LOADED", [aFilenames, sClassName, sMsgName, oThisRef, oArguments]); return; } //this.oApp.exec("LOAD_FULL_PLUGIN", [aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx+1]); this._loadFullPlugin(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx+1); }, this).bind(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx), "utf-8" ); }, $ON_MSG_FULL_PLUGIN_LOADED : function(aFilenames, sClassName, sMsgName, oThisRef, oArguments, oRes){ // oThisRef.$this는 현재 로드되는 플러그인이 parent 인스턴스일 경우 존재 함. oThisRef.$this는 현재 플러그인(oThisRef)를 parent로 삼고 있는 인스턴스 // oThisRef에 $this 속성이 없다면 parent가 아닌 일반 인스턴스 // oPluginRef는 결과적으로 상속 관계가 있다면 자식 인스턴스를 아니라면 일반적인 인스턴스를 가짐 var oPluginRef = oThisRef.$this || oThisRef; var sFilename = aFilenames; // now the source code is loaded, remove the loader handlers for(var i=0, nLen=oThisRef._huskyFLT.length; ili>button"); } }, $LOCAL_BEFORE_FIRST : function(sMsg) { var aToolItems = jindo.$$(">ul>li[class*=" + this.sUIClassPrefix + "]>button", this.elTextTool); var nItemLength = aToolItems.length; this.elFirstToolbarItem = this.elFirstToolbarItem || aToolItems[0]; this.elLastToolbarItem = aToolItems[nItemLength-1]; this.oApp.registerBrowserEvent(this.toolbarArea, "keydown", "NAVIGATE_TOOLBAR", []); }, /** * @param {Element} oAppContainer * @param {Object} htOptions * @param {Array} htOptions.aDisabled 비활성화할 버튼명 배열 */ $init : function(oAppContainer, htOptions){ this._htOptions = htOptions || {}; this.htUIList = {}; this.htWrappedUIList = {}; this.aUICmdMap = {}; this._assignHTMLElements(oAppContainer); }, $ON_MSG_APP_READY : function(){ if(this.oApp.bMobile){ this.oApp.registerBrowserEvent(this.toolbarArea, "touchstart", "EVENT_TOOLBAR_TOUCHSTART"); }else{ this.oApp.registerBrowserEvent(this.toolbarArea, "mouseover", "EVENT_TOOLBAR_MOUSEOVER"); this.oApp.registerBrowserEvent(this.toolbarArea, "mouseout", "EVENT_TOOLBAR_MOUSEOUT"); } this.oApp.registerBrowserEvent(this.toolbarArea, "mousedown", "EVENT_TOOLBAR_MOUSEDOWN"); this.oApp.exec("ADD_APP_PROPERTY", ["getToolbarButtonByUIName", jindo.$Fn(this.getToolbarButtonByUIName, this).bind()]); // [SMARTEDITORSUS-1679] 초기 disabled 처리가 필요한 버튼은 비활성화 if(this._htOptions.aDisabled){ this._htOptions._sDisabled = "," + this._htOptions.aDisabled.toString() + ","; // 버튼을 활성화할때 비교하기 위한 문자열구성 this.oApp.exec("DISABLE_UI", [this._htOptions.aDisabled]); } }, $ON_NAVIGATE_TOOLBAR : function(weEvent) { var TAB_KEY_CODE = 9; //이벤트가 발생한 엘리먼트가 마지막 아이템이고 TAB 키가 눌려졌다면 if ((weEvent.element == this.elLastToolbarItem) && (weEvent.key().keyCode == TAB_KEY_CODE) ) { if (weEvent.key().shift) { //do nothing } else { this.elFirstToolbarItem.focus(); weEvent.stopDefault(); } } //이벤트가 발생한 엘리먼트가 첫번째 아이템이고 TAB 키가 눌려졌다면 if (weEvent.element == this.elFirstToolbarItem && (weEvent.key().keyCode == TAB_KEY_CODE)) { if (weEvent.key().shift) { weEvent.stopDefault(); this.elLastToolbarItem.focus(); } } }, $ON_TOGGLE_TOOLBAR_ACTIVE_LAYER : function(elLayer, elBtn, sOpenCmd, aOpenArgs, sCloseCmd, aCloseArgs){ this.oApp.exec("TOGGLE_ACTIVE_LAYER", [elLayer, "MSG_TOOLBAR_LAYER_SHOWN", [elLayer, elBtn, sOpenCmd, aOpenArgs], sCloseCmd, aCloseArgs]); }, $ON_MSG_TOOLBAR_LAYER_SHOWN : function(elLayer, elBtn, aOpenCmd, aOpenArgs){ this.oApp.exec("POSITION_TOOLBAR_LAYER", [elLayer, elBtn]); if(aOpenCmd){ this.oApp.exec(aOpenCmd, aOpenArgs); } }, $ON_SHOW_TOOLBAR_ACTIVE_LAYER : function(elLayer, sCmd, aArgs, elBtn){ this.oApp.exec("SHOW_ACTIVE_LAYER", [elLayer, sCmd, aArgs]); this.oApp.exec("POSITION_TOOLBAR_LAYER", [elLayer, elBtn]); }, $ON_ENABLE_UI : function(sUIName){ this._enableUI(sUIName); }, /** * [SMARTEDITORSUS-1679] 여러개의 버튼을 동시에 비활성화 할 수 있도록 수정 * @param {String|Array} vUIName 비활성화할 버튼명, 배열일 경우 여러개 동시 적용 */ $ON_DISABLE_UI : function(sUIName){ if(sUIName instanceof Array){ for(var i = 0, sName; (sName = sUIName[i]); i++){ this._disableUI(sName); } }else{ this._disableUI(sUIName); } }, $ON_SELECT_UI : function(sUIName){ var welUI = this.htWrappedUIList[sUIName]; if(!welUI){ return; } welUI.removeClass("hover"); welUI.addClass("active"); }, $ON_DESELECT_UI : function(sUIName){ var welUI = this.htWrappedUIList[sUIName]; if(!welUI){ return; } welUI.removeClass("active"); }, /** * [SMARTEDITORSUS-1646] 툴바버튼 선택상태를 토글링한다. * @param {String} sUIName 토글링할 툴바버튼 이름 */ $ON_TOGGLE_UI_SELECTED : function(sUIName){ var welUI = this.htWrappedUIList[sUIName]; if(!welUI){ return; } if(welUI.hasClass("active")){ welUI.removeClass("active"); }else{ welUI.removeClass("hover"); welUI.addClass("active"); } }, $ON_ENABLE_ALL_UI : function(htOptions){ if(this.nUIStatus === 1){ return; } var sUIName, className; htOptions = htOptions || {}; var waExceptions = jindo.$A(htOptions.aExceptions || []); for(sUIName in this.htUIList){ if(sUIName && !waExceptions.has(sUIName)){ this._enableUI(sUIName); } // if(sUIName) this.oApp.exec("ENABLE_UI", [sUIName]); } // jindo.$Element(this.toolbarArea).removeClass("off"); this.nUIStatus = 1; }, $ON_DISABLE_ALL_UI : function(htOptions){ if(this.nUIStatus === 2){ return; } var sUIName; htOptions = htOptions || {}; var waExceptions = jindo.$A(htOptions.aExceptions || []); var bLeavlActiveLayer = htOptions.bLeaveActiveLayer || false; if(!bLeavlActiveLayer){ this.oApp.exec("HIDE_ACTIVE_LAYER"); } for(sUIName in this.htUIList){ if(sUIName && !waExceptions.has(sUIName)){ this._disableUI(sUIName); } // if(sUIName) this.oApp.exec("DISABLE_UI", [sUIName]); } // jindo.$Element(this.toolbarArea).addClass("off"); this.nUIStatus = 2; }, $ON_MSG_STYLE_CHANGED : function(sAttributeName, attributeValue){ if(attributeValue === "@^"){ this.oApp.exec("SELECT_UI", [sAttributeName]); }else{ this.oApp.exec("DESELECT_UI", [sAttributeName]); } }, $ON_POSITION_TOOLBAR_LAYER : function(elLayer, htOption){ var nLayerLeft, nLayerRight, nToolbarLeft, nToolbarRight; elLayer = jindo.$(elLayer); htOption = htOption || {}; var elBtn = jindo.$(htOption.elBtn); var sAlign = htOption.sAlign; var nMargin = -1; if(!elLayer){ return; } if(elBtn && elBtn.tagName && elBtn.tagName == "BUTTON"){ elBtn.parentNode.appendChild(elLayer); } var welLayer = jindo.$Element(elLayer); if(sAlign != "right"){ elLayer.style.left = "0"; nLayerLeft = welLayer.offset().left; nLayerRight = nLayerLeft + elLayer.offsetWidth; nToolbarLeft = this.welToolbarArea.offset().left; nToolbarRight = nToolbarLeft + this.toolbarArea.offsetWidth; if(nLayerRight > nToolbarRight){ welLayer.css("left", (nToolbarRight-nLayerRight-nMargin)+"px"); } if(nLayerLeft < nToolbarLeft){ welLayer.css("left", (nToolbarLeft-nLayerLeft+nMargin)+"px"); } }else{ elLayer.style.right = "0"; nLayerLeft = welLayer.offset().left; nLayerRight = nLayerLeft + elLayer.offsetWidth; nToolbarLeft = this.welToolbarArea.offset().left; nToolbarRight = nToolbarLeft + this.toolbarArea.offsetWidth; if(nLayerRight > nToolbarRight){ welLayer.css("right", -1*(nToolbarRight-nLayerRight-nMargin)+"px"); } if(nLayerLeft < nToolbarLeft){ welLayer.css("right", -1*(nToolbarLeft-nLayerLeft+nMargin)+"px"); } } }, $ON_EVENT_TOOLBAR_MOUSEOVER : function(weEvent){ if(this.nUIStatus === 2){ return; } var aAffectedElements = this._getAffectedElements(weEvent.element); for(var i=0; i childNodes.length<=2) // -> In this case, do not close here(mousedown). The layer will be closed on "click". If we close the layer here, the click event will open it again because it toggles the visibility. while(elTmp){ if(elTmp.className && elTmp.className.match(/active/) && (elTmp.childNodes.length>2 || elTmp.parentNode.className.match(/se2_pair/))){ return; } elTmp = elTmp.parentNode; } this.oApp.exec("HIDE_ACTIVE_LAYER_IF_NOT_CHILD", [weEvent.element]); }, _enableUI : function(sUIName){ // [SMARTEDITORSUS-1679] 초기 disabled 설정된 버튼은 skip if(this._htOptions._sDisabled && this._htOptions._sDisabled.indexOf(","+sUIName+",") > -1){ return; } var i, nLen; this.nUIStatus = 0; var welUI = this.htWrappedUIList[sUIName]; var elUI = this.htUIList[sUIName]; if(!welUI){ return; } welUI.removeClass("off"); var aAllBtns = elUI.getElementsByTagName("BUTTON"); for(i=0, nLen=aAllBtns.length; i // ", $init : function(elAppContainer){ this.elAppContainer = elAppContainer; }, $ON_MSG_APP_READY : function(){}, _assignHTMLElements : function(oAppContainer){ var htConfiguration = nhn.husky.SE2M_Configuration.SE2M_ColorPalette; if(htConfiguration){ this.bUseRecentColor = htConfiguration.bUseRecentColor || false; this.URL_COLOR_ADD = htConfiguration.addColorURL || "http://api.se2.naver.com/1/colortable/TextAdd.nhn"; this.URL_COLOR_UPDATE = htConfiguration.updateColorURL || "http://api.se2.naver.com/1/colortable/TextUpdate.nhn"; this.URL_COLOR_LIST = htConfiguration.colorListURL || "http://api.se2.naver.com/1/colortable/TextList.nhn"; } this.elColorPaletteLayer = jindo.$$.getSingle("DIV.husky_se2m_color_palette", oAppContainer); this.elColorPaletteLayerColorPicker = jindo.$$.getSingle("DIV.husky_se2m_color_palette_colorpicker", this.elColorPaletteLayer); this.elRecentColorForm = jindo.$$.getSingle("form", this.elColorPaletteLayerColorPicker); this.elBackgroundColor = jindo.$$.getSingle("ul.husky_se2m_bgcolor_list", oAppContainer); this.elInputColorCode = jindo.$$.getSingle("INPUT.husky_se2m_cp_colorcode", this.elColorPaletteLayerColorPicker); this.elPreview = jindo.$$.getSingle("SPAN.husky_se2m_cp_preview", this.elColorPaletteLayerColorPicker); this.elCP_ColPanel = jindo.$$.getSingle("DIV.husky_se2m_cp_colpanel", this.elColorPaletteLayerColorPicker); this.elCP_HuePanel = jindo.$$.getSingle("DIV.husky_se2m_cp_huepanel", this.elColorPaletteLayerColorPicker); this.elCP_ColPanel.style.position = "relative"; this.elCP_HuePanel.style.position = "relative"; this.elColorPaletteLayerColorPicker.style.display = "none"; this.elMoreBtn = jindo.$$.getSingle("BUTTON.husky_se2m_color_palette_more_btn", this.elColorPaletteLayer); this.welMoreBtn = jindo.$Element(this.elMoreBtn); this.elOkBtn = jindo.$$.getSingle("BUTTON.husky_se2m_color_palette_ok_btn", this.elColorPaletteLayer); if(this.bUseRecentColor){ this.elColorPaletteLayerRecent = jindo.$$.getSingle("DIV.husky_se2m_color_palette_recent", this.elColorPaletteLayer); this.elRecentColor = jindo.$$.getSingle("ul.se2_pick_color", this.elColorPaletteLayerRecent); this.elDummyNode = jindo.$$.getSingle("ul.se2_pick_color > li", this.elColorPaletteLayerRecent) || null; this.elColorPaletteLayerRecent.style.display = "none"; } }, $LOCAL_BEFORE_FIRST : function(){ this._assignHTMLElements(this.elAppContainer); if(this.elDummyNode){ jindo.$Element(jindo.$$.getSingle("ul.se2_pick_color > li", this.elColorPaletteLayerRecent)).leave(); } if( this.bUseRecentColor ){ this._ajaxRecentColor(this._ajaxRecentColorCallback); } this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "click", "EVENT_CLICK_COLOR_PALETTE"); // [SMARTEDITORSUS-1833] 아이패드에서 mouseover 이벤트리스너를 등록하면 후속 click 이벤트가 바로 동작하지 않음 // 모바일환경에서 hover 처리는 의미가 없으므로 PC 환경에서만 hover 처리하도록 함 if(!this.oApp.bMobile){ this.oApp.registerBrowserEvent(this.elBackgroundColor, "mouseover", "EVENT_MOUSEOVER_COLOR_PALETTE"); this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "mouseover", "EVENT_MOUSEOVER_COLOR_PALETTE"); this.oApp.registerBrowserEvent(this.elBackgroundColor, "mouseout", "EVENT_MOUSEOUT_COLOR_PALETTE"); this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "mouseout", "EVENT_MOUSEOUT_COLOR_PALETTE"); } }, $ON_EVENT_MOUSEOVER_COLOR_PALETTE : function(oEvent){ var elHovered = oEvent.element; while(elHovered && elHovered.tagName && elHovered.tagName.toLowerCase() != "li"){ elHovered = elHovered.parentNode; } //조건 추가-by cielo 2010.04.20 if(!elHovered || !elHovered.nodeType || elHovered.nodeType == 9){return;} if(elHovered.className == "" || (!elHovered.className) || typeof(elHovered.className) == 'undefined'){jindo.$Element(elHovered).addClass("hover");} }, $ON_EVENT_MOUSEOUT_COLOR_PALETTE : function(oEvent){ var elHovered = oEvent.element; while(elHovered && elHovered.tagName && elHovered.tagName.toLowerCase() != "li"){ elHovered = elHovered.parentNode; } if(!elHovered){return;} if(elHovered.className == "hover"){jindo.$Element(elHovered).removeClass("hover");} }, $ON_EVENT_CLICK_COLOR_PALETTE : function(oEvent){ var elClicked = oEvent.element; while(elClicked.tagName == "SPAN"){elClicked = elClicked.parentNode;} if(elClicked.tagName && elClicked.tagName == "BUTTON"){ if(elClicked == this.elMoreBtn){ this.oApp.exec("TOGGLE_COLOR_PICKER"); return; } this.oApp.exec("APPLY_COLOR", [elClicked]); } }, $ON_APPLY_COLOR : function(elButton){ var sColorCode = this.elInputColorCode.value, welColorParent = null; if(sColorCode.indexOf("#") == -1){ sColorCode = "#" + sColorCode; this.elInputColorCode.value = sColorCode; } // 입력 버튼인 경우 if(elButton == this.elOkBtn){ if(!this._verifyColorCode(sColorCode)){ this.elInputColorCode.value = ""; alert(this.oApp.$MSG("SE_Color.invalidColorCode")); this.elInputColorCode.focus(); return; } this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode,true]); return; } // 색상 버튼인 경우 welColorParent = jindo.$Element(elButton.parentNode.parentNode.parentNode); sColorCode = elButton.title; if(welColorParent.hasClass("husky_se2m_color_palette")){ // 템플릿 색상 적용 /* * [SMARTEDITORSUS-1884][SMARTEDITORSUS-2117] * 설정값 제거(r12236) 전에도 * 모든 타입에서 * nhn.husky.SE2M_Configuration.SE2M_ColorPalette.bAddRecentColorFromDefault 값이 * undefined인 상태로 동작하고 있었기 때문에 * false로 처리 */ this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode, false]); }else if(welColorParent.hasClass("husky_se2m_color_palette_recent")){ // 최근 색상 적용 this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode,true]); } }, $ON_RESET_COLOR_PALETTE : function(){ this._initColor(); }, $ON_TOGGLE_COLOR_PICKER : function(){ if(this.elColorPaletteLayerColorPicker.style.display == "none"){ this.oApp.exec("SHOW_COLOR_PICKER"); }else{ this.oApp.exec("HIDE_COLOR_PICKER"); } }, $ON_SHOW_COLOR_PICKER : function(){ this.elColorPaletteLayerColorPicker.style.display = ""; this.cpp = new nhn.ColorPicker(this.elCP_ColPanel, {huePanel:this.elCP_HuePanel}); var fn = jindo.$Fn(function(oEvent) { this.elPreview.style.backgroundColor = oEvent.hexColor; this.elInputColorCode.value = oEvent.hexColor; }, this).bind(); this.cpp.attach("colorchange", fn); this.$ON_SHOW_COLOR_PICKER = this._showColorPickerMain; this.$ON_SHOW_COLOR_PICKER(); }, $ON_HIDE_COLOR_PICKER : function(){ this.elColorPaletteLayerColorPicker.style.display = "none"; this.welMoreBtn.addClass("se2_view_more"); this.welMoreBtn.removeClass("se2_view_more2"); }, $ON_SHOW_COLOR_PALETTE : function(sCallbackCmd, oLayerContainer){ this.sCallbackCmd = sCallbackCmd; this.oLayerContainer = oLayerContainer; this.oLayerContainer.insertBefore(this.elColorPaletteLayer, null); this.elColorPaletteLayer.style.display = "block"; this.oApp.delayedExec("POSITION_TOOLBAR_LAYER", [this.elColorPaletteLayer.parentNode.parentNode], 0); }, $ON_HIDE_COLOR_PALETTE : function(){ this.elColorPaletteLayer.style.display = "none"; }, $ON_COLOR_PALETTE_APPLY_COLOR : function(sColorCode , bAddRecentColor){ bAddRecentColor = (!bAddRecentColor)? false : bAddRecentColor; sColorCode = this._getHexColorCode(sColorCode); //더보기 레이어에서 적용한 색상만 최근 사용한 색에 추가한다. if( this.bUseRecentColor && !!bAddRecentColor ){ this.oApp.exec("ADD_RECENT_COLOR", [sColorCode]); } this.oApp.exec(this.sCallbackCmd, [sColorCode]); }, $ON_EVENT_MOUSEUP_COLOR_PALETTE : function(oEvent){ var elButton = oEvent.element; if(! elButton.style.backgroundColor){return;} this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [elButton.style.backgroundColor,false]); }, $ON_ADD_RECENT_COLOR : function(sRGBCode){ var bAdd = (this.aRecentColor.length === 0); this._addRecentColor(sRGBCode); if(bAdd){ this._ajaxAddColor(); }else{ this._ajaxUpdateColor(); } this._redrawRecentColorElement(); }, _verifyColorCode : function(sColorCode){ return this.rxColorPattern.test(sColorCode); }, _getHexColorCode : function(sColorCode){ if(this.rxRGBColorPattern.test(sColorCode)){ var dec2Hex = function(sDec){ var sTmp = parseInt(sDec, 10).toString(16); if(sTmp.length<2){sTmp = "0"+sTmp;} return sTmp.toUpperCase(); }; var sR = dec2Hex(RegExp.$1); var sG = dec2Hex(RegExp.$2); var sB = dec2Hex(RegExp.$3); sColorCode = "#"+sR+sG+sB; } return sColorCode; }, _addRecentColor : function(sRGBCode){ var waRecentColor = jindo.$A(this.aRecentColor); waRecentColor = waRecentColor.refuse(sRGBCode); waRecentColor.unshift(sRGBCode); if(waRecentColor.length() > this.nLimitRecentColor){ waRecentColor.length(this.nLimitRecentColor); } this.aRecentColor = waRecentColor.$value(); }, _redrawRecentColorElement : function(){ var aRecentColorHtml = [], nRecentColor = this.aRecentColor.length, i; if(nRecentColor === 0){ return; } for(i=0; i this.nLimitRecentColor){ waColorList.length(this.nLimitRecentColor); } aColorList = waColorList.reverse().$value(); for(i = 0, nLen = aColorList.length; i < nLen; i++){ this._addRecentColor(this._getHexColorCode(aColorList[i])); } this._redrawRecentColorElement(); } }).extend(jindo.Component); //} /** * @fileOverview This file contains Husky plugin that takes care of the operations related to changing the font color * @name hp_SE_FontColor.js */ nhn.husky.SE2M_FontColor = jindo.$Class({ name : "SE2M_FontColor", rxColorPattern : /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, $init : function(elAppContainer){ this._assignHTMLElements(elAppContainer); }, _assignHTMLElements : function(elAppContainer){ //@ec[ this.elLastUsed = jindo.$$.getSingle("SPAN.husky_se2m_fontColor_lastUsed", elAppContainer); this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_fontcolor_layer", elAppContainer); this.elPaletteHolder = jindo.$$.getSingle("DIV.husky_se2m_fontcolor_paletteHolder", this.elDropdownLayer); //@ec] this._setLastUsedFontColor("#000000"); }, $BEFORE_MSG_APP_READY : function() { this.oApp.exec("ADD_APP_PROPERTY", ["getLastUsedFontColor", jindo.$Fn(this.getLastUsedFontColor, this).bind()]); }, $ON_MSG_APP_READY : function(){ this.oApp.exec("REGISTER_UI_EVENT", ["fontColorA", "click", "APPLY_LAST_USED_FONTCOLOR"]); this.oApp.exec("REGISTER_UI_EVENT", ["fontColorB", "click", "TOGGLE_FONTCOLOR_LAYER"]); this.oApp.registerLazyMessage(["APPLY_LAST_USED_FONTCOLOR", "TOGGLE_FONTCOLOR_LAYER"], ["hp_SE2M_FontColor$Lazy.js"]); }, _setLastUsedFontColor : function(sFontColor){ this.sLastUsedColor = sFontColor; this.elLastUsed.style.backgroundColor = this.sLastUsedColor; }, getLastUsedFontColor : function(){ return (!!this.sLastUsedColor) ? this.sLastUsedColor : '#000000'; } }); //{ /** * @fileOverview This file contains Husky plugin that takes care of changing the background color * @name hp_SE2M_BGColor.js */ nhn.husky.SE2M_BGColor = jindo.$Class({ name : "SE2M_BGColor", rxColorPattern : /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, $init : function(elAppContainer){ this._assignHTMLElements(elAppContainer); }, _assignHTMLElements : function(elAppContainer){ //@ec[ this.elLastUsed = jindo.$$.getSingle("SPAN.husky_se2m_BGColor_lastUsed", elAppContainer); this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_BGColor_layer", elAppContainer); this.elBGColorList = jindo.$$.getSingle("UL.husky_se2m_bgcolor_list", elAppContainer); this.elPaletteHolder = jindo.$$.getSingle("DIV.husky_se2m_BGColor_paletteHolder", this.elDropdownLayer); //@ec] this._setLastUsedBGColor("#777777"); }, $BEFORE_MSG_APP_READY : function() { this.oApp.exec("ADD_APP_PROPERTY", ["getLastUsedBackgroundColor", jindo.$Fn(this.getLastUsedBGColor, this).bind()]); }, $ON_MSG_APP_READY : function(){ this.oApp.exec("REGISTER_UI_EVENT", ["BGColorA", "click", "APPLY_LAST_USED_BGCOLOR"]); this.oApp.exec("REGISTER_UI_EVENT", ["BGColorB", "click", "TOGGLE_BGCOLOR_LAYER"]); this.oApp.registerBrowserEvent(this.elBGColorList, "click", "EVENT_APPLY_BGCOLOR", []); this.oApp.registerLazyMessage(["APPLY_LAST_USED_BGCOLOR", "TOGGLE_BGCOLOR_LAYER"], ["hp_SE2M_BGColor$Lazy.js"]); }, _setLastUsedBGColor : function(sBGColor){ this.sLastUsedColor = sBGColor; this.elLastUsed.style.backgroundColor = this.sLastUsedColor; }, getLastUsedBGColor : function(){ return (!!this.sLastUsedColor) ? this.sLastUsedColor : '#777777'; } }); //} /** * @fileOverview This file contains Husky plugin that takes care of the operations related to hyperlink * @name hp_SE_Hyperlink.js */ nhn.husky.SE2M_Hyperlink = jindo.$Class({ name : "SE2M_Hyperlink", sATagMarker : "HTTP://HUSKY_TMP.MARKER/", _assignHTMLElements : function(elAppContainer){ this.oHyperlinkButton = jindo.$$.getSingle("li.husky_seditor_ui_hyperlink", elAppContainer); this.oHyperlinkLayer = jindo.$$.getSingle("div.se2_layer", this.oHyperlinkButton); this.oLinkInput = jindo.$$.getSingle("INPUT[type=text]", this.oHyperlinkLayer); this.oBtnConfirm = jindo.$$.getSingle("button.se2_apply", this.oHyperlinkLayer); this.oBtnCancel = jindo.$$.getSingle("button.se2_cancel", this.oHyperlinkLayer); //this.oCbNewWin = jindo.$$.getSingle("INPUT[type=checkbox]", this.oHyperlinkLayer) || null; }, _generateAutoLink : function(sAll, sBreaker, sURL, sWWWURL, sHTTPURL) { sBreaker = sBreaker || ""; var sResult; if (sWWWURL){ sResult = ''+sURL+''; } else { sResult = ''+sURL+''; } return sBreaker+sResult; }, /** * [SMARTEDITORSUS-1405] 자동링크 비활성화 옵션을 체크해서 처리한다. * $ON_REGISTER_CONVERTERS 메시지가 SE_EditingAreaManager.$ON_MSG_APP_READY 에서 수행되므로 먼저 처리한다. */ $BEFORE_MSG_APP_READY : function(){ var htOptions = nhn.husky.SE2M_Configuration.SE2M_Hyperlink; if(htOptions && htOptions.bAutolink === false){ // 자동링크 컨버터 비활성화 this.$ON_REGISTER_CONVERTERS = null; // UI enable/disable 처리 제외 this.$ON_DISABLE_MESSAGE = null; this.$ON_ENABLE_MESSAGE = null; // 브라우저의 자동링크기능 비활성화 try{ this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, false); } catch(e){} } }, $ON_MSG_APP_READY : function(){ this.bLayerShown = false; // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 if (jindo.$Agent().os().mac) { this.oApp.exec("REGISTER_HOTKEY", ["meta+k", "TOGGLE_HYPERLINK_LAYER", []]); } else { this.oApp.exec("REGISTER_HOTKEY", ["ctrl+k", "TOGGLE_HYPERLINK_LAYER", []]); } this.oApp.exec("REGISTER_UI_EVENT", ["hyperlink", "click", "TOGGLE_HYPERLINK_LAYER"]); this.oApp.registerLazyMessage(["TOGGLE_HYPERLINK_LAYER", "APPLY_HYPERLINK"], ["hp_SE2M_Hyperlink$Lazy.js"]); }, $ON_REGISTER_CONVERTERS : function(){ this.oApp.exec("ADD_CONVERTER_DOM", ["IR_TO_DB", jindo.$Fn(this.irToDb, this).bind()]); }, $LOCAL_BEFORE_FIRST : function(sMsg){ if(!!sMsg.match(/(REGISTER_CONVERTERS)/)){ this.oApp.acceptLocalBeforeFirstAgain(this, true); return true; } this._assignHTMLElements(this.oApp.htOptions.elAppContainer); this.sRXATagMarker = this.sATagMarker.replace(/\//g, "\\/").replace(/\./g, "\\."); this.oApp.registerBrowserEvent(this.oBtnConfirm, "click", "APPLY_HYPERLINK"); this.oApp.registerBrowserEvent(this.oBtnCancel, "click", "HIDE_ACTIVE_LAYER"); this.oApp.registerBrowserEvent(this.oLinkInput, "keydown", "EVENT_HYPERLINK_KEYDOWN"); }, $ON_EVENT_HYPERLINK_KEYDOWN : function(oEvent){ if (oEvent.key().enter){ this.oApp.exec("APPLY_HYPERLINK"); oEvent.stop(); } }, /** * [MUG-1265] 버튼이 사용불가 상태이면 자동변환기능을 막는다. * @see http://stackoverflow.com/questions/7556007/avoid-transformation-text-to-link-ie-contenteditable-mode * IE9 이전 버전은 AutoURlDetect을 사용할 수 없어 오류 발생되기 때문에, try catch로 블럭 처리(http://msdn.microsoft.com/en-us/library/aa769893%28VS.85%29.aspx) */ $ON_DISABLE_MESSAGE : function(sCmd) { if(sCmd !== "TOGGLE_HYPERLINK_LAYER"){ return; } try{ this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, false); } catch(e){} this._bDisabled = true; }, /** * [MUG-1265] 버튼이 사용가능 상태이면 자동변환기능을 복원해준다. */ $ON_ENABLE_MESSAGE : function(sCmd) { if(sCmd !== "TOGGLE_HYPERLINK_LAYER"){ return; } try{ this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, true); } catch(e){} this._bDisabled = false; }, irToDb : function(oTmpNode){ if(this._bDisabled){ // [MUG-1265] 버튼이 사용불가 상태이면 자동변환하지 않는다. return; } //저장 시점에 자동 링크를 위한 함수. //[SMARTEDITORSUS-1207][IE][메일] object 삽입 후 글을 저장하면 IE 브라우저가 죽어버리는 현상 //원인 : 확인 불가. IE 저작권 관련 이슈로 추정 //해결 : contents를 가지고 있는 div 태그를 이 함수 내부에서 복사하여 수정 후 call by reference로 넘어온 변수의 innerHTML을 변경 var oCopyNode = oTmpNode.cloneNode(true); try{ oCopyNode.innerHTML; }catch(e) { oCopyNode = jindo.$(oTmpNode.outerHTML); } var oTmpRange = this.oApp.getEmptySelection(); var elFirstNode = oTmpRange._getFirstRealChild(oCopyNode); var elLastNode = oTmpRange._getLastRealChild(oCopyNode); var waAllNodes = jindo.$A(oTmpRange._getNodesBetween(elFirstNode, elLastNode)); var aAllTextNodes = waAllNodes.filter(function(elNode){return (elNode && elNode.nodeType === 3);}).$value(); var a = aAllTextNodes; /* // 텍스트 검색이 용이 하도록 끊어진 텍스트 노드가 있으면 합쳐줌. (화면상으로 ABC라고 보이나 상황에 따라 실제 2개의 텍스트 A, BC로 이루어져 있을 수 있음. 이를 ABC 하나의 노드로 만들어 줌.) // 문제 발생 가능성에 비해서 퍼포먼스나 사이드 이펙트 가능성 높아 일단 주석 var aCleanTextNodes = []; for(var i=0, nLen=aAllTextNodes.length; i display name 매핑 (웹폰트는 두개가 다름) this.htFamilyName2DisplayName = {}; this.htAllFonts = {}; this.aBaseFontList = []; this.aDefaultFontList = []; this.aTempSavedFontList = []; this.htOptions = this.oApp.htOptions.SE2M_FontName; if(this.htOptions){ aDefaultFontList = this.htOptions.aDefaultFontList || []; aFontList = this.htOptions.aFontList; htMainFont = this.htOptions.htMainFont; aFontInUse = this.htOptions.aFontInUse; //add Font if(this.htBrowser.ie && aFontList){ for(i=0; i 0){ for(i = 0, nLen = this.aAdditionalFontList.length; i < nLen; i++){ this.addFont(this.aAdditionalFontList[i][0], this.aAdditionalFontList[i][1], 0, "", "", 1); } } }, $ON_MSG_APP_READY : function(){ this.bDoNotRecordUndo = false; this.oApp.exec("ADD_APP_PROPERTY", ["addFont", jindo.$Fn(this.addFont, this).bind()]); this.oApp.exec("ADD_APP_PROPERTY", ["addFontInUse", jindo.$Fn(this.addFontInUse, this).bind()]); // 블로그등 팩토리 폰트 포함 용 this.oApp.exec("ADD_APP_PROPERTY", ["setMainFont", jindo.$Fn(this.setMainFont, this).bind()]); // 메일등 단순 폰트 지정 용 this.oApp.exec("ADD_APP_PROPERTY", ["setDefaultFont", jindo.$Fn(this.setDefaultFont, this).bind()]); this.oApp.exec("REGISTER_UI_EVENT", ["fontName", "click", "SE2M_TOGGLE_FONTNAME_LAYER"]); this._initFontName(); // [SMARTEDITORSUS-2111] 메일쪽 요청으로 글꼴목록 초기화시점 변경 }, $AFTER_MSG_APP_READY : function(){ this._attachIEEvent(); }, _assignHTMLElements : function(elAppContainer){ //@ec[ this.oDropdownLayer = jindo.$$.getSingle("DIV.husky_se_fontName_layer", elAppContainer); this.elFontNameLabel = jindo.$$.getSingle("SPAN.husky_se2m_current_fontName", elAppContainer); this.elFontNameList = jindo.$$.getSingle("UL", this.oDropdownLayer); this.elInnerLayer = this.elFontNameList.parentNode; this.aelFontInMarkup = jindo.$$("LI", this.oDropdownLayer); // 마크업에 있는 LI this.elFontItemTemplate = this.aelFontInMarkup.shift(); // 맨앞에 있는 LI 는 템플릿 this.aLIFontNames = jindo.$A(jindo.$$("LI", this.oDropdownLayer)).filter(function(v,i,a){return (v.firstChild !== null);})._array; //@ec] this.sDefaultText = this.elFontNameLabel.innerHTML; }, //$LOCAL_BEFORE_FIRST : function(){ _initFontName : function(){ this._addFontInMarkup(); this.addAllFonts(); // [SMARTEDITORSUS-1853] 폰트가 초기화되면 현재 스타일정보를 가져와서 툴바에 반영해준다. var oStyle; if(this.oApp.getCurrentStyle && (oStyle = this.oApp.getCurrentStyle())){ this.$ON_MSG_STYLE_CHANGED("fontFamily", oStyle.fontFamily); } this.oApp.registerBrowserEvent(this.oDropdownLayer, "mouseover", "EVENT_FONTNAME_LAYER_MOUSEOVER", []); this.oApp.registerBrowserEvent(this.oDropdownLayer, "click", "EVENT_FONTNAME_LAYER_CLICKED", []); }, /** * 해당 글꼴이 존재하면 LI 요소를 보여주고 true 를 반환한다. * @param {Element} el 글꼴리스트의 LI 요소 * @param {String} sFontName 확인할 글꼴이름 * @return {Boolean} LI 요소가 있고 글꼴이 OS에 존재하면 true 반환 */ _checkFontLI : function(el, sFontName){ if(!el){ return false; } var bInstalled = IsInstalledFont(sFontName); el.style.display = bInstalled ? "block" : "none"; return bInstalled; }, /** * 마크업에 있는 글꼴 목록을 추가해준다. */ _addFontInMarkup : function(){ for(var i = 0, elLi, sFontFamily, elSeparator, bUseSeparator; (elLi = this.aelFontInMarkup[i]); i++){ if(elLi.firstChild){ sFontFamily = this._getFontFamilyFromLI(elLi).replace(this._rxQuote, "").replace(this._rxComma, ","); // 폰트패밀리값으로 OS에 폰트가 설치되어있는지 확인하여 노출하고 노출되면 구분선노출플래그를 true 로 세팅한다. bUseSeparator |= this._checkFontLI(elLi, sFontFamily); }else if(elLi.className.indexOf(this.FONT_SEPARATOR) > -1){ if(elSeparator){ // 이전에 구분선이 있었으면 구분선 노출여부 판단 elSeparator.style.display = bUseSeparator ? "block" : "none"; } elSeparator = elLi; // 새로운 구분선 저장 bUseSeparator = false; // 구분선노출플래그 리셋 }else{ elLi.style.display = "none"; } } // 마지막 구분선 노출여부를 확인한다. if(elSeparator){ elSeparator.style.display = bUseSeparator ? "block" : "none"; } }, _attachIEEvent : function(){ if(!this.htBrowser.ie){ return; } if(this.htBrowser.nativeVersion < 9){ // [SMARTEDITORSUS-187] [< IE9] 최초 paste 시점에 웹폰트 파일을 로드 this._wfOnPasteWYSIWYGBody = jindo.$Fn(this._onPasteWYSIWYGBody, this); this._wfOnPasteWYSIWYGBody.attach(this.oApp.getWYSIWYGDocument().body, "paste"); return; } if(document.documentMode < 9){ // [SMARTEDITORSUS-169] [>= IE9] 최초 포커스 시점에 웹폰트 로드 this._wfOnFocusWYSIWYGBody = jindo.$Fn(this._onFocusWYSIWYGBody, this); this._wfOnFocusWYSIWYGBody.attach(this.oApp.getWYSIWYGDocument().body, "focus"); return; } // documentMode === 9 // http://blogs.msdn.com/b/ie/archive/2010/08/17/ie9-opacity-and-alpha.aspx // opacity:0.0; this.welEditingAreaCover = jindo.$Element('
'); this.oApp.welEditingAreaContainer.prepend(this.welEditingAreaCover); jindo.$Fn(this._onMouseupCover, this).attach(this.welEditingAreaCover.$value(), "mouseup"); }, _onFocusWYSIWYGBody : function(e){ this._wfOnFocusWYSIWYGBody.detach(this.oApp.getWYSIWYGDocument().body, "focus"); this._loadAllBaseFont(); }, _onPasteWYSIWYGBody : function(e){ this._wfOnPasteWYSIWYGBody.detach(this.oApp.getWYSIWYGDocument().body, "paste"); this._loadAllBaseFont(); }, _onMouseupCover : function(e){ e.stop(); // [SMARTEDITORSUS-1632] 문서 모드가 9 이상일 때, 경우에 따라 this.welEditingAreaContainer가 없을 때 스크립트 오류 발생 if(this.welEditingAreaCover){ this.welEditingAreaCover.leave(); } //this.welEditingAreaCover.leave(); // --[SMARTEDITORSUS-1632] var oMouse = e.mouse(), elBody = this.oApp.getWYSIWYGDocument().body, welBody = jindo.$Element(elBody), oSelection = this.oApp.getEmptySelection(); // [SMARTEDITORSUS-363] 강제로 Selection 을 주도록 처리함 oSelection.selectNode(elBody); oSelection.collapseToStart(); oSelection.select(); welBody.fireEvent("mousedown", {left : oMouse.left, middle : oMouse.middle, right : oMouse.right}); welBody.fireEvent("mouseup", {left : oMouse.left, middle : oMouse.middle, right : oMouse.right}); /** * [SMARTEDITORSUS-1691] * [IE 10-] 에디터가 초기화되고 나서

로만 innerHTML을 설정하는데, * 이 경우 실제 커서는

내부에 있는 것이 아니라 그 앞에 위치한다. * 따라서 임시 북마크를 사용해서

내부로 커서를 이동시켜 준다. * * [SMARTEDITORSUS-1781] * [IE 11] 문서 모드가 Edge인 경우에 한하여 *


로 innerHTML을 설정하는데, * 실제 커서는


앞에 위치한다. * 이 경우에는 임시 북마크를 삽입할 필요 없이
앞에 커서를 위치시켜 준다. * */ if(this.oApp.oNavigator.ie && document.documentMode < 11 && this.oApp.getEditingMode() === "WYSIWYG"){ if(this.oApp.getWYSIWYGDocument().body.innerHTML == "

"){ this.oApp.getWYSIWYGDocument().body.innerHTML = '

'; var oSelection = this.oApp.getSelection(); oSelection.moveToStringBookmark("INIT"); oSelection.select(); oSelection.removeStringBookmark("INIT"); } }else if(this.oApp.oNavigator.ie && this.oApp.oNavigator.nativeVersion == 11 && document.documentMode == 11 && this.oApp.getEditingMode() === "WYSIWYG"){ if(this.oApp.getWYSIWYGDocument().body.innerHTML == "


"){ var elCursorHolder_br = jindo.$$.getSingle("br", elBody); oSelection.setStartBefore(elCursorHolder_br); oSelection.setEndBefore(elCursorHolder_br); oSelection.select(); } } // --[SMARTEDITORSUS-1781][SMARTEDITORSUS-1691] }, $ON_EVENT_TOOLBAR_MOUSEDOWN : function(){ if(this.htBrowser.nativeVersion < 9 || document.documentMode < 9){ return; } // [SMARTEDITORSUS-1632] 문서 모드가 9 이상일 때, 경우에 따라 this.welEditingAreaContainer가 없을 때 스크립트 오류 발생 if(this.welEditingAreaCover){ this.welEditingAreaCover.leave(); } //this.welEditingAreaCover.leave(); // --[SMARTEDITORSUS-1632] }, _loadAllBaseFont : function(){ var i, nFontLen; if(!this.htBrowser.ie){ return; } if(this.htBrowser.nativeVersion < 9){ for(i=0, nFontLen=this.aBaseFontList.length; i 20){ this.oDropdownLayer.style.overflowX = 'hidden'; this.oDropdownLayer.style.overflowY = 'auto'; this.oDropdownLayer.style.height = '400px'; this.oDropdownLayer.style.width = '204px'; // [SMARTEDITORSUS-155] 스크롤을 포함하여 206px 이 되도록 처리 } }, $ON_EVENT_FONTNAME_LAYER_MOUSEOVER : function(wev){ var elTmp = this._findLI(wev.element); if(!elTmp){ return; } this._clearLastHover(); elTmp.className = "hover"; this.elLastHover = elTmp; }, $ON_EVENT_FONTNAME_LAYER_CLICKED : function(wev){ var elTmp = this._findLI(wev.element); if(!elTmp){ return; } var sFontFamily = this._getFontFamilyFromLI(elTmp); // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 var htFontInfo = this.htAllFonts[sFontFamily.replace(/\"/g, nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS)]; var nDefaultFontSize; if(htFontInfo){ nDefaultFontSize = htFontInfo.defaultSize+"pt"; }else{ nDefaultFontSize = 0; } this.oApp.exec("SET_FONTFAMILY", [sFontFamily, nDefaultFontSize]); }, _findLI : function(elTmp){ while(elTmp.tagName != "LI"){ if(!elTmp || elTmp === this.oDropdownLayer){ return null; } elTmp = elTmp.parentNode; } if(elTmp.className.indexOf(this.FONT_SEPARATOR) > -1){ return null; } return elTmp; }, _clearLastHover : function(){ if(this.elLastHover){ this.elLastHover.className = ""; } }, $ON_SE2M_TOGGLE_FONTNAME_LAYER : function(){ this.oApp.exec("TOGGLE_TOOLBAR_ACTIVE_LAYER", [this.oDropdownLayer, null, "MSG_FONTNAME_LAYER_OPENED", [], "MSG_FONTNAME_LAYER_CLOSED", []]); this.oApp.exec('MSG_NOTIFY_CLICKCR', ['font']); }, $ON_MSG_FONTNAME_LAYER_OPENED : function(){ this.oApp.exec("SELECT_UI", ["fontName"]); }, $ON_MSG_FONTNAME_LAYER_CLOSED : function(){ this._clearLastHover(); this.oApp.exec("DESELECT_UI", ["fontName"]); }, $ON_MSG_STYLE_CHANGED : function(sAttributeName, sAttributeValue){ if(sAttributeName == "fontFamily"){ sAttributeValue = sAttributeValue.replace(/["']/g, ""); var elLi = this._getMatchingLI(sAttributeValue); this._clearFontNameSelection(); if(elLi){ this.elFontNameLabel.innerHTML = this._getFontNameLabelFromLI(elLi); jindo.$Element(elLi).addClass("active"); }else{ //var sDisplayName = this.htFamilyName2DisplayName[sAttributeValue] || sAttributeValue; var sDisplayName = this.sDefaultText; this.elFontNameLabel.innerHTML = sDisplayName; } } }, $BEFORE_RECORD_UNDO_BEFORE_ACTION : function(){ return !this.bDoNotRecordUndo; }, $BEFORE_RECORD_UNDO_AFTER_ACTION : function(){ return !this.bDoNotRecordUndo; }, $BEFORE_RECORD_UNDO_ACTION : function(){ return !this.bDoNotRecordUndo; }, $ON_SET_FONTFAMILY : function(sFontFamily, sDefaultSize){ if(!sFontFamily){return;} // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 var oFontInfo = this.htAllFonts[sFontFamily.replace(/\"/g, nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS)]; if(!!oFontInfo){ oFontInfo.loadCSS(this.oApp.getWYSIWYGDocument()); } // fontFamily와 fontSize 두개의 액션을 하나로 묶어서 undo history 저장 this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["SET FONTFAMILY", {bMustBlockElement:true}]); this.bDoNotRecordUndo = true; if(parseInt(sDefaultSize, 10) > 0){ this.oApp.exec("SET_WYSIWYG_STYLE", [{"fontSize":sDefaultSize}]); } this.oApp.exec("SET_WYSIWYG_STYLE", [{"fontFamily":sFontFamily}]); this.bDoNotRecordUndo = false; this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["SET FONTFAMILY", {bMustBlockElement:true}]); this.oApp.exec("HIDE_ACTIVE_LAYER", []); this.oApp.exec("CHECK_STYLE_CHANGE", []); }, _getMatchingLI : function(sFontName){ sFontName = sFontName.toLowerCase(); var elLi, aFontFamily; for(var i=0; i return (elLi.getElementsByTagName("EM")[0]).style.fontFamily; }, _getFontNameLabelFromLI : function(elLi){ return elLi.firstChild.firstChild.firstChild.nodeValue; }, _clearFontNameSelection : function(elLi){ for(var i=0; i0){ sFontFamily = fontId+"_"+defaultSize; sDisplayName = fontName+"_"+defaultSize; }else{ sFontFamily = fontId; sDisplayName = fontName; } if(!fontType){ sFontFamily = nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS + sFontFamily + nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS; } if(this.htAllFonts[sFontFamily]){ return this.htAllFonts[sFontFamily]; } this.htAllFonts[sFontFamily] = newFont; /* // do not add again, if the font is already in the list for(var i=0; i웹폰트B 선택>웹폰트 A를 다시 선택하면 웹폰트 A가 적용되지 않는 문제가 발생 // // [원인] // - IE9의 웹폰트 로드/언로드 시점 // 웹폰트 로드 시점: StyleSheet 의 @font-face 구문이 해석된 이후, DOM Tree 상에서 해당 웹폰트가 최초로 사용된 시점 // 웹폰트 언로드 시점: StyleSheet 의 @font-face 구문이 해석된 이후, DOM Tree 상에서 해당 웬폰트가 더이상 사용되지 않는 시점 // - 메뉴 리스트에 적용되는 스타일은 @font-face 이전에 처리되는 것이어서 언로드에 영향을 미치지 않음 // // 스마트에디터의 경우, 웹폰트를 선택할 때마다 SPAN 이 새로 추가되는 것이 아닌 선택된 SPAN 의 fontFamily 를 변경하여 처리하므로 // fontFamily 변경 후 DOM Tree 상에서 더이상 사용되지 않는 것으로 브라우저 판단하여 언로드 해버림. // [해결] // 언로드가 발생하지 않도록 메뉴 리스트에 스타일을 적용하는 것을 @font-face 이후로 하도록 처리하여 DOM Tree 상에 항상 적용될 수 있도록 함 // // [SMARTEDITORSUS-969] [IE10] 웹폰트를 사용하여 글을 등록하고, 수정모드로 들어갔을 때 웹폰트가 적용되지 않는 문제 // - IE10에서도 웹폰트 언로드가 발생하지 않도록 조건을 수정함 // -> 기존 : nativeVersion === 9 && documentMode === 9 // -> 수정 : nativeVersion >= 9 && documentMode >= 9 if(this.htBrowser.ie && this.htBrowser.nativeVersion >= 9 && document.documentMode >= 9) { newFont.loadCSSToMenu(); } this.htFamilyName2DisplayName[sFontFamily] = fontName; sSampleText = sSampleText || this.oApp.$MSG('SE2M_FontNameWithLayerUI.sSampleText'); this._addFontToMenu(sDisplayName, sFontFamily, sSampleText); if(!fontType){ this.aBaseFontList[this.aBaseFontList.length] = newFont; }else{ if(fontType == 1){ this.aDefaultFontList[this.aDefaultFontList.length] = newFont; }else{ this.aTempSavedFontList[this.aTempSavedFontList.length] = newFont; } } return newFont; }, // Add the font AND load it right away addFontInUse : function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType) { var newFont = this.addFont(fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType); if(!newFont){ return null; } newFont.loadCSS(this.oApp.getWYSIWYGDocument()); return newFont; }, // Add the font AND load it right away AND THEN set it as the default font setMainFont : function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType) { var newFont = this.addFontInUse(fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType); if(!newFont){ return null; } this.setDefaultFont(newFont.fontFamily, defaultSize); return newFont; }, setDefaultFont : function(sFontFamily, nFontSize){ var elBody = this.oApp.getWYSIWYGDocument().body; elBody.style.fontFamily = sFontFamily; if(nFontSize>0){elBody.style.fontSize = nFontSize + 'pt';} } }); nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS = "'"; // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 // property function for all fonts - including the default fonts and the custom fonts // non-custom fonts will have the defaultSize of 0 and empty string for fontURL/fontCSSURL function fontProperty(fontId, fontName, defaultSize, fontURL, fontCSSURL){ this.fontId = fontId; this.fontName = fontName; this.defaultSize = defaultSize; this.fontURL = fontURL; this.fontCSSURL = fontCSSURL; this.displayName = fontName; this.isLoaded = true; this.fontFamily = this.fontId; // it is custom font if(this.fontCSSURL != ""){ this.displayName += '' + defaultSize; this.fontFamily += '_' + defaultSize; // custom fonts requires css loading this.isLoaded = false; // load the css that loads the custom font this.loadCSS = function(doc){ // if the font is loaded already, return if(this.isLoaded){ return; } this._importCSS(doc); this.isLoaded = true; }; // [SMARTEDITORSUS-169] [IE9] // addImport 후에 처음 적용된 DOM-Tree 가 iframe 내부인 경우 (setMainFont || addFontInUse 에서 호출된 경우) // 해당 폰트에 대한 언로드 문제가 계속 발생하여 IE9에서 addFont 에서 호출하는 loadCSS 의 경우에는 isLoaded를 true 로 변경하지 않음. this.loadCSSToMenu = function(){ this._importCSS(document); }; this._importCSS = function(doc){ var nStyleSheet = doc.styleSheets.length; var oStyleSheet = doc.styleSheets[nStyleSheet - 1]; if(nStyleSheet === 0 || oStyleSheet.imports.length > 30){ // imports limit // [SMARTEDITORSUS-1828] IE11에서 document.createStyleSheet API가 제거되었음 // [SMARTEDITORSUS-2181] createStyleSheet 를 사용하면 순서보장이 안되어 createElement("style") 로 삽입하는 방식만 사용 // 참고1 : http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#legacyapis // 참고2 : http://msdn.microsoft.com/en-us/library/ie/ms531194(v=vs.85).aspx oStyleSheet = doc.createElement("style"); doc.documentElement.firstChild.appendChild(oStyleSheet); // [SMARTEDITORSUS-2189] sheet은 모던브라우저, IE9이상부터 지원 (IE8이하는 styleSheet 으로 가져와야함) // 참고 : http://help.dottoro.com/ljpatulu.php oStyleSheet = oStyleSheet.sheet || oStyleSheet.styleSheet; } oStyleSheet.addImport(this.fontCSSURL); }; }else{ this.loadCSS = function(){}; this.loadCSSToMenu = function(){}; } this.toStruct = function(){ return {fontId:this.fontId, fontName:this.fontName, defaultSize:this.defaultSize, fontURL:this.fontURL, fontCSSURL:this.fontCSSURL}; }; } /** * ColorPicker Component * @author gony */ nhn.ColorPicker = jindo.$Class({ elem : null, huePanel : null, canvasType : "Canvas", _hsvColor : null, $init : function(oElement, oOptions) { this.elem = jindo.$Element(oElement).empty(); this.huePanel = null; this.cursor = jindo.$Element("
").css("overflow", "hidden"); this.canvasType = jindo.$(oElement).filters?"Filter":jindo.$("").getContext?"Canvas":null; if(!this.canvasType) { return false; } this.option({ huePanel : null, huePanelType : "horizontal" }); this.option(oOptions); if (this.option("huePanel")) { this.huePanel = jindo.$Element(this.option("huePanel")).empty(); } // rgb this._hsvColor = this._hsv(0,100,100); // #FF0000 // event binding for(var name in this) { if (/^_on[A-Z][a-z]+[A-Z][a-z]+$/.test(name)) { this[name+"Fn"] = jindo.$Fn(this[name], this); } } this._onDownColorFn.attach(this.elem, "mousedown"); if (this.huePanel) { this._onDownHueFn.attach(this.huePanel, "mousedown"); } // paint this.paint(); }, rgb : function(rgb) { this.hsv(this._rgb2hsv(rgb.r, rgb.g, rgb.b)); }, hsv : function(hsv) { if (typeof hsv == "undefined") { return this._hsvColor; } var rgb = null; var w = this.elem.width(); var h = this.elem.height(); var cw = this.cursor.width(); var ch = this.cursor.height(); var x = 0, y = 0; if (this.huePanel) { rgb = this._hsv2rgb(hsv.h, 100, 100); this.elem.css("background", "#"+this._rgb2hex(rgb.r, rgb.g, rgb.b)); x = hsv.s/100 * w; y = (100-hsv.v)/100 * h; } else { var hw = w / 2; if (hsv.v > hsv.s) { hsv.v = 100; x = hsv.s/100 * hw; } else { hsv.s = 100; x = (100-hsv.v)/100 * hw + hw; } y = hsv.h/360 * h; } x = Math.max(Math.min(x-1,w-cw), 1); y = Math.max(Math.min(y-1,h-ch), 1); this.cursor.css({left:x+"px",top:y+"px"}); this._hsvColor = hsv; rgb = this._hsv2rgb(hsv.h, hsv.s, hsv.v); this.fireEvent("colorchange", {type:"colorchange", element:this, currentElement:this, rgbColor:rgb, hexColor:"#"+this._rgb2hex(rgb.r, rgb.g, rgb.b), hsvColor:hsv} ); }, paint : function() { if (this.huePanel) { // paint color panel this["_paintColWith"+this.canvasType](); // paint hue panel this["_paintHueWith"+this.canvasType](); } else { // paint color panel this["_paintOneWith"+this.canvasType](); } // draw cursor this.cursor.appendTo(this.elem); this.cursor.css({position:"absolute",top:"1px",left:"1px",background:"white",border:"1px solid black"}).width(3).height(3); this.hsv(this._hsvColor); }, _paintColWithFilter : function() { // white : left to right jindo.$Element("
").css({ position : "absolute", top : 0, left : 0, width : "100%", height : "100%", filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#FFFFFFFF',EndColorStr='#00FFFFFF')" }).appendTo(this.elem); // black : down to up jindo.$Element("
").css({ position : "absolute", top : 0, left : 0, width : "100%", height : "100%", filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#00000000',EndColorStr='#FF000000')" }).appendTo(this.elem); }, _paintColWithCanvas : function() { var cvs = jindo.$Element("").css({width:"100%",height:"100%"}); cvs.appendTo(this.elem.empty()); var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); var lin = null; var w = cvs.width(); var h = cvs.height(); // white : left to right lin = ctx.createLinearGradient(0,0,w,0); lin.addColorStop(0, "rgba(255,255,255,1)"); lin.addColorStop(1, "rgba(255,255,255,0)"); ctx.fillStyle = lin; ctx.fillRect(0,0,w,h); // black : down to top lin = ctx.createLinearGradient(0,0,0,h); lin.addColorStop(0, "rgba(0,0,0,0)"); lin.addColorStop(1, "rgba(0,0,0,1)"); ctx.fillStyle = lin; ctx.fillRect(0,0,w,h); }, _paintOneWithFilter : function() { var sp, ep, s_rgb, e_rgb, s_hex, e_hex; var h = this.elem.height(); for(var i=1; i < 7; i++) { sp = Math.floor((i-1)/6 * h); ep = Math.floor(i/6 * h); s_rgb = this._hsv2rgb((i-1)/6*360, 100, 100); e_rgb = this._hsv2rgb(i/6*360, 100, 100); s_hex = "#FF"+this._rgb2hex(s_rgb.r, s_rgb.g, s_rgb.b); e_hex = "#FF"+this._rgb2hex(e_rgb.r, e_rgb.g, e_rgb.b); jindo.$Element("
").css({ position : "absolute", left : 0, width : "100%", top : sp + "px", height : (ep-sp) + "px", filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='"+s_hex+"',EndColorStr='"+e_hex+"')" }).appendTo(this.elem); } // white : left to right jindo.$Element("
").css({ position : "absolute", top : 0, left : 0, width : "50%", height : "100%", filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#FFFFFFFF',EndColorStr='#00FFFFFF')" }).appendTo(this.elem); // black : down to up jindo.$Element("
").css({ position : "absolute", top : 0, right : 0, width : "50%", height : "100%", filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#00000000',EndColorStr='#FF000000')" }).appendTo(this.elem); }, _paintOneWithCanvas : function() { var rgb = {r:0, g:0, b:0}; var cvs = jindo.$Element("").css({width:"100%",height:"100%"}); cvs.appendTo(this.elem.empty()); var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); var w = cvs.width(); var h = cvs.height(); var lin = ctx.createLinearGradient(0,0,0,h); for(var i=0; i < 7; i++) { rgb = this._hsv2rgb(i/6*360, 100, 100); lin.addColorStop(i/6, "rgb("+rgb.join(",")+")"); } ctx.fillStyle = lin; ctx.fillRect(0,0,w,h); lin = ctx.createLinearGradient(0,0,w,0); lin.addColorStop(0, "rgba(255,255,255,1)"); lin.addColorStop(0.5, "rgba(255,255,255,0)"); lin.addColorStop(0.5, "rgba(0,0,0,0)"); lin.addColorStop(1, "rgba(0,0,0,1)"); ctx.fillStyle = lin; ctx.fillRect(0,0,w,h); }, _paintHueWithFilter : function() { var sp, ep, s_rgb, e_rgb, s_hex, e_hex; var vert = (this.option().huePanelType == "vertical"); var w = this.huePanel.width(); var h = this.huePanel.height(); var elDiv = null; var nPanelBorderWidth = parseInt(this.huePanel.css('borderWidth'), 10); if (!!isNaN(nPanelBorderWidth)) { nPanelBorderWidth = 0; } w -= nPanelBorderWidth * 2; // borderWidth를 제외한 내측 폭을 구함 for(var i=1; i < 7; i++) { sp = Math.floor((i-1)/6 * (vert?h:w)); ep = Math.floor(i/6 * (vert?h:w)); s_rgb = this._hsv2rgb((i-1)/6*360, 100, 100); e_rgb = this._hsv2rgb(i/6*360, 100, 100); s_hex = "#FF"+this._rgb2hex(s_rgb.r, s_rgb.g, s_rgb.b); e_hex = "#FF"+this._rgb2hex(e_rgb.r, e_rgb.g, e_rgb.b); elDiv = jindo.$Element("
").css({ position : "absolute", filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType="+(vert?0:1)+",StartColorStr='"+s_hex+"',EndColorStr='"+e_hex+"')" }); var width = (ep - sp) + 1; // IE에서 폭을 넓혀주지 않으면 확대 시 벌어짐, 그래서 1px 보정 elDiv.appendTo(this.huePanel); elDiv.css(vert?"left":"top", 0).css(vert?"width":"height", '100%'); elDiv.css(vert?"top":"left", sp + "px").css(vert?"height":"width", width + "px"); } }, _paintHueWithCanvas : function() { var opt = this.option(), rgb; var vtc = (opt.huePanelType == "vertical"); var cvs = jindo.$Element("").css({width:"100%",height:"100%"}); cvs.appendTo(this.huePanel.empty()); var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); var lin = ctx.createLinearGradient(0,0,vtc?0:cvs.width(),vtc?cvs.height():0); for(var i=0; i < 7; i++) { rgb = this._hsv2rgb(i/6*360, 100, 100); lin.addColorStop(i/6, "rgb("+rgb.join(",")+")"); } ctx.fillStyle = lin; ctx.fillRect(0,0,cvs.width(),cvs.height()); }, _rgb2hsv : function(r,g,b) { var h = 0, s = 0, v = Math.max(r,g,b), min = Math.min(r,g,b), delta = v - min; s = (v ? delta/v : 0); if (s) { if (r == v) { h = 60 * (g - b) / delta; } else if (g == v) { h = 120 + 60 * (b - r) / delta; } else if (b == v) { h = 240 + 60 * (r - g) / delta; } if (h < 0) { h += 360; } } h = Math.floor(h); s = Math.floor(s * 100); v = Math.floor(v / 255 * 100); return this._hsv(h,s,v); }, _hsv2rgb : function(h,s,v) { h = (h % 360) / 60; s /= 100; v /= 100; var r=0, g=0, b=0; var i = Math.floor(h); var f = h-i; var p = v*(1-s); var q = v*(1-s*f); var t = v*(1-s*(1-f)); switch (i) { case 0: r=v; g=t; b=p; break; case 1: r=q; g=v; b=p; break; case 2: r=p; g=v; b=t; break; case 3: r=p; g=q; b=v; break; case 4: r=t; g=p; b=v; break; case 5: r=v; g=p; b=q;break; case 6: break; } r = Math.floor(r*255); g = Math.floor(g*255); b = Math.floor(b*255); return this._rgb(r,g,b); }, _rgb2hex : function(r,g,b) { r = r.toString(16); if (r.length == 1) { r = '0'+r; } g = g.toString(16); if (g.length==1) { g = '0'+g; } b = b.toString(16); if (b.length==1) { b = '0'+b; } return r+g+b; }, _hex2rgb : function(hex) { var m = hex.match(/#?([0-9a-f]{6}|[0-9a-f]{3})/i); if (m[1].length == 3) { m = m[1].match(/./g).filter(function(c) { return c+c; }); } else { m = m[1].match(/../g); } return { r : Number("0x" + m[0]), g : Number("0x" + m[1]), b : Number("0x" + m[2]) }; }, _rgb : function(r,g,b) { var ret = [r,g,b]; ret.r = r; ret.g = g; ret.b = b; return ret; }, _hsv : function(h,s,v) { var ret = [h,s,v]; ret.h = h; ret.s = s; ret.v = v; return ret; }, _onDownColor : function(e) { if (!e.mouse().left) { return false; } var pos = e.pos(); this._colPagePos = [pos.pageX, pos.pageY]; this._colLayerPos = [pos.layerX, pos.layerY]; this._onUpColorFn.attach(document, "mouseup"); this._onMoveColorFn.attach(document, "mousemove"); this._onMoveColor(e); }, _onUpColor : function(e) { this._onUpColorFn.detach(document, "mouseup"); this._onMoveColorFn.detach(document, "mousemove"); }, _onMoveColor : function(e) { var hsv = this._hsvColor; var pos = e.pos(); var x = this._colLayerPos[0] + (pos.pageX - this._colPagePos[0]); var y = this._colLayerPos[1] + (pos.pageY - this._colPagePos[1]); var w = this.elem.width(); var h = this.elem.height(); x = Math.max(Math.min(x, w), 0); y = Math.max(Math.min(y, h), 0); if (this.huePanel) { hsv.s = hsv[1] = x / w * 100; hsv.v = hsv[2] = (h - y) / h * 100; } else { hsv.h = y/h*360; var hw = w/2; if (x < hw) { hsv.s = x/hw * 100; hsv.v = 100; } else { hsv.s = 100; hsv.v = (w-x)/hw * 100; } } this.hsv(hsv); e.stop(); }, _onDownHue : function(e) { if (!e.mouse().left) { return false; } var pos = e.pos(); this._huePagePos = [pos.pageX, pos.pageY]; this._hueLayerPos = [pos.layerX, pos.layerY]; this._onUpHueFn.attach(document, "mouseup"); this._onMoveHueFn.attach(document, "mousemove"); this._onMoveHue(e); }, _onUpHue : function(e) { this._onUpHueFn.detach(document, "mouseup"); this._onMoveHueFn.detach(document, "mousemove"); }, _onMoveHue : function(e) { var hsv = this._hsvColor; var pos = e.pos(); var cur = 0, len = 0; var x = this._hueLayerPos[0] + (pos.pageX - this._huePagePos[0]); var y = this._hueLayerPos[1] + (pos.pageY - this._huePagePos[1]); if (this.option().huePanelType == "vertical") { cur = y; len = this.huePanel.height(); } else { cur = x; len = this.huePanel.width(); } hsv.h = hsv[0] = (Math.min(Math.max(cur, 0), len)/len * 360)%360; this.hsv(hsv); e.stop(); } }).extend(jindo.Component); //{ /** * @fileOverview This file contains Husky plugin that takes care of Accessibility about SmartEditor2. * @name hp_SE2M_Accessibility.js */ nhn.husky.SE2M_Accessibility = jindo.$Class({ name : "SE2M_Accessibility", /* * elAppContainer : mandatory * sLocale, sEditorType : optional */ $init: function(elAppContainer, sLocale, sEditorType) { this._assignHTMLElements(elAppContainer); if(!!sLocale){ this.sLang = sLocale; } if(!!sEditorType){ this.sType = sEditorType; } }, _assignHTMLElements : function(elAppContainer){ this.elHelpPopupLayer = jindo.$$.getSingle("DIV.se2_accessibility", elAppContainer); this.welHelpPopupLayer = jindo.$Element(this.elHelpPopupLayer); //close buttons this.oCloseButton = jindo.$$.getSingle("BUTTON.se2_close", this.elHelpPopupLayer); this.oCloseButton2 = jindo.$$.getSingle("BUTTON.se2_close2", this.elHelpPopupLayer); this.nDefaultTop = 150; // [SMARTEDITORSUS-1594] 포커스 탐색에 사용하기 위해 할당 this.elAppContainer = elAppContainer; // --[SMARTEDITORSUS-1594] }, $ON_MSG_APP_READY : function(){ this.htAccessOption = nhn.husky.SE2M_Configuration.SE2M_Accessibility || {}; this.oApp.exec("REGISTER_HOTKEY", ["alt+F10", "FOCUS_TOOLBAR_AREA", []]); this.oApp.exec("REGISTER_HOTKEY", ["alt+COMMA", "FOCUS_BEFORE_ELEMENT", []]); this.oApp.exec("REGISTER_HOTKEY", ["alt+PERIOD", "FOCUS_NEXT_ELEMENT", []]); if (this.sLang && this.sLang !== 'ko_KR') { //do nothing return; } else { this.oApp.exec("REGISTER_HOTKEY", ["alt+0", "OPEN_HELP_POPUP", []]); //[SMARTEDITORSUS-1327] IE 7/8에서 ALT+0으로 팝업 띄우고 esc클릭시 팝업창 닫히게 하려면 아래 부분 꼭 필요함. (target은 document가 되어야 함!) this.oApp.exec("REGISTER_HOTKEY", ["esc", "CLOSE_HELP_POPUP", [], document]); } //[SMARTEDITORSUS-1353] if (this.htAccessOption.sTitleElementId) { this.oApp.registerBrowserEvent(document.getElementById(this.htAccessOption.sTitleElementId), "keydown", "MOVE_TO_EDITAREA", []); } }, $ON_MOVE_TO_EDITAREA : function(weEvent) { var TAB_KEY_CODE = 9; if (weEvent.key().keyCode == TAB_KEY_CODE) { if(weEvent.key().shift) {return;} this.oApp.delayedExec("FOCUS", [], 0); } }, $LOCAL_BEFORE_FIRST : function(sMsg){ jindo.$Fn(jindo.$Fn(this.oApp.exec, this.oApp).bind("CLOSE_HELP_POPUP", [this.oCloseButton]), this).attach(this.oCloseButton, "click"); jindo.$Fn(jindo.$Fn(this.oApp.exec, this.oApp).bind("CLOSE_HELP_POPUP", [this.oCloseButton2]), this).attach(this.oCloseButton2, "click"); //레이어의 이동 범위 설정. var elIframe = this.oApp.getWYSIWYGWindow().frameElement; this.htOffsetPos = jindo.$Element(elIframe).offset(); this.nEditorWidth = elIframe.offsetWidth; this.htInitialPos = this.welHelpPopupLayer.offset(); var htScrollXY = this.oApp.oUtils.getScrollXY(); this.nLayerWidth = 590; this.nLayerHeight = 480; this.htTopLeftCorner = {x:parseInt(this.htOffsetPos.left, 10), y:parseInt(this.htOffsetPos.top, 10)}; //[css markup] left:11 top:74로 되어 있음 }, /** * [SMARTEDITORSUS-1594] * SE2M_Configuration_General에서 포커스를 이동할 에디터 영역 이후의 엘레먼트를 지정해 두었다면, 설정값을 따른다. * 지정하지 않았거나 빈 String이라면, elAppContainer를 기준으로 자동 탐색한다. * */ $ON_FOCUS_NEXT_ELEMENT : function() { // 포커스 캐싱 this._currentNextFocusElement = null; // 새로운 포커스 이동이 발생할 때마다 캐싱 초기화 if(this.htAccessOption.sNextElementId){ this._currentNextFocusElement = document.getElementById(this.htAccessOption.sNextElementId); }else{ this._currentNextFocusElement = this._findNextFocusElement(this.elAppContainer); } if(this._currentNextFocusElement){ window.focus(); // [SMARTEDITORSUS-1360] IE7에서는 element에 대한 focus를 주기 위해 선행되어야 한다. this._currentNextFocusElement.focus(); }else if(parent && parent.nhn && parent.nhn.husky && parent.nhn.husky.EZCreator && parent.nhn.husky.EZCreator.elIFrame){ parent.focus(); if(this._currentNextFocusElement = this._findNextFocusElement(parent.nhn.husky.EZCreator.elIFrame)){ this._currentNextFocusElement.focus(); } } }, /** * [SMARTEDITORSUS-1594] DIV#smart_editor2 다음 요소에서 가장 가까운 포커스용 태그를 탐색 * */ _findNextFocusElement : function(targetElement){ var target = null; var el = targetElement.nextSibling; while(el){ if(el.nodeType !== 1){ // Element Node만을 대상으로 한다. // 대상 노드 대신 nextSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. // document.body까지 거슬러 올라가게 되면 탐색 종료 el = this._switchToSiblingOrNothing(el); if(!el){ break; }else{ continue; } } // 대상 노드를 기준으로, 전위순회로 조건에 부합하는 노드 탐색 this._recursivePreorderTraversalFilter(el, this._isFocusTag); if(this._nextFocusElement){ target = this._nextFocusElement; // 탐색에 사용했던 변수 초기화 this._bStopFindingNextElement = false; this._nextFocusElement = null; break; }else{ // 대상 노드 대신 nextSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. // document.body까지 거슬러 올라가게 되면 탐색 종료 el = this._switchToSiblingOrNothing(el); if(!el){ break; } } } // target이 존재하지 않으면 null 반환 return target; }, /** * [SMARTEDITORSUS-1594] 대상 노드를 기준으로 하여, nextSibling 또는 previousSibling을 찾는다. * nextSibling 또는 previousSibling이 없다면, * 부모를 거슬러 올라가면서 첫 nextSibling 또는 previousSibling을 찾는다. * document의 body까지 올라가도 nextSibling 또는 previousSibling이 나타나지 않는다면 * 탐색 대상으로 null을 반환한다. * @param {NodeElement} 대상 노드 (주의:NodeElement에 대한 null 체크 안함) * @param {Boolean} 생략하거나 false이면 nextSibling을 찾고, true이면 previousSibling을 찾는다. * */ _switchToSiblingOrNothing : function(targetElement, isPreviousOrdered){ var el = targetElement; if(isPreviousOrdered){ if(el.previousSibling){ el = el.previousSibling; }else{ // 형제가 없다면 부모를 거슬러 올라가면서 탐색 // 이 루프의 종료 조건 // 1. 부모를 거슬러 올라가다가 el이 document.body가 되는 시점 // - 더 이상 previousSibling을 탐색할 수 없음 // 2. el이 부모로 대체된 뒤 previousSibling이 존재하는 경우 while(el.nodeName.toUpperCase() != "BODY" && !el.previousSibling){ el = el.parentNode; } if(el.nodeName.toUpperCase() == "BODY"){ el = null; }else{ el = el.previousSibling; } } }else{ if(el.nextSibling){ el = el.nextSibling; }else{ // 형제가 없다면 부모를 거슬러 올라가면서 탐색 // 이 루프의 종료 조건 // 1. 부모를 거슬러 올라가다가 el이 document.body가 되는 시점 // - 더 이상 nextSibling을 탐색할 수 없음 // 2. el이 부모로 대체된 뒤 nextSibling이 존재하는 경우 while(el.nodeName.toUpperCase() != "BODY" && !el.nextSibling){ el = el.parentNode; } if(el.nodeName.toUpperCase() == "BODY"){ el = null; }else{ el = el.nextSibling; } } } return el; }, /** * [SMARTEDITORSUS-1594] 대상 노드를 기준으로 하는 트리를 전위순회를 거쳐, 필터 조건에 부합하는 첫 노드를 찾는다. * @param {NodeElement} 탐색하려는 트리의 루트 노드 * @param {Function} 필터 조건으로 사용할 함수 * @param {Boolean} 생략하거나 false이면 순수 전위순회(루트 - 좌측 - 우측 순)로 탐색하고, true이면 반대 방향의 전위순회(루트 - 우측 - 좌측)로 탐색한다. * */ _recursivePreorderTraversalFilter : function(node, filterFunction, isReversed){ var self = this; // 현재 노드를 기준으로 필터링 var _bStopFindingNextElement = filterFunction.apply(node); if(_bStopFindingNextElement){ // 최초로 포커스 태그를 찾는다면 탐색 중단용 flag 변경 self._bStopFindingNextElement = true; if(isReversed){ self._previousFocusElement = node; }else{ self._nextFocusElement = node; } return; }else{ // 필터링 조건에 부합하지 않는다면, 자식들을 기준으로 반복하게 된다. if(isReversed){ for(var len = node.childNodes.length, i = len - 1; i >= 0; i--){ self._recursivePreorderTraversalFilter(node.childNodes[i], filterFunction, true); if(!!this._bStopFindingNextElement){ break; } } }else{ for(var i=0, len = node.childNodes.length; i < len; i++){ self._recursivePreorderTraversalFilter(node.childNodes[i], filterFunction); if(!!this._bStopFindingNextElement){ break; } } } } }, /** * [SMARTEDITORSUS-1594] 필터 함수로, 이 노드가 tab 키로 포커스를 이동하는 태그에 해당하는지 확인한다. * */ _isFocusTag : function(){ var self = this; // tab 키로 포커스를 잡아주는 태그 목록 var aFocusTagViaTabKey = ["A", "BUTTON", "INPUT", "TEXTAREA"]; // 포커스 태그가 현재 노드에 존재하는지 확인하기 위한 flag var bFocusTagExists = false; for(var i = 0, len = aFocusTagViaTabKey.length; i < len; i++){ if(self.nodeType === 1 && self.nodeName && self.nodeName.toUpperCase() == aFocusTagViaTabKey[i] && !self.disabled && jindo.$Element(self).visible()){ bFocusTagExists = true; break; } } return bFocusTagExists; }, /** * [SMARTEDITORSUS-1594] * SE2M_Configuration_General에서 포커스를 이동할 에디터 영역 이전의 엘레먼트를 지정해 두었다면, 설정값을 따른다. * 지정하지 않았거나 빈 String이라면, elAppContainer를 기준으로 자동 탐색한다. * */ $ON_FOCUS_BEFORE_ELEMENT : function() { // 포커스 캐싱 this._currentPreviousFocusElement = null; // 새로운 포커스 이동이 발생할 때마다 캐싱 초기화 if(this.htAccessOption.sBeforeElementId){ this._currentPreviousFocusElement = document.getElementById(this.htAccessOption.sBeforeElementId); }else{ this._currentPreviousFocusElement = this._findPreviousFocusElement(this.elAppContainer); // 삽입될 대상 } if(this._currentPreviousFocusElement){ window.focus(); // [SMARTEDITORSUS-1360] IE7에서는 element에 대한 focus를 주기 위해 선행되어야 한다. this._currentPreviousFocusElement.focus(); }else if(parent && parent.nhn && parent.nhn.husky && parent.nhn.husky.EZCreator && parent.nhn.husky.EZCreator.elIFrame){ parent.focus(); if(this._currentPreviousFocusElement = this._findPreviousFocusElement(parent.nhn.husky.EZCreator.elIFrame)){ this._currentPreviousFocusElement.focus(); } } }, /** * [SMARTEDITORSUS-1594] DIV#smart_editor2 이전 요소에서 가장 가까운 포커스용 태그를 탐색 * */ _findPreviousFocusElement : function(targetElement){ var target = null; var el = targetElement.previousSibling; while(el){ if(el.nodeType !== 1){ // Element Node만을 대상으로 한다. // 대상 노드 대신 previousSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. // document.body까지 거슬러 올라가게 되면 탐색 종료 el = this._switchToSiblingOrNothing(el, /*isReversed*/true); if(!el){ break; }else{ continue; } } // 대상 노드를 기준으로, 역 전위순회로 조건에 부합하는 노드 탐색 this._recursivePreorderTraversalFilter(el, this._isFocusTag, true); if(this._previousFocusElement){ target = this._previousFocusElement; // 탐색에 사용했던 변수 초기화 this._bStopFindingNextElement = false; this._previousFocusElement = null; break; }else{ // 대상 노드 대신 previousSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. // document.body까지 거슬러 올라가게 되면 탐색 종료 el = this._switchToSiblingOrNothing(el, /*isReversed*/true); if(!el){ break; } } } // target이 존재하지 않으면 null 반환 return target; }, $ON_FOCUS_TOOLBAR_AREA : function(){ this.oButton = jindo.$$.getSingle("BUTTON.se2_font_family", this.elAppContainer); if(this.oButton && !this.oButton.disabled){ // [SMARTEDITORSUS-1369] IE9이하에서 disabled 요소에 포커스를 주면 오류 발생 window.focus(); this.oButton.focus(); } }, $ON_OPEN_HELP_POPUP : function() { this.oApp.exec("DISABLE_ALL_UI", [{aExceptions: ["se2_accessibility"]}]); this.oApp.exec("SHOW_EDITING_AREA_COVER"); this.oApp.exec("SELECT_UI", ["se2_accessibility"]); //아래 코드 없어야 블로그에서도 동일한 위치에 팝업 뜸.. //this.elHelpPopupLayer.style.top = this.nDefaultTop+"px"; this.nCalcX = this.htTopLeftCorner.x + this.oApp.getEditingAreaWidth() - this.nLayerWidth; this.nCalcY = this.htTopLeftCorner.y - 30; // 블로그버전이 아닌 경우 에디터영역을 벗어나는 문제가 있기 때문에 기본툴바(30px) 크기만큼 올려줌 this.oApp.exec("SHOW_DIALOG_LAYER", [this.elHelpPopupLayer, { elHandle: this.elTitle, nMinX : this.htTopLeftCorner.x + 0, nMinY : this.nDefaultTop + 77, nMaxX : this.nCalcX, nMaxY : this.nCalcY }]); // offset (nTop:Numeric, nLeft:Numeric) this.welHelpPopupLayer.offset(this.nCalcY, (this.nCalcX)/2); //[SMARTEDITORSUS-1327] IE에서 포커스 이슈로 IE에 대해서만 window.focus실행함. if(jindo.$Agent().navigator().ie) { window.focus(); } var self = this; setTimeout(function(){ try{ self.oCloseButton2.focus(); }catch(e){ } },200); }, $ON_CLOSE_HELP_POPUP : function() { this.oApp.exec("ENABLE_ALL_UI"); // 모든 UI 활성화. this.oApp.exec("DESELECT_UI", ["helpPopup"]); this.oApp.exec("HIDE_ALL_DIALOG_LAYER", []); this.oApp.exec("HIDE_EDITING_AREA_COVER"); // 편집 영역 활성화. this.oApp.exec("FOCUS"); } }); //} /** * @fileOverview This file contains Husky plugin that takes care of the operations related to inserting special characters * @name hp_SE2M_SCharacter.js * @required HuskyRangeManager */ nhn.husky.SE2M_SCharacter = jindo.$Class({ name : "SE2M_SCharacter", $ON_MSG_APP_READY : function(){ this.oApp.exec("REGISTER_UI_EVENT", ["sCharacter", "click", "TOGGLE_SCHARACTER_LAYER"]); this.oApp.registerLazyMessage(["TOGGLE_SCHARACTER_LAYER"], ["hp_SE2M_SCharacter$Lazy.js"]); } }); /** * @fileOverview This file contains Husky plugin that takes care of the operations related to Find/Replace * @name hp_SE2M_FindReplacePlugin.js */ nhn.husky.SE2M_FindReplacePlugin = jindo.$Class({ name : "SE2M_FindReplacePlugin", oEditingWindow : null, oFindReplace : null, bFindMode : true, bLayerShown : false, $init : function(){ this.nDefaultTop = 20; }, $ON_MSG_APP_READY : function(){ // the right document will be available only when the src is completely loaded this.oEditingWindow = this.oApp.getWYSIWYGWindow(); // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 if (jindo.$Agent().os().mac) { this.oApp.exec("REGISTER_HOTKEY", ["meta+f", "SHOW_FIND_LAYER", []]); this.oApp.exec("REGISTER_HOTKEY", ["meta+h", "SHOW_REPLACE_LAYER", []]); } else { this.oApp.exec("REGISTER_HOTKEY", ["ctrl+f", "SHOW_FIND_LAYER", []]); this.oApp.exec("REGISTER_HOTKEY", ["ctrl+h", "SHOW_REPLACE_LAYER", []]); } this.oApp.exec("REGISTER_UI_EVENT", ["findAndReplace", "click", "TOGGLE_FIND_REPLACE_LAYER"]); this.oApp.registerLazyMessage(["TOGGLE_FIND_REPLACE_LAYER","SHOW_FIND_LAYER","SHOW_REPLACE_LAYER","SHOW_FIND_REPLACE_LAYER"], ["hp_SE2M_FindReplacePlugin$Lazy.js","N_FindReplace.js"]); }, $ON_SHOW_ACTIVE_LAYER : function(){ this.oApp.exec("HIDE_DIALOG_LAYER", [this.elDropdownLayer]); } }); /** * @fileOverview This file contains Husky plugin that takes care of the operations related to quote * @name hp_SE_Quote.js * @required SE_EditingArea_WYSIWYG */ nhn.husky.SE2M_Quote = jindo.$Class({ name : "SE2M_Quote", htQuoteStyles_view : null, $init : function(){ var htConfig = nhn.husky.SE2M_Configuration.Quote || {}; var sImageBaseURL = htConfig.sImageBaseURL; this.nMaxLevel = htConfig.nMaxLevel || 14; this.htQuoteStyles_view = {}; this.htQuoteStyles_view["se2_quote1"] = "_zoom:1;padding:0 8px; margin:0 0 30px 20px; margin-right:15px; border-left:2px solid #cccccc;color:#888888;"; this.htQuoteStyles_view["se2_quote2"] = "_zoom:1;margin:0 0 30px 13px;padding:0 8px 0 16px;background:url("+sImageBaseURL+"/bg_quote2.gif) 0 3px no-repeat;color:#888888;"; this.htQuoteStyles_view["se2_quote3"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #cccccc;color:#888888;"; this.htQuoteStyles_view["se2_quote4"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #66b246;color:#888888;"; this.htQuoteStyles_view["se2_quote5"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #cccccc;background:url("+sImageBaseURL+"/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+sImageBaseURL+"/bg_b1.png',sizingMethod='scale');color:#888888;"; this.htQuoteStyles_view["se2_quote6"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #e5e5e5;color:#888888;"; this.htQuoteStyles_view["se2_quote7"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #66b246;color:#888888;"; this.htQuoteStyles_view["se2_quote8"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #e5e5e5;background:url("+sImageBaseURL+"/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+sImageBaseURL+"/bg_b1.png',sizingMethod='scale');color:#888888;"; this.htQuoteStyles_view["se2_quote9"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:2px solid #e5e5e5;color:#888888;"; this.htQuoteStyles_view["se2_quote10"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:2px solid #e5e5e5;background:url("+sImageBaseURL+"/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+sImageBaseURL+"/bg_b1.png',sizingMethod='scale');color:#888888;"; }, _assignHTMLElements : function(){ //@ec this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_seditor_blockquote_layer", this.oApp.htOptions.elAppContainer); this.aLI = jindo.$$("LI", this.elDropdownLayer); }, $ON_REGISTER_CONVERTERS : function(){ this.oApp.exec("ADD_CONVERTER", ["DB_TO_IR", jindo.$Fn(function(sContents){ sContents = sContents.replace(/<(blockquote)[^>]*class=['"]?(se2_quote[0-9]+)['"]?[^>]*>/gi, "<$1 class=$2>"); return sContents; }, this).bind()]); this.oApp.exec("ADD_CONVERTER", ["IR_TO_DB", jindo.$Fn(function(sContents){ var htQuoteStyles_view = this.htQuoteStyles_view; sContents = sContents.replace(/<(blockquote)[^>]*class=['"]?(se2_quote[0-9]+)['"]?[^>]*>/gi, function(sAll, sTag, sClassName){ return '<'+sTag+' class='+sClassName+' style="'+htQuoteStyles_view[sClassName]+'">'; }); return sContents; }, this).bind()]); this.htSE1toSE2Map = { "01" : "1", "02" : "2", "03" : "6", "04" : "8", "05" : "9", "07" : "3", "08" : "5" }; // convert SE1's quotes to SE2's // -> 블로그 개발 쪽에서 처리 하기로 함. /* this.oApp.exec("ADD_CONVERTER", ["DB_TO_IR", jindo.$Fn(function(sContents){ return sContents.replace(/]* class="?vview_quote([0-9]+)"?[^>]*>((?:\s|.)*?)<\/blockquote>/ig, jindo.$Fn(function(m0,sQuoteType,sQuoteContents){ if (/((?:\s|.)*?)/ig.test(sQuoteContents)){ if(!this.htSE1toSE2Map[sQuoteType]){ return m0; } return '
'+RegExp.$1+'
'; }else{ return ''; } }, this).bind()); }, this).bind()]); */ }, $LOCAL_BEFORE_FIRST : function(){ this._assignHTMLElements(); this.oApp.registerBrowserEvent(this.elDropdownLayer, "click", "EVENT_SE2_BLOCKQUOTE_LAYER_CLICK", []); this.oApp.delayedExec("SE2_ATTACH_HOVER_EVENTS", [this.aLI], 0); }, $ON_MSG_APP_READY: function(){ this.oApp.exec("REGISTER_UI_EVENT", ["quote", "click", "TOGGLE_BLOCKQUOTE_LAYER"]); this.oApp.registerLazyMessage(["TOGGLE_BLOCKQUOTE_LAYER"], ["hp_SE2M_Quote$Lazy.js"]); }, // [SMARTEDITORSUS-209] 인용구 내에 내용이 없을 때 Backspace 로 인용구가 삭제되도록 처리 $ON_EVENT_EDITING_AREA_KEYDOWN : function(weEvent) { var oSelection, elParentQuote; if ('WYSIWYG' !== this.oApp.getEditingMode()){ return; } if(8 !== weEvent.key().keyCode){ return; } oSelection = this.oApp.getSelection(); oSelection.fixCommonAncestorContainer(); elParentQuote = this._findParentQuote(oSelection.commonAncestorContainer); if(!elParentQuote){ return; } if(this._isBlankQuote(elParentQuote)){ weEvent.stop(jindo.$Event.CANCEL_DEFAULT); oSelection.selectNode(elParentQuote); oSelection.collapseToStart(); jindo.$Element(elParentQuote).leave(); oSelection.select(); } }, // [SMARTEDITORSUS-215] Delete 로 인용구 뒤의 P 가 제거되지 않도록 처리 $ON_EVENT_EDITING_AREA_KEYUP : function(weEvent) { var oSelection, elParentQuote, oP; if ('WYSIWYG' !== this.oApp.getEditingMode()){ return; } if(46 !== weEvent.key().keyCode){ return; } oSelection = this.oApp.getSelection(); oSelection.fixCommonAncestorContainer(); elParentQuote = this._findParentQuote(oSelection.commonAncestorContainer); if(!elParentQuote){ return false; } if(!elParentQuote.nextSibling){ weEvent.stop(jindo.$Event.CANCEL_DEFAULT); oP = oSelection._document.createElement("P"); oP.innerHTML = " "; jindo.$Element(elParentQuote).after(oP); setTimeout(jindo.$Fn(function(oSelection){ var sBookmarkID = oSelection.placeStringBookmark(); oSelection.select(); oSelection.removeStringBookmark(sBookmarkID); },this).bind(oSelection), 0); } }, _isBlankQuote : function(elParentQuote){ var elChild, aChildNodes, i, nLen, bChrome = this.oApp.oNavigator.chrome, bSafari = this.oApp.oNavigator.safari, isBlankText = function(sText){ sText = sText.replace(/[\r\n]/ig, '').replace(unescape("%uFEFF"), ''); if(sText === ""){ return true; } if(sText === " " || sText === " "){ // [SMARTEDITORSUS-479] return true; } return false; }, isBlank = function(oNode){ if(oNode.nodeType === 3 && isBlankText(oNode.nodeValue)){ return true; } if((oNode.tagName === "P" || oNode.tagName === "SPAN") && (isBlankText(oNode.innerHTML) || oNode.innerHTML === "
")){ return true; } return false; }, isBlankTable = function(oNode){ if((jindo.$$("tr", oNode)).length === 0){ return true; } return false; }; if(isBlankText(elParentQuote.innerHTML) || elParentQuote.innerHTML === "
"){ return true; } if(bChrome || bSafari){ // [SMARTEDITORSUS-352], [SMARTEDITORSUS-502] var aTable = jindo.$$("TABLE", elParentQuote), nTable = aTable.length, elTable; for(i=0; i 속성 직접입력 > 테두리 스타일 // - 테두리 없음을 선택하는 경우 본문에 삽입하는 표에 가이드 라인을 표시해 줍니다. 보기 시에는 테두리가 보이지 않습니다. $ON_REGISTER_CONVERTERS : function(){ this.oApp.exec("ADD_CONVERTER_DOM", ["IR_TO_DB", jindo.$Fn(this.irToDbDOM, this).bind()]); this.oApp.exec("ADD_CONVERTER_DOM", ["DB_TO_IR", jindo.$Fn(this.dbToIrDOM, this).bind()]); }, irToDbDOM : function(oTmpNode){ /** * 저장을 위한 Table Tag 는 아래와 같이 변경됩니다. * (1) *
* -->
* (2) * --> */ var aNoBorderTable = []; var aTables = jindo.$$('table[class=__se_tbl]', oTmpNode, {oneTimeOffCache:true}); // 테두리가 없음 속성의 table (임의로 추가한 attr_no_border_tbl 속성이 있는 table 을 찾음) jindo.$A(aTables).forEach(function(oValue, nIdx, oArray) { if(jindo.$Element(oValue).attr("attr_no_border_tbl")){ aNoBorderTable.push(oValue); } }, this); if(aNoBorderTable.length < 1){ return; } // [SMARTEDITORSUS-410] 글 저장 시, 테두리 없음 속성을 선택할 때 임의로 표시한 가이드 라인 property 만 style 에서 제거해 준다. //
*

 

 
의 속성값을 변경 및 제거 var aTDs = [], oTable; for(var i = 0, nCount = aNoBorderTable.length; i < nCount; i++){ oTable = aNoBorderTable[i]; // 에서 border, cellpadding 속성값 변경, style property 제거 jindo.$Element(oTable).css({"border": "", "borderLeft": "", "borderBottom": ""}); jindo.$Element(oTable).attr({"border": 0, "cellpadding": 1}); //
에서는 background-color 를 제외한 style 을 모두 제거 aTDs = jindo.$$('tbody>tr>td', oTable); jindo.$A(aTDs).forEach(function(oTD, nIdx, oTDArray) { jindo.$Element(oTD).css({"border": "", "borderTop": "", "borderRight": ""}); }); } }, dbToIrDOM : function(oTmpNode){ /** * 수정을 위한 Table Tag 는 아래와 같이 변경됩니다. * (1) *
* -->
* (2) * --> */ var aNoBorderTable = []; var aTables = jindo.$$('table[class=__se_tbl]', oTmpNode, {oneTimeOffCache:true}); // 테두리가 없음 속성의 table (임의로 추가한 attr_no_border_tbl 속성이 있는 table 을 찾음) jindo.$A(aTables).forEach(function(oValue, nIdx, oArray) { if(jindo.$Element(oValue).attr("attr_no_border_tbl")){ aNoBorderTable.push(oValue); } }, this); if(aNoBorderTable.length < 1){ return; } //
*  

 

의 속성값을 변경/추가 var aTDs = [], oTable; for(var i = 0, nCount = aNoBorderTable.length; i < nCount; i++){ oTable = aNoBorderTable[i]; // 에서 border, cellpadding 속성값 변경/ style 속성 추가 jindo.$Element(oTable).css({"border": "1px dashed #c7c7c7", "borderLeft": 0, "borderBottom": 0}); jindo.$Element(oTable).attr({"border": 1, "cellpadding": 0}); //
에서 style 속성값 추가 aTDs = jindo.$$('tbody>tr>td', oTable); jindo.$A(aTDs).forEach(function(oTD, nIdx, oTDArray) { jindo.$Element(oTD).css({"border": "1px dashed #c7c7c7", "borderTop": 0, "borderRight": 0}); }); } } }); /** * @fileOverview This file contains Husky plugin that takes care of the operations related to changing the font style in the table. * @requires SE2M_TableEditor.js * @name SE2M_TableBlockManager */ nhn.husky.SE2M_TableBlockStyler = jindo.$Class({ name : "SE2M_TableBlockStyler", nSelectedTD : 0, htSelectedTD : {}, aTdRange : [], $init : function(){ }, $LOCAL_BEFORE_ALL : function(){ return (this.oApp.getEditingMode() == "WYSIWYG"); }, $ON_MSG_APP_READY : function(){ this.oDocument = this.oApp.getWYSIWYGDocument(); }, $ON_EVENT_EDITING_AREA_MOUSEUP : function(wevE){ if(this.oApp.getEditingMode() != "WYSIWYG") return; this.setTdBlock(); }, /** * selected Area가 td block인지 체크하는 함수. */ $ON_IS_SELECTED_TD_BLOCK : function(sAttr,oReturn) { if( this.nSelectedTD > 0){ oReturn[sAttr] = true; return oReturn[sAttr]; }else{ oReturn[sAttr] = false; return oReturn[sAttr]; } }, /** * */ $ON_GET_SELECTED_TD_BLOCK : function(sAttr,oReturn){ //use : this.oApp.exec("GET_SELECTED_TD_BLOCK",['aCells',this.htSelectedTD]); oReturn[sAttr] = this.htSelectedTD.aTdCells; }, setTdBlock : function() { this.oApp.exec("GET_SELECTED_CELLS",['aTdCells',this.htSelectedTD]); //tableEditor로 부터 얻어온다. var aNodes = this.htSelectedTD.aTdCells; if(aNodes){ this.nSelectedTD = aNodes.length; } }, $ON_DELETE_BLOCK_CONTENTS : function(){ var self = this, welParent, oBlockNode, oChildNode; this.setTdBlock(); for (var j = 0; j < this.nSelectedTD ; j++){ jindo.$Element(this.htSelectedTD.aTdCells[j]).child( function(elChild){ welParent = jindo.$Element(elChild._element.parentNode); welParent.remove(elChild); oBlockNode = self.oDocument.createElement('P'); if (jindo.$Agent().navigator().firefox) { oChildNode = self.oDocument.createElement('BR'); } else { oChildNode = self.oDocument.createTextNode('\u00A0'); } oBlockNode.appendChild(oChildNode); welParent.append(oBlockNode); }, 1); } } }); //{ /** * @fileOverview This file contains Husky plugin with test handlers * @name hp_SE2M_StyleRemover.js */ nhn.husky.SE2M_StyleRemover = jindo.$Class({ name: "SE2M_StyleRemover", $ON_MSG_APP_READY : function(){ this.oApp.exec("REGISTER_UI_EVENT", ["styleRemover", "click", "CHOOSE_REMOVE_STYLE", []]); }, $LOCAL_BEFORE_FIRST : function(){ // The plugin may be used in view and when it is used there, EditingAreaManager plugin is not loaded. // So, get the document from the selection instead of EditingAreaManager. this.oHuskyRange = this.oApp.getEmptySelection(); this._document = this.oHuskyRange._document; }, $ON_CHOOSE_REMOVE_STYLE : function(oSelection){ var bSelectedBlock = false; var htSelectedTDs = {}; this.oApp.exec("IS_SELECTED_TD_BLOCK",['bIsSelectedTd',htSelectedTDs]); bSelectedBlock = htSelectedTDs.bIsSelectedTd; this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["REMOVE STYLE", {bMustBlockElement:true}]); if( bSelectedBlock ){ this.oApp.exec("REMOVE_STYLE_IN_BLOCK", []); }else{ this.oApp.exec("REMOVE_STYLE", []); } this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["REMOVE STYLE", {bMustBlockElement:true}]); this.oApp.exec('MSG_NOTIFY_CLICKCR', ['noeffect']); }, $ON_REMOVE_STYLE_IN_BLOCK : function(oSelection){ var htSelectedTDs = {}; this.oSelection = this.oApp.getSelection(); this.oApp.exec("GET_SELECTED_TD_BLOCK",['aTdCells',htSelectedTDs]); var aNodes = htSelectedTDs.aTdCells; for( var j = 0; j < aNodes.length ; j++){ this.oSelection.selectNodeContents(aNodes[j]); this.oSelection.select(); this.oApp.exec("REMOVE_STYLE", []); } }, $ON_REMOVE_STYLE : function(oSelection){ if(!oSelection || !oSelection.commonAncestorContainer){ oSelection = this.oApp.getSelection(); } if(oSelection.collapsed){return;} oSelection.expandBothEnds(); var sBookmarkID = oSelection.placeStringBookmark(); var aNodes = oSelection.getNodes(true); this._removeStyle(aNodes); oSelection.moveToBookmark(sBookmarkID); var aNodes = oSelection.getNodes(true); for(var i=0; i 테이블 외부로 커서 이동 불가 if( htBrowser.ie ){ sHtml += "

 

"; }else if(htBrowser.firefox){ //[SMARTEDITORSUS-477][개별블로그](파폭특정)포스트쓰기>요약글을 삽입 후 요약글 아래 임의의 본문영역에 마우스 클릭 시 커서가 요약안에 노출됩니다. // 본문에 table만 있는 경우, 커서가 밖으로 못나오는 현상이 있음.FF버그임. sHtml += "



"; } oSelection.selectNode(elDivHolder); oSelection.pasteHTML(sHtml); //Table인경우, 커서를 테이블 첫 TD에 넣기 위한 작업. aNodesInSelection = oSelection.getNodes() || []; for(var i = 0; i < aNodesInSelection.length ; i++){ if(!!aNodesInSelection[i].tagName && aNodesInSelection[i].tagName.toLowerCase() == 'td'){ elFirstTD = aNodesInSelection[i]; oSelection.selectNodeContents(elFirstTD.firstChild || elFirstTD); oSelection.collapseToStart(); oSelection.select(); break; } } oSelection.collapseToEnd(); //파란색 커버 제거. oSelection.select(); this.oApp.exec("FOCUS"); if (!elDivHolder) {// 임시 div 삭제. elDivHolder.parentNode.removeChild(elDivHolder); } if(!bNoUndo){ this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["REMOVE STYLE AND PASTE HTML"]); } }, _removeStyle : function(aNodes){ var arNodes = jindo.$A(aNodes); for(var i=0; i current node will be handled in the next iteration if(arNodes.indexOf(this._getVeryLast(oNode.nextSibling)) != -1){continue;} // Since the case // 1. oNode is the right most node // 2. oNode's got a right sibling that is included in the selection // were all taken care of above, so from here we just need take care of the case when oNode is NOT the right most node and oNode's right sibling is NOT included in the selection // Case 3: the rest // When all of the left siblings were selected, take all the left siblings and current node and append them to the left of the parent node. // H // | // P // / | | \ // A B C D // -> if A, B and C were selected, then make it // H // / | | \ // A B C P // | // D i--; // bring up selected prev siblings if(arNodes.indexOf(this._getVeryFirst(oNode.parentNode)) != -1){ // move var tmp = oNode; var lastInserted = parent; while(tmp){ var prevNode = tmp.previousSibling; parent.parentNode.insertBefore(tmp, lastInserted); lastInserted = tmp; if(!prevNode){break;} tmp = prevNode; } if(parent.childNodes.length === 0){parent.parentNode.removeChild(parent);} // When NOT all of the left siblings were selected, split the parent node and insert the selected nodes in between. // H // | // P // / | | \ // A B C D // -> if B and C were selected, then make it // H // / | | \ // P B C P // | | // A D }else{ //split if(bDontSplit){ i++; continue; } var oContainer = this._document.createElement("SPAN"); var tmp = oNode; parent.insertBefore(oContainer, tmp.nextSibling); while(tmp){ var prevNode = tmp.previousSibling; oContainer.insertBefore(tmp, oContainer.firstChild); if(!prevNode){break;} if(arNodes.indexOf(this._getVeryFirst(prevNode)) == -1){break;} tmp = prevNode; } this._splitAndAppendAtTop(oContainer); while(oContainer.firstChild){ oContainer.parentNode.insertBefore(oContainer.firstChild, oContainer); } oContainer.parentNode.removeChild(oContainer); } } }, _splitAndAppendAtTop : function(oSpliter){ var targetNode = oSpliter; var oTmp = targetNode; var oCopy = oTmp; while(jindo.$Element(oTmp.parentNode).css("display") == "inline"){ var oNode = oTmp.parentNode.cloneNode(false); while(oTmp.nextSibling){ oNode.appendChild(oTmp.nextSibling); } oTmp = oTmp.parentNode; oNode.insertBefore(oCopy, oNode.firstChild); oCopy = oNode; } oTop = oTmp.parentNode; oTop.insertBefore(targetNode, oTmp.nextSibling); oTop.insertBefore(oCopy, targetNode.nextSibling); }, _getVeryFirst : function(oNode){ if(!oNode){return null;} if(oNode.firstChild){ return this.oHuskyRange._getVeryFirstRealChild(oNode); }else{ return oNode; } }, _getVeryLast : function(oNode){ if(!oNode){return null;} if(oNode.lastChild){ return this.oHuskyRange._getVeryLastRealChild(oNode); }else{ return oNode; } } }); //} nhn.husky.SE2M_TableEditor = jindo.$Class({ name : "SE2M_TableEditor", _sSETblClass : "__se_tbl", _sSEReviewTblClass : "__se_tbl_review", STATUS : { S_0 : 1, // neither cell selection nor cell resizing is active MOUSEDOWN_CELL : 2, // mouse down on a table cell CELL_SELECTING : 3, // cell selection is in progress CELL_SELECTED : 4, // cell selection was (completely) made MOUSEOVER_BORDER : 5, // mouse is over a table/cell border and the cell resizing grip is shown MOUSEDOWN_BORDER : 6 // mouse down on the cell resizing grip (cell resizing is in progress) }, CELL_SELECTION_CLASS : "se2_te_selection", MIN_CELL_WIDTH : 5, MIN_CELL_HEIGHT : 5, TMP_BGC_ATTR : "_se2_tmp_te_bgc", TMP_BGIMG_ATTR : "_se2_tmp_te_bg_img", ATTR_TBL_TEMPLATE : "_se2_tbl_template", nStatus : 1, nMouseEventsStatus : 0, aSelectedCells : [], $ON_REGISTER_CONVERTERS : function(){ // remove the cell selection class this.oApp.exec("ADD_CONVERTER_DOM", ["WYSIWYG_TO_IR", jindo.$Fn(function(elTmpNode){ if(this.aSelectedCells.length < 1){ //return sContents; return; } var aCells; var aCellType = ["TD", "TH"]; for(var n = 0; n < aCellType.length; n++){ aCells = elTmpNode.getElementsByTagName(aCellType[n]); for(var i = 0, nLen = aCells.length; i < nLen; i++){ if(aCells[i].className){ aCells[i].className = aCells[i].className.replace(this.CELL_SELECTION_CLASS, ""); if(aCells[i].getAttribute(this.TMP_BGC_ATTR)){ aCells[i].style.backgroundColor = aCells[i].getAttribute(this.TMP_BGC_ATTR); aCells[i].removeAttribute(this.TMP_BGC_ATTR); }else if(aCells[i].getAttribute(this.TMP_BGIMG_ATTR)){ jindo.$Element(this.aCells[i]).css("backgroundImage",aCells[i].getAttribute(this.TMP_BGIMG_ATTR)); aCells[i].removeAttribute(this.TMP_BGIMG_ATTR); } } } } // this.wfnMouseDown.attach(this.elResizeCover, "mousedown"); // return elTmpNode.innerHTML; // var rxSelectionColor = new RegExp("<(TH|TD)[^>]*)("+this.TMP_BGC_ATTR+"=[^> ]*)([^>]*>)", "gi"); }, this).bind()]); }, $ON_MSG_APP_READY : function(){ this.oApp.registerLazyMessage(["EVENT_EDITING_AREA_MOUSEMOVE","STYLE_TABLE"], ["hp_SE2M_TableEditor$Lazy.js","SE2M_TableTemplate.js"]); } }); /** * @name SE2M_QuickEditor_Common * @class * @description Quick Editor Common function Class * @author NHN AjaxUI Lab - mixed * @version 1.0 * @since 2009.09.29 * */ nhn.husky.SE2M_QuickEditor_Common = jindo.$Class({ /** * class 이름 * @type {String} */ name : "SE2M_QuickEditor_Common", /** * 환경 정보. * @type {Object} */ _environmentData : "", /** * 현재 타입 (table|img) * @type {String} */ _currentType :"", /** * 이벤트가 레이어 안에서 호출되었는지 알기 위한 변수 * @type {Boolean} */ _in_event : false, /** * Ajax처리를 하지 않음 * @type {Boolean} */ _bUseConfig : false, /** * 공통 서버에서 개인 설정 받아오는 AjaxUrl * @See SE2M_Configuration.js */ _sBaseAjaxUrl : "", _sAddTextAjaxUrl : "", /** * 초기 인스턴스 생성 실행되는 함수. */ $init : function() { this.waHotkeys = new jindo.$A([]); this.waHotkeyLayers = new jindo.$A([]); }, $ON_MSG_APP_READY : function() { var htConfiguration = nhn.husky.SE2M_Configuration.QuickEditor; if(htConfiguration){ this._bUseConfig = (!!htConfiguration.common && typeof htConfiguration.common.bUseConfig !== "undefined") ? htConfiguration.common.bUseConfig : true; } if(!this._bUseConfig){ this.setData("{table:'full',img:'full',review:'full'}"); } else { this._sBaseAjaxUrl = htConfiguration.common.sBaseAjaxUrl; this._sAddTextAjaxUrl = htConfiguration.common.sAddTextAjaxUrl; this.getData(); } this.oApp.registerLazyMessage(["OPEN_QE_LAYER"], ["hp_SE2M_QuickEditor_Common$Lazy.js"]); }, //삭제 시에 qe layer close $ON_EVENT_EDITING_AREA_KEYDOWN : function(oEvent){ var oKeyInfo = oEvent.key(); //Backspace : 8, Delete :46 if (oKeyInfo.keyCode == 8 || oKeyInfo.keyCode == 46 ) { // [SMARTEDITORSUS-1213][IE9, 10] 사진 삭제 후 zindex 1000인 div가 잔존하는데, 그 위로 썸네일 drag를 시도하다 보니 drop이 불가능. var htBrowser = jindo.$Agent().navigator(); if(htBrowser.ie && htBrowser.nativeVersion > 8){ var elFirstChild = jindo.$$.getSingle("DIV.husky_seditor_editing_area_container").childNodes[0]; if((elFirstChild.tagName == "DIV") && (elFirstChild.style.zIndex == 1000)){ elFirstChild.parentNode.removeChild(elFirstChild); } } // --[SMARTEDITORSUS-1213] this.oApp.exec("CLOSE_QE_LAYER", [oEvent]); } }, getData : function() { var self = this; jindo.$Ajax(self._sBaseAjaxUrl, { type : "jsonp", timeout : 1, onload: function(rp) { var result = rp.json().result; // [SMARTEDITORSUS-1028][SMARTEDITORSUS-1517] QuickEditor 설정 API 개선 //if (!!result && !!result.length) { if (!!result && !!result.text_data) { //self.setData(result[result.length - 1]); self.setData(result.text_data); } else { self.setData("{table:'full',img:'full',review:'full'}"); } // --[SMARTEDITORSUS-1028][SMARTEDITORSUS-1517] }, onerror : function() { self.setData("{table:'full',img:'full',review:'full'}"); }, ontimeout : function() { self.setData("{table:'full',img:'full',review:'full'}"); } }).request({ text_key : "qeditor_fold" }); }, setData : function(sResult){ var oResult = { table : "full", img : "full", review : "full" }; if(sResult){ oResult = eval("("+sResult+")"); } this._environmentData = { table : { isOpen : false, type : oResult["table"],//full,fold, isFixed : false, position : [] }, img : { isOpen : false, type : oResult["img"],//full,fold isFixed : false }, review : { isOpen : false, type : oResult["review"],//full,fold isFixed : false, position : [] } }; this.waTableTagNames =jindo.$A(["table","tbody","td","tfoot","th","thead","tr"]); }, /** * 위지윅 영역에 단축키가 등록될 때, * tab 과 shift+tab (들여쓰기 / 내어쓰기 ) 를 제외한 단축키 리스트를 저장한다. */ $ON_REGISTER_HOTKEY : function(sHotkey, sCMD, aArgs){ if(sHotkey != "tab" && sHotkey != "shift+tab"){ this.waHotkeys.push([sHotkey, sCMD, aArgs]); } } }); /** * @classDescription shortcut * @author AjaxUI Lab - mixed */ function Shortcut(sKey,sId){ var sKey = sKey.replace(/\s+/g,""); var store = Shortcut.Store; var action = Shortcut.Action; if(typeof sId === "undefined"&&sKey.constructor == String){ store.set("document",sKey,document); return action.init(store.get("document"),sKey); }else if(sId.constructor == String&&sKey.constructor == String){ store.set(sId,sKey,jindo.$(sId)); return action.init(store.get(sId),sKey); }else if(sId.constructor != String&&sKey.constructor == String){ var fakeId = "nonID"+new Date().getTime(); fakeId = Shortcut.Store.searchId(fakeId,sId); store.set(fakeId,sKey,sId); return action.init(store.get(fakeId),sKey); } alert(sId+"는 반드시 string이거나 없어야 됩니다."); }; Shortcut.Store = { anthorKeyHash:{}, datas:{}, currentId:"", currentKey:"", searchId:function(sId,oElement){ // [SMARTEDITORSUS-2103] var isElementInData = false; jindo.$H(this.datas).forEach(function(oValue,sKey){ if(oElement == oValue.element){ sId = sKey; isElementInData = true; jindo.$H.Break(); } }); /* * element context는 서로 다르지만 * 이미 추가된 Shortcut.Data 객체와 timestamp가 중복되는 경우, * * 원래 동작해야 할 element context가 아닌, * 이미 추가된 element context에서 잘못 동작할 수 있다. * * 이 경우에는 timestamp 기반 id를 새롭개 생성해서 * 원래 동작해야 할 element context에서 잘 동작할 수 있도록 한다. */ if(!isElementInData && (sId in this.datas)){ var newFakeId = sId; while(newFakeId in this.datas){ newFakeId = "nonID"+new Date().getTime(); } return newFakeId; } return sId; // --[SMARTEDITORSUS-2103] }, set:function(sId,sKey,oElement){ this.currentId = sId; this.currentKey = sKey; var idData = this.get(sId); this.datas[sId] = idData?idData.createKey(sKey):new Shortcut.Data(sId,sKey,oElement); }, get:function(sId,sKey){ if(sKey){ return this.datas[sId].keys[sKey]; }else{ return this.datas[sId]; } }, reset:function(sId){ var data = this.datas[sId]; Shortcut.Helper.bind(data.func,data.element,"detach"); delete this.datas[sId]; }, allReset: function(){ jindo.$H(this.datas).forEach(jindo.$Fn(function(value,key) { this.reset(key); },this).bind()); } }; Shortcut.Data = jindo.$Class({ $init:function(sId,sKey,oElement){ this.id = sId; this.element = oElement; this.func = jindo.$Fn(this.fire,this).bind(); Shortcut.Helper.bind(this.func,oElement,"attach"); this.keys = {}; this.keyStemp = {}; this.createKey(sKey); }, createKey:function(sKey){ this.keyStemp[Shortcut.Helper.keyInterpretor(sKey)] = sKey; this.keys[sKey] = {}; var data = this.keys[sKey]; data.key = sKey; data.events = []; data.commonExceptions = []; // data.keyAnalysis = Shortcut.Helper.keyInterpretor(sKey); data.stopDefalutBehavior = true; return this; }, getKeyStamp : function(eEvent){ var sKey = eEvent.keyCode || eEvent.charCode; var returnVal = ""; returnVal += eEvent.altKey?"1":"0"; returnVal += eEvent.ctrlKey?"1":"0"; returnVal += eEvent.metaKey?"1":"0"; returnVal += eEvent.shiftKey?"1":"0"; returnVal += sKey; return returnVal; }, fire:function(eEvent){ eEvent = eEvent||window.eEvent; var oMatchKeyData = this.keyStemp[this.getKeyStamp(eEvent)]; if(oMatchKeyData){ this.excute(new jindo.$Event(eEvent),oMatchKeyData); } }, excute:function(weEvent,sRawKey){ var isExcute = true; var staticFun = Shortcut.Helper; var data = this.keys[sRawKey]; if(staticFun.notCommonException(weEvent,data.commonExceptions)){ jindo.$A(data.events).forEach(function(v){ if(data.stopDefalutBehavior){ var leng = v.exceptions.length; if(leng){ for(var i=0;i", sDefaultXPath : "/HTML[0]/BODY[0]", $init : function(){ this.aUndoHistory = []; this.oCurStateIdx = {nIdx: 0, nStep: 0}; this.nHardLimit = this.nMaxUndoCount + this.nAfterMaxDeleteBuffer; }, $LOCAL_BEFORE_ALL : function(sCmd){ if(sCmd.match(/_DO_RECORD_UNDO_HISTORY_AT$/)){ return true; } try{ if(this.oApp.getEditingMode() != "WYSIWYG"){ return false; } }catch(e){ return false; } return true; }, $BEFORE_MSG_APP_READY : function(){ this._historyLength = 0; this.oApp.exec("ADD_APP_PROPERTY", ["getUndoHistory", jindo.$Fn(this._getUndoHistory, this).bind()]); this.oApp.exec("ADD_APP_PROPERTY", ["getUndoStateIdx", jindo.$Fn(this._getUndoStateIdx, this).bind()]); this.oApp.exec("ADD_APP_PROPERTY", ["saveSnapShot", jindo.$Fn(this._saveSnapShot, this).bind()]); this.oApp.exec("ADD_APP_PROPERTY", ["getLastKey", jindo.$Fn(this._getLastKey, this).bind()]); this.oApp.exec("ADD_APP_PROPERTY", ["setLastKey", jindo.$Fn(this._setLastKey, this).bind()]); this._saveSnapShot(); this.oApp.exec("DO_RECORD_UNDO_HISTORY_AT", [this.oCurStateIdx, "", "", "", null, this.sDefaultXPath]); }, _getLastKey : function(){ return this.sLastKey; }, _setLastKey : function(sLastKey){ this.sLastKey = sLastKey; }, $ON_MSG_APP_READY : function(){ var oNavigator = jindo.$Agent().navigator(); this.bIE = oNavigator.ie; this.bFF = oNavigator.firefox; //this.bChrome = oNavigator.chrome; //this.bSafari = oNavigator.safari; this.oApp.exec("REGISTER_UI_EVENT", ["undo", "click", "UNDO"]); this.oApp.exec("REGISTER_UI_EVENT", ["redo", "click", "REDO"]); // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 if (jindo.$Agent().os().mac) { this.oApp.exec("REGISTER_HOTKEY", ["meta+z", "UNDO"]); this.oApp.exec("REGISTER_HOTKEY", ["meta+y", "REDO"]); } else { this.oApp.exec("REGISTER_HOTKEY", ["ctrl+z", "UNDO"]); this.oApp.exec("REGISTER_HOTKEY", ["ctrl+y", "REDO"]); } // this.htOptions = this.oApp.htOptions["SE_UndoRedo"] || {}; }, $ON_UNDO : function(){ this._doRecordUndoHistory("UNDO", { nStep : 0, bSkipIfEqual : true, bMustBlockContainer : true }); if(this.oCurStateIdx.nIdx <= 0){ return; } // 현재의 상태에서 Undo 했을 때 처리해야 할 메시지 호출 var oUndoCallback = this.aUndoHistory[this.oCurStateIdx.nIdx].oUndoCallback[this.oCurStateIdx.nStep]; var sCurrentPath = this.aUndoHistory[this.oCurStateIdx.nIdx].sParentXPath[this.oCurStateIdx.nStep]; if(oUndoCallback){ this.oApp.exec(oUndoCallback.sMsg, oUndoCallback.aParams); } if(this.oCurStateIdx.nStep > 0){ this.oCurStateIdx.nStep--; }else{ var oTmpHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; this.oCurStateIdx.nIdx--; if(oTmpHistory.nTotalSteps>1){ this.oCurStateIdx.nStep = 0; }else{ oTmpHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; this.oCurStateIdx.nStep = oTmpHistory.nTotalSteps-1; } } var sUndoHistoryPath = this.aUndoHistory[this.oCurStateIdx.nIdx].sParentXPath[this.oCurStateIdx.nStep]; var bUseDefault = false; if(sUndoHistoryPath !== sCurrentPath && sUndoHistoryPath.indexOf(sCurrentPath) === 0){ // 현재의 Path가 Undo의 Path보다 범위가 큰 경우 bUseDefault = true; } this.oApp.exec("RESTORE_UNDO_HISTORY", [this.oCurStateIdx.nIdx, this.oCurStateIdx.nStep, bUseDefault]); this.oApp.exec("CHECK_STYLE_CHANGE", []); this.sLastKey = null; }, $ON_REDO : function(){ if(this.oCurStateIdx.nIdx >= this.aUndoHistory.length){ return; } var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; if(this.oCurStateIdx.nIdx == this.aUndoHistory.length-1 && this.oCurStateIdx.nStep >= oCurHistory.nTotalSteps-1){ return; } if(this.oCurStateIdx.nStep < oCurHistory.nTotalSteps-1){ this.oCurStateIdx.nStep++; }else{ this.oCurStateIdx.nIdx++; oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; this.oCurStateIdx.nStep = oCurHistory.nTotalSteps-1; } // 원복될 상태에서 Redo 했을 때 처리해야 할 메시지 호출 var oRedoCallback = this.aUndoHistory[this.oCurStateIdx.nIdx].oRedoCallback[this.oCurStateIdx.nStep]; if(oRedoCallback){ this.oApp.exec(oRedoCallback.sMsg, oRedoCallback.aParams); } this.oApp.exec("RESTORE_UNDO_HISTORY", [this.oCurStateIdx.nIdx, this.oCurStateIdx.nStep]); this.oApp.exec("CHECK_STYLE_CHANGE", []); this.sLastKey = null; }, $ON_RECORD_UNDO_ACTION : function(sAction, oSaveOption){ oSaveOption = oSaveOption || { sSaveTarget : null, elSaveTarget : null, bMustBlockElement : false, bMustBlockContainer : false, bDontSaveSelection : false }; oSaveOption.nStep = 0; oSaveOption.bSkipIfEqual = false; oSaveOption.bTwoStepAction = false; this._doRecordUndoHistory(sAction, oSaveOption); }, $ON_RECORD_UNDO_BEFORE_ACTION : function(sAction, oSaveOption){ oSaveOption = oSaveOption || { sSaveTarget : null, elSaveTarget : null, bMustBlockElement : false, bMustBlockContainer : false, bDontSaveSelection : false }; oSaveOption.nStep = 0; oSaveOption.bSkipIfEqual = false; oSaveOption.bTwoStepAction = true; this._doRecordUndoHistory(sAction, oSaveOption); }, $ON_RECORD_UNDO_AFTER_ACTION : function(sAction, oSaveOption){ oSaveOption = oSaveOption || { sSaveTarget : null, elSaveTarget : null, bMustBlockElement : false, bMustBlockContainer : false, bDontSaveSelection : false }; oSaveOption.nStep = 1; oSaveOption.bSkipIfEqual = false; oSaveOption.bTwoStepAction = true; this._doRecordUndoHistory(sAction, oSaveOption); }, $ON_RESTORE_UNDO_HISTORY : function(nUndoIdx, nUndoStateStep, bUseDefault){ this.oApp.exec("HIDE_ACTIVE_LAYER"); this.oCurStateIdx.nIdx = nUndoIdx; this.oCurStateIdx.nStep = nUndoStateStep; var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx], sContent = oCurHistory.sContent[this.oCurStateIdx.nStep], sFullContents = oCurHistory.sFullContents[this.oCurStateIdx.nStep], oBookmark = oCurHistory.oBookmark[this.oCurStateIdx.nStep], sParentXPath = oCurHistory.sParentXPath[this.oCurStateIdx.nStep], oParent = null, sCurContent = "", oSelection = this.oApp.getEmptySelection(); this.oApp.exec("RESTORE_IE_SELECTION"); // this is done to null the ie selection if(bUseDefault){ this.oApp.getWYSIWYGDocument().body.innerHTML = sFullContents; sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; sCurContent = sFullContents; sParentXPath = this.sDefaultXPath; }else{ oParent = oSelection._evaluateXPath(sParentXPath, oSelection._document); try{ oParent.innerHTML = sContent; sCurContent = oParent.innerHTML; }catch(e){ // Path 노드를 찾지 못하는 경우 this.oApp.getWYSIWYGDocument().body.innerHTML = sFullContents; sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; // setting the innerHTML may change the internal DOM structure, so save the value again. sCurContent = sFullContents; sParentXPath = this.sDefaultXPath; } } if(this.bFF && sCurContent == this.sBlankContentsForFF){ sCurContent = ""; } oCurHistory.sFullContents[this.oCurStateIdx.nStep] = sFullContents; oCurHistory.sContent[this.oCurStateIdx.nStep] = sCurContent; oCurHistory.sParentXPath[this.oCurStateIdx.nStep] = sParentXPath; if(oBookmark && oBookmark.sType == "scroll"){ setTimeout(jindo.$Fn(function(){this.oApp.getWYSIWYGDocument().documentElement.scrollTop = oBookmark.nScrollTop;}, this).bind(), 0); }else{ oSelection = this.oApp.getEmptySelection(); if(oSelection.selectionLoaded){ if(oBookmark){ oSelection.moveToXPathBookmark(oBookmark); }else{ oSelection = this.oApp.getEmptySelection(); } oSelection.select(); } } }, _doRecordUndoHistory : function(sAction, htRecordOption){ /* htRecordOption = { nStep : 0 | 1, bSkipIfEqual : false | true, bTwoStepAction : false | true, sSaveTarget : [TAG] | null elSaveTarget : [Element] | null bDontSaveSelection : false | true bMustBlockElement : false | true bMustBlockContainer : false | true }; */ htRecordOption = htRecordOption || {}; var nStep = htRecordOption.nStep || 0, bSkipIfEqual = htRecordOption.bSkipIfEqual || false, bTwoStepAction = htRecordOption.bTwoStepAction || false, sSaveTarget = htRecordOption.sSaveTarget || null, elSaveTarget = htRecordOption.elSaveTarget || null, bDontSaveSelection = htRecordOption.bDontSaveSelection || false, bMustBlockElement = htRecordOption.bMustBlockElement || false, bMustBlockContainer = htRecordOption.bMustBlockContainer || false, oUndoCallback = htRecordOption.oUndoCallback, oRedoCallback = htRecordOption.oRedoCallback; // if we're in the middle of some action history, // remove everything after current idx if any "little" change is made this._historyLength = this.aUndoHistory.length; if(this.oCurStateIdx.nIdx !== this._historyLength-1){ bSkipIfEqual = true; } var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx], sHistoryFullContents = oCurHistory.sFullContents[this.oCurStateIdx.nStep], sCurContent = "", sFullContents = "", sParentXPath = "", oBookmark = null, oSelection = null, oInsertionIdx = {nIdx:this.oCurStateIdx.nIdx, nStep:this.oCurStateIdx.nStep}; // 히스토리를 저장할 위치 oSelection = this.oApp.getSelection(); if(oSelection.selectionLoaded){ oBookmark = oSelection.getXPathBookmark(); } if(elSaveTarget){ sParentXPath = oSelection._getXPath(elSaveTarget); }else if(sSaveTarget){ sParentXPath = this._getTargetXPath(oBookmark, sSaveTarget); }else{ sParentXPath = this._getParentXPath(oBookmark, bMustBlockElement, bMustBlockContainer); } sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; // sCurContent = this.oApp.getWYSIWYGDocument().body.innerHTML.replace(/ *_cssquery_UID="[^"]+" */g, ""); if(sParentXPath === this.sDefaultXPath){ sCurContent = sFullContents; }else{ sCurContent = oSelection._evaluateXPath(sParentXPath, oSelection._document).innerHTML; } if(this.bFF && sCurContent == this.sBlankContentsForFF){ sCurContent = ""; } // every TwoStepAction needs to be recorded if(!bTwoStepAction && bSkipIfEqual){ if(sHistoryFullContents.length === sFullContents.length){ return; } // 저장된 데이터와 같음에도 다르다고 처리되는 경우에 대한 처리 // (예, P안에 Block엘리먼트가 추가된 경우 P를 분리) //if(this.bChrome || this.bSafari){ var elCurrentDiv = document.createElement("div"); var elHistoryDiv = document.createElement("div"); elCurrentDiv.innerHTML = sFullContents; elHistoryDiv.innerHTML = sHistoryFullContents; var elDocFragment = document.createDocumentFragment(); elDocFragment.appendChild(elCurrentDiv); elDocFragment.appendChild(elHistoryDiv); sFullContents = elCurrentDiv.innerHTML; sHistoryFullContents = elHistoryDiv.innerHTML; elCurrentDiv = null; elHistoryDiv = null; elDocFragment = null; if(sHistoryFullContents.length === sFullContents.length){ return; } //} } if(bDontSaveSelection){ oBookmark = { sType : "scroll", nScrollTop : this.oApp.getWYSIWYGDocument().documentElement.scrollTop }; } oInsertionIdx.nStep = nStep; if(oInsertionIdx.nStep === 0 && this.oCurStateIdx.nStep === oCurHistory.nTotalSteps-1){ oInsertionIdx.nIdx = this.oCurStateIdx.nIdx+1; } this._doRecordUndoHistoryAt(oInsertionIdx, sAction, sCurContent, sFullContents, oBookmark, sParentXPath, oUndoCallback, oRedoCallback); }, $ON_DO_RECORD_UNDO_HISTORY_AT : function(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath){ this._doRecordUndoHistoryAt(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath); }, _doRecordUndoHistoryAt : function(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath, oUndoCallback, oRedoCallback){ if(oInsertionIdx.nStep !== 0){ this.aUndoHistory[oInsertionIdx.nIdx].nTotalSteps = oInsertionIdx.nStep+1; this.aUndoHistory[oInsertionIdx.nIdx].sContent[oInsertionIdx.nStep] = sContent; this.aUndoHistory[oInsertionIdx.nIdx].sFullContents[oInsertionIdx.nStep] = sFullContents; this.aUndoHistory[oInsertionIdx.nIdx].oBookmark[oInsertionIdx.nStep] = oBookmark; this.aUndoHistory[oInsertionIdx.nIdx].sParentXPath[oInsertionIdx.nStep] = sParentXPath; this.aUndoHistory[oInsertionIdx.nIdx].oUndoCallback[oInsertionIdx.nStep] = oUndoCallback; this.aUndoHistory[oInsertionIdx.nIdx].oRedoCallback[oInsertionIdx.nStep] = oRedoCallback; }else{ var oNewHistory = {sAction:sAction, nTotalSteps: 1}; oNewHistory.sContent = []; oNewHistory.sContent[0] = sContent; oNewHistory.sFullContents = []; oNewHistory.sFullContents[0] = sFullContents; oNewHistory.oBookmark = []; oNewHistory.oBookmark[0] = oBookmark; oNewHistory.sParentXPath = []; oNewHistory.sParentXPath[0] = sParentXPath; oNewHistory.oUndoCallback = []; oNewHistory.oUndoCallback[0] = oUndoCallback; oNewHistory.oRedoCallback = []; oNewHistory.oRedoCallback[0] = oRedoCallback; this.aUndoHistory.splice(oInsertionIdx.nIdx, this._historyLength - oInsertionIdx.nIdx, oNewHistory); this._historyLength = this.aUndoHistory.length; } if(this._historyLength > this.nHardLimit){ this.aUndoHistory.splice(0, this.nAfterMaxDeleteBuffer); oInsertionIdx.nIdx -= this.nAfterMaxDeleteBuffer; } this.oCurStateIdx.nIdx = oInsertionIdx.nIdx; this.oCurStateIdx.nStep = oInsertionIdx.nStep; }, _saveSnapShot : function(){ this.oSnapShot = { oBookmark : this.oApp.getSelection().getXPathBookmark() }; }, _getTargetXPath : function(oBookmark, sSaveTarget){ // ex. A, TABLE ... var sParentXPath = this.sDefaultXPath, aStartXPath = oBookmark[0].sXPath.split("/"), aEndXPath = oBookmark[1].sXPath.split("/"), aParentPath = [], nPathLen = aStartXPath.length < aEndXPath.length ? aStartXPath.length : aEndXPath.length, nPathIdx = 0, nTargetIdx = -1; if(sSaveTarget === "BODY"){ return sParentXPath; } for(nPathIdx=0; nPathIdx -1){ nTargetIdx = nPathIdx; } } if(nTargetIdx > -1){ aParentPath.length = nTargetIdx; // Target 의 상위 노드까지 지정 } sParentXPath = aParentPath.join("/"); if(sParentXPath.length < this.sDefaultXPath.length){ sParentXPath = this.sDefaultXPath; } return sParentXPath; }, _getParentXPath : function(oBookmark, bMustBlockElement, bMustBlockContainer){ var sParentXPath = this.sDefaultXPath, aStartXPath, aEndXPath, aSnapShotStart, aSnapShotEnd, nSnapShotLen, nPathLen, aParentPath = ["", "HTML[0]", "BODY[0]"], nPathIdx = 0, nBlockIdx = -1, // rxBlockContainer = /\bUL|OL|TD|TR|TABLE|BLOCKQUOTE\[/i, // DL // rxBlockElement = /\bP|LI|DIV|UL|OL|TD|TR|TABLE|BLOCKQUOTE\[/i, // H[1-6]|DD|DT|DL|PRE // rxBlock, sPath, sTag; if(!oBookmark){ return sParentXPath; } // 가능한 중복되는 Parent 를 검색 if(oBookmark[0].sXPath === sParentXPath || oBookmark[1].sXPath === sParentXPath){ return sParentXPath; } aStartXPath = oBookmark[0].sXPath.split("/"); aEndXPath = oBookmark[1].sXPath.split("/"); aSnapShotStart = this.oSnapShot.oBookmark[0].sXPath.split("/"); aSnapShotEnd = this.oSnapShot.oBookmark[1].sXPath.split("/"); nSnapShotLen = aSnapShotStart.length < aSnapShotEnd.length ? aSnapShotStart.length : aSnapShotEnd.length; nPathLen = aStartXPath.length < aEndXPath.length ? aStartXPath.length : aEndXPath.length; nPathLen = nPathLen < nSnapShotLen ? nPathLen : nSnapShotLen; if(nPathLen < 3){ // BODY return sParentXPath; } bMustBlockElement = bMustBlockElement || false; bMustBlockContainer = bMustBlockContainer || false; // rxBlock = bMustBlockElement ? rxBlockElement : rxBlockContainer; for(nPathIdx=3; nPathIdx -1){ aParentPath.length = nBlockIdx + 1; }else if(bMustBlockElement || bMustBlockContainer){ return sParentXPath; } return aParentPath.join("/"); }, _getUndoHistory : function(){ return this.aUndoHistory; }, _getUndoStateIdx : function(){ return this.oCurStateIdx; } }); /*[ * ATTACH_HOVER_EVENTS * * 주어진 HTML엘리먼트에 Hover 이벤트 발생시 특정 클래스가 할당 되도록 설정 * * aElms array Hover 이벤트를 걸 HTML Element 목록 * sHoverClass string Hover 시에 할당 할 클래스 * ---------------------------------------------------------------------------]*/ /** * @pluginDesc Husky Framework에서 자주 사용되는 유틸성 메시지를 처리하는 플러그인 */ nhn.husky.Utils = jindo.$Class({ name : "Utils", $init : function(){ var oAgentInfo = jindo.$Agent(); var oNavigatorInfo = oAgentInfo.navigator(); if(oNavigatorInfo.ie && oNavigatorInfo.version == 6){ try{ document.execCommand('BackgroundImageCache', false, true); }catch(e){} } }, $BEFORE_MSG_APP_READY : function(){ this.oApp.exec("ADD_APP_PROPERTY", ["htBrowser", jindo.$Agent().navigator()]); }, $ON_ATTACH_HOVER_EVENTS : function(aElms, htOptions){ htOptions = htOptions || []; var sHoverClass = htOptions.sHoverClass || "hover"; var fnElmToSrc = htOptions.fnElmToSrc || function(el){return el}; var fnElmToTarget = htOptions.fnElmToTarget || function(el){return el}; if(!aElms) return; var wfAddClass = jindo.$Fn(function(wev){ jindo.$Element(fnElmToTarget(wev.currentElement)).addClass(sHoverClass); }, this); var wfRemoveClass = jindo.$Fn(function(wev){ jindo.$Element(fnElmToTarget(wev.currentElement)).removeClass(sHoverClass); }, this); for(var i=0, len = aElms.length; itag 사이에 '\n\'개행문자를 넣어준다. if( jindo.$Agent().navigator().ie ){ sTmpStr = sTmpStr +'(\r\n)?'; //ie+win에서는 개행이 \r\n로 들어감. rxTmpStr = new RegExp(sTmpStr , "g"); } } aConverters = this.oConverters[sRuleName]; if(aConverters){ for(var i=0; iSpec Code *
Spec
*
wiki
*
wiki
* @author NHN AjaxUI Lab - sung jong min * @version 0.1 * @since 2009.07.06 */ nhn.husky.PopUpManager = {}; /** * @ignore */ nhn.husky.PopUpManager._instance = null; /** * @ignore */ nhn.husky.PopUpManager._pluginKeyCnt = 0; /** * @description 팝업 매니저 인스턴스 호출 메소드, nhn.husky js framework 기반 코드 * @public * @param {Object} oApp 허스키 코어 객체를 넘겨준다.(this.oApp) * @return {Object} nhn.husky.PopUpManager Instance * @example 팝업관련 플러그인 제작 예제 * nhn.husky.NewPlugin = function(){ * this.$ON_APP_READY = function(){ * // 팝업 매니저 getInstance 메소드를 호출한다. * // 허스키 코어의 참조값을 넘겨준다(this.oApp) * this.oPopupMgr = nhn.husky.PopUpMaganer.getInstance(this.oApp); * }; * * // 팝업을 요청하는 메시지 메소드는 아래와 같음 * this.$ON_NEWPLUGIN_OPEN_WINDOW = function(){ * var oWinOp = { * oApp : this.oApp, // oApp this.oApp 허스키 참조값 * sUrl : "", // sUrl : 페이지 URL * sName : "", // sName : 페이지 name * nWidth : 400, * nHeight : 400, * bScroll : true * } * this.oPopUpMgr.openWindow(oWinOp); * }; * * // 팝업페이지 응답데이타 반환 메시지 메소드를 정의함. * // 각 플러그인 팝업페이지에서 해당 메시지와 데이타를 넘기게 됨. * this.@ON_NEWPLUGIN_WINDOW_CALLBACK = function(){ * // 팝업페이지별로 정의된 형태의 아규먼트 데이타가 넘어오면 처리한다. * } * } * @example 팝업 페이지와 opener 호출 인터페이스 예제 * onclick시 * "nhn.husky.PopUpManager.setCallback(window, "NEWPLUGIN_WINDOW_CALLBACK", oData);" * 형태로 호출함. * * */ nhn.husky.PopUpManager.getInstance = function(oApp) { if (this._instance==null) { this._instance = new (function(){ this._whtPluginWin = new jindo.$H(); this._whtPlugin = new jindo.$H(); this.addPlugin = function(sKey, vValue){ this._whtPlugin.add(sKey, vValue); }; this.getPlugin = function() { return this._whtPlugin; }; this.getPluginWin = function() { return this._whtPluginWin; }; this.openWindow = function(oWinOpt) { var op= { oApp : null, sUrl : "", sName : "popup", sLeft : null, sTop : null, nWidth : 400, nHeight : 400, sProperties : null, bScroll : true }; for(var i in oWinOpt) op[i] = oWinOpt[i]; if(op.oApp == null) { alert("팝업 요청시 옵션으로 oApp(허스키 reference) 값을 설정하셔야 합니다."); } var left = op.sLeft || (screen.availWidth-op.nWidth)/2; var top = op.sTop ||(screen.availHeight-op.nHeight)/2; var sProperties = op.sProperties != null ? op.sProperties : "top="+ top +",left="+ left +",width="+op.nWidth+",height="+op.nHeight+",scrollbars="+(op.bScroll?"yes":"no")+",status=yes"; var win = window.open(op.sUrl, op.sName,sProperties); if (!!win) { setTimeout( function(){ try{win.focus();}catch(e){} }, 100); } this.removePluginWin(win); this._whtPluginWin.add(this.getCorrectKey(this._whtPlugin, op.oApp), win); return win; }; this.getCorrectKey = function(whtData, oCompare) { var key = null; whtData.forEach(function(v,k){ if (v == oCompare) { key = k; return; } }); return key; }; this.removePluginWin = function(vValue) { var list = this._whtPluginWin.search(vValue); if (list) { this._whtPluginWin.remove(list); this.removePluginWin(vValue); } } })(); } this._instance.addPlugin("plugin_" + (this._pluginKeyCnt++), oApp); return nhn.husky.PopUpManager._instance; }; /** * @description opener 연동 interface * @public * @param {Object} oOpenWin 팝업 페이지의 window 객체 * @param {Object} sMsg 플러그인 메시지명 * @param {Object} oData 응답 데이타 */ nhn.husky.PopUpManager.setCallback = function(oOpenWin, sMsg, oData) { if (this._instance.getPluginWin().hasValue(oOpenWin)) { var key = this._instance.getCorrectKey(this._instance.getPluginWin(), oOpenWin); if (key) { this._instance.getPlugin().$(key).exec(sMsg, oData); } } }; /** * @description opener에 허스키 함수를 실행시키고 데이터 값을 리턴 받음. * @param */ nhn.husky.PopUpManager.getFunc = function(oOpenWin, sFunc) { if (this._instance.getPluginWin().hasValue(oOpenWin)) { var key = this._instance.getCorrectKey(this._instance.getPluginWin(), oOpenWin); if (key) { return this._instance.getPlugin().$(key)[sFunc](); } } }; if(typeof window.nhn == 'undefined') { window.nhn = {}; } if(!nhn.husky) { nhn.husky = {}; } (function(){ // 구버전 jindo.$Agent polyfill var ua = navigator.userAgent, oAgent = jindo.$Agent(), browser = oAgent.navigator(), os = oAgent.os(); // [SMARTEDITORSUS-1795] 갤럭시노트 기본브라우저 구분을 위해 구분필드 추가 var aMatch = ua.match(/(SHW-|Chrome|Safari)/gi) || ""; if(aMatch.length === 2 && aMatch[0] === "SHW-" && aMatch[1] === "Safari"){ // 갤럭시노트 기본브라우저 browser.bGalaxyBrowser = true; }else if(ua.indexOf("LG-V500") > -1 && ua.indexOf("Version/4.0") > -1){ // [SMARTEDITORSUS-1802] G패드 기본브라우저 browser.bGPadBrowser = true; } // [SMARTEDITORSUS-1860] iOS 버전 확인용 // os 에서 ios 여부 및 version 정보는 jindo2.3.0 부터 추가되었음 if(typeof os.ios === 'undefined'){ os.ios = ua.indexOf("iPad") > -1 || ua.indexOf("iPhone") > -1; if(os.ios){ aMatch = ua.match(/(iPhone )?OS ([\d|_]+)/); if(aMatch != null && aMatch[2] != undefined){ os.version = String(aMatch[2]).split("_").join("."); } } } })(); nhn.husky.SE2M_UtilPlugin = jindo.$Class({ name : "SE2M_UtilPlugin", $BEFORE_MSG_APP_READY : function(){ this.oApp.exec("ADD_APP_PROPERTY", ["oAgent", jindo.$Agent()]); this.oApp.exec("ADD_APP_PROPERTY", ["oNavigator", jindo.$Agent().navigator()]); this.oApp.exec("ADD_APP_PROPERTY", ["oUtils", this]); }, $ON_REGISTER_HOTKEY : function(sHotkey, sCMD, aArgs, elTarget) { this.oApp.exec("ADD_HOTKEY", [sHotkey, sCMD, aArgs, (elTarget || this.oApp.getWYSIWYGDocument())]); }, $ON_SE2_ATTACH_HOVER_EVENTS : function(aElms){ this.oApp.exec("ATTACH_HOVER_EVENTS", [aElms, {fnElmToSrc: this._elm2Src, fnElmToTarget: this._elm2Target}]); }, _elm2Src : function(el){ if(el.tagName == "LI" && el.firstChild && el.firstChild.tagName == "BUTTON"){ return el.firstChild; }else{ return el; } }, _elm2Target : function(el){ if(el.tagName == "BUTTON" && el.parentNode.tagName == "LI"){ return el.parentNode; }else{ return el; } }, getScrollXY : function(){ var scrollX,scrollY; var oAppWindow = this.oApp.getWYSIWYGWindow(); if(typeof oAppWindow.scrollX == "undefined"){ scrollX = oAppWindow.document.documentElement.scrollLeft; scrollY = oAppWindow.document.documentElement.scrollTop; }else{ scrollX = oAppWindow.scrollX; scrollY = oAppWindow.scrollY; } return {x:scrollX, y:scrollY}; } }); nhn.husky.SE2M_Utils = { sURLPattern : '(http|https|ftp|mailto):(?:\\/\\/)?((:?\\w|-)+(:?\\.(:?\\w|-)+)+)([^ <>]+)?', rxDateFormat : /^(?:\d{4}\.)?\d{1,2}\.\d{1,2}$/, _rxTable : /^(?:CAPTION|TBODY|THEAD|TFOOT|TR|TD|TH|COLGROUP|COL)$/i, _rxSpaceOnly : /^\s+$/, _rxFontStart : /]*)?>/i, _htFontSize : { // @see http://jerekdain.com/fontconversion.html "1" : "7pt", "2" : "10pt", "3" : "12pt", "4" : "13.5pt", "5" : "18pt", "6" : "24pt" }, /** * 유효하지 않은 노드가 TBODY, TR, TD 등등 사이에 있는지 판별한다. * @param oNode {Node} 검사할 노드 * @return {Boolean} TBODY, TR, TD 등등 사이에 있는 유효하지 않은 노드인지 여부 */ isInvalidNodeInTable : function(oNode){ // 해당노드가 table 관련노드들이 아닌데 table 관련노드들 사이에 있는지 검사한다. // TODO: 좀 더 정확하게 검사할 필요가 있을까? if(oNode && !this._rxTable.test(oNode.nodeName)){ var oTmpNode; if((oTmpNode = oNode.previousSibling) && this._rxTable.test(oTmpNode.nodeName)){ return true; } if((oTmpNode = oNode.nextSibling) && this._rxTable.test(oTmpNode.nodeName)){ return true; } } return false; }, /** * [SMARTEDITORSUS-1584][SMARTEDITORSUS-2237] * TBODY, TR, TD 사이에 있는 유효하지 않은 노드이면 제거한다. * @param oNode {Node} 검사할 노드 */ removeInvalidNodeInTable : function(oNode){ if(this.isInvalidNodeInTable(oNode) && oNode.parentNode){ oNode.parentNode.removeChild(oNode); } }, /** * [SMARTEDITORSUS-2315] 유효하지 않은 폰트태그를 제거한다. * @param {Element} el 태그정제를 제한할 상위 요소 */ removeInvalidFont : function(el){ if(!el){ return; } this._removeInvalidFontInTable(el); this._removeEmptyFont(el); }, /** * [SMARTEDITORSUS-2237] IE11 에서 엑셀을 복사붙여넣기하면 유효하지 않은 위치(tbody, tr, td 사이)에 font 태그가 삽입되므로 제거 * @param {Element} el 태그제거를 제한할 상위 요소 */ _removeInvalidFontInTable : function(el){ var aelFonts = jindo.$$("table font", el); for(var i = 0, elFont; (elFont = aelFonts[i]); i++){ this.removeInvalidNodeInTable(elFont); } }, /** * 빈 폰트태그를 제거한다. * @param {Element} el 태그제거를 제한할 상위 요소 */ _removeEmptyFont : function(el){ var aelFonts = jindo.$$("font", el); for(var i = 0, elFont, sInner; (elFont = aelFonts[i]); i++){ sInner = elFont.innerHTML || ""; sInner = sInner.replace(this._rxSpaceOnly, ""); if (!sInner) { elFont.parentNode.removeChild(elFont); } } }, /** * [SMARTEDITORSUS-2315] font 태그를 span 으로 변환 * 포스트이슈[MUG-7757] 처리 로직 참고 * @param {Element} el 태그변환을 제한할 상위 요소 */ convertFontToSpan : function(el){ if(!el){ return; } var oDoc = el.ownerDocument || document; var aelTarget = jindo.$$("font", el); for(var i = 0, elTarget, elSpan, sAttrValue; (elTarget = aelTarget[i]); i++){ elSpan = elTarget.parentNode; if(elSpan.tagName !== "SPAN" || elSpan.childNodes.length > 1){ elSpan = oDoc.createElement("SPAN"); elTarget.parentNode.insertBefore(elSpan, elTarget); } sAttrValue = elTarget.getAttribute("face"); if(sAttrValue){ elSpan.style.fontFamily = sAttrValue; } sAttrValue = this._htFontSize[elTarget.getAttribute("size")]; if(sAttrValue){ elSpan.style.fontSize = sAttrValue; } sAttrValue = elTarget.getAttribute("color"); if(sAttrValue){ elSpan.style.color = sAttrValue; } // font태그 안쪽 내용을 span에 넣는다. this._switchFontInnerToSpan(elTarget, elSpan); if(elTarget.parentNode){ elTarget.parentNode.removeChild(elTarget); } } }, /** * font태그 안쪽 내용을 span에 넣는다. * @param {Element} elFont 대상FONT태그 * @param {Element} elSpan 빈SPAN태그 */ _switchFontInnerToSpan : function(elFont, elSpan){ var sInnerHTML = elFont.innerHTML; /** * 폰트태그안에 폰트태그가 있을때 innerHTML으로 넣으면 안쪽 폰트태그는 span변환작업에서 누락될 수 있기 때문에 * 폰트태그가 중첩해서 있으면 appendChild를 이용하고 그렇지 않으면 innerHTML을 이용 */ if(this._rxFontStart.test(sInnerHTML)){ for(var elChild; (elChild = elFont.firstChild);){ elSpan.appendChild(elChild); } }else{ elSpan.innerHTML = sInnerHTML; } }, /** * 대상요소의 하위 요소들을 모두 밖으로 빼내고 대상요소를 제거한다. * @param {Element} el 제거 대상 요소 */ stripTag : function(el){ for(var elChild; (elChild = el.firstChild);){ el.parentNode.insertBefore(elChild, el); } el.parentNode.removeChild(el); }, /** * 노드 하위의 특정태그를 모두 제거한다. * @param {Element} el 확인 대상 요소 * @param {String} sTagName 제거 대상 태그명 (대문자) */ stripTags :function(el, sTagName){ var aelTarget = jindo.$$(sTagName, el); for(var i = 0, elTarget; (elTarget = aelTarget[i]); i++){ this.stripTag(elTarget); } }, /** * [SMARTEDITORSUS-2203] 날짜표기를 바로 잡는다. ex) 2015.10.13 -> 2015.10.13. * @param {String} sDate 확인할 문자열 * @returns {String} 올바른 표기로 변환한 문자열 */ reviseDateFormat : function(sDate){ if(sDate && sDate.replace){ sDate = sDate.replace(this.rxDateFormat, "$&."); } return sDate; }, /** * 사용자 클래스 정보를 추출한다. * @param {String} sStr 추출 String * @param {rx} rxValue rx type 형식의 값 * @param {String} sDivision value의 split 형식 * @return {Array} */ getCustomCSS : function(sStr, rxValue, sDivision) { var ret = []; if('undefined' == typeof(sStr) || 'undefined' == typeof(rxValue) || !sStr || !rxValue) { return ret; } var aMatch = sStr.match(rxValue); if(aMatch && aMatch[0]&&aMatch[1]) { if(sDivision) { ret = aMatch[1].split(sDivision); } else { ret[0] = aMatch[1]; } } return ret; }, /** * HashTable로 구성된 Array의 같은 프로퍼티를 sSeperator 로 구분된 String 값으로 변환 * @param {Object} v * @param {Object} sKey * @author senxation * @example a = [{ b : "1" }, { b : "2" }] toStringSamePropertiesOfArray(a, "b", ", "); ==> "1, 2" */ toStringSamePropertiesOfArray : function(v, sKey, sSeperator) { if(!v){ return ""; } if (v instanceof Array) { var a = []; for (var i = 0; i < v.length; i++) { a.push(v[i][sKey]); } return a.join(",").replace(/,/g, sSeperator); } else { if (typeof v[sKey] == "undefined") { return ""; } if (typeof v[sKey] == "string") { return v[sKey]; } } }, /** * 단일 객체를 배열로 만들어줌 * @param {Object} v * @return {Array} * @author senxation * @example makeArray("test"); ==> ["test"] */ makeArray : function(v) { if (v === null || typeof v === "undefined"){ return []; } if (v instanceof Array) { return v; } var a = []; a.push(v); return a; }, /** * 말줄임을 할때 줄일 내용과 컨테이너가 다를 경우 처리 * 컨테이너의 css white-space값이 "normal"이어야한다. (컨테이너보다 텍스트가 길면 여러행으로 표현되는 상태) * @param {HTMLElement} elText 말줄임할 엘리먼트 * @param {HTMLElement} elContainer 말줄임할 엘리먼트를 감싸는 컨테이너 * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) * @param {Number} nLine 최대 라인수 (미지정시 1) * @author senxation * @example //div가 2줄 이하가 되도록 strong 내부의 내용을 줄임
말줄임을적용할내용말줄임을적용할내용말줄임을적용할내용상세보기
ellipsis(jindo.$("a"), jindo.$("a").parentNode, "...", 2); */ ellipsis : function(elText, elContainer, sStringTail, nLine) { sStringTail = sStringTail || "..."; if (typeof nLine == "undefined") { nLine = 1; } var welText = jindo.$Element(elText); var welContainer = jindo.$Element(elContainer); var sText = welText.html(); var nLength = sText.length; var nCurrentHeight = welContainer.height(); var nIndex = 0; welText.html('A'); var nHeight = welContainer.height(); if (nCurrentHeight < nHeight * (nLine + 0.5)) { return welText.html(sText); } /** * 지정된 라인보다 커질때까지 전체 남은 문자열의 절반을 더해나감 */ nCurrentHeight = nHeight; while(nCurrentHeight < nHeight * (nLine + 0.5)) { nIndex += Math.max(Math.ceil((nLength - nIndex)/2), 1); welText.html(sText.substring(0, nIndex) + sStringTail); nCurrentHeight = welContainer.height(); } /** * 지정된 라인이 될때까지 한글자씩 잘라냄 */ while(nCurrentHeight > nHeight * (nLine + 0.5)) { nIndex--; welText.html(sText.substring(0, nIndex) + sStringTail); nCurrentHeight = welContainer.height(); } }, /** * 최대 가로사이즈를 지정하여 말줄임한다. * elText의 css white-space값이 "nowrap"이어야한다. (컨테이너보다 텍스트가 길면 행변환되지않고 가로로 길게 표현되는 상태) * @param {HTMLElement} elText 말줄임할 엘리먼트 * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) * @param {Function} fCondition 조건 함수. 내부에서 true를 리턴하는 동안에만 말줄임을 진행한다. * @author senxation * @example //150픽셀 이하가 되도록 strong 내부의 내용을 줄임 말줄임을적용할내용말줄임을적용할내용말줄임을적용할내용> ellipsisByPixel(jindo.$("a"), "...", 150); */ ellipsisByPixel : function(elText, sStringTail, nPixel, fCondition) { sStringTail = sStringTail || "..."; var welText = jindo.$Element(elText); var nCurrentWidth = welText.width(); if (nCurrentWidth < nPixel) { return; } var sText = welText.html(); var nLength = sText.length; var nIndex = 0; if (typeof fCondition == "undefined") { var nWidth = welText.html('A').width(); nCurrentWidth = nWidth; while(nCurrentWidth < nPixel) { nIndex += Math.max(Math.ceil((nLength - nIndex)/2), 1); welText.html(sText.substring(0, nIndex) + sStringTail); nCurrentWidth = welText.width(); } fCondition = function() { return true; }; } nIndex = welText.html().length - sStringTail.length; while(nCurrentWidth > nPixel) { if (!fCondition()) { break; } nIndex--; welText.html(sText.substring(0, nIndex) + sStringTail); nCurrentWidth = welText.width(); } }, /** * 여러개의 엘리먼트를 각각의 지정된 최대너비로 말줄임한다. * 말줄임할 엘리먼트의 css white-space값이 "nowrap"이어야한다. (컨테이너보다 텍스트가 길면 행변환되지않고 가로로 길게 표현되는 상태) * @param {Array} aElement 말줄임할 엘리먼트의 배열. 지정된 순서대로 말줄임한다. * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) * @param {Array} aMinWidth 말줄임할 너비의 배열. * @param {Function} fCondition 조건 함수. 내부에서 true를 리턴하는 동안에만 말줄임을 진행한다. * @example //#a #b #c의 너비를 각각 100, 50, 50픽셀로 줄임 (div#parent 가 200픽셀 이하이면 중단) //#c의 너비를 줄이는 동안 fCondition에서 false를 리턴하면 b, a는 말줄임 되지 않는다.
말줄임을적용할내용 말줄임을적용할내용 말줄임을적용할내용
ellipsisElementsToDesinatedWidth([jindo.$("c"), jindo.$("b"), jindo.$("a")], "...", [100, 50, 50], function(){ if (jindo.$Element("parent").width() > 200) { return true; } return false; }); */ ellipsisElementsToDesinatedWidth : function(aElement, sStringTail, aMinWidth, fCondition) { jindo.$A(aElement).forEach(function(el, i){ if (!el) { jindo.$A.Continue(); } nhn.husky.SE2M_Utils.ellipsisByPixel(el, sStringTail, aMinWidth[i], fCondition); }); }, /** * 숫자를 입력받아 정해진 길이만큼 앞에 "0"이 추가된 문자열을 구한다. * @param {Number} nNumber * @param {Number} nLength * @return {String} * @example paddingZero(10, 5); ==> "00010" (String) */ paddingZero : function(nNumber, nLength) { var sResult = nNumber.toString(); while (sResult.length < nLength) { sResult = ("0" + sResult); } return sResult; }, /** * string을 byte 단위로 짤라서 tail를 붙힌다. * @param {String} sString * @param {Number} nByte * @param {String} sTail * @example cutStringToByte('일이삼사오육', 6, '...') ==> '일이삼...' (string) */ cutStringToByte : function(sString, nByte, sTail){ if(sString === null || sString.length === 0) { return sString; } sString = sString.replace(/ +/g, " "); if (!sTail && sTail != "") { sTail = "..."; } var maxByte = nByte; var n=0; var nLen = sString.length; for(var i=0; i maxByte ) { return sString.substring(0, i)+sTail; } } return sString; }, /** * 입력받은 문자의 byte 구한다. * @param {String} ch * */ getCharByte : function(ch){ if (ch === null || ch.length < 1) { return 0; } var byteSize = 0; var str = escape(ch); if ( str.length == 1 ) { // when English then 1byte byteSize ++; } else if ( str.indexOf("%u") != -1 ) { // when Korean then 2byte byteSize += 2; } else if ( str.indexOf("%") != -1 ) { // else 3byte byteSize += str.length/3; } return byteSize; }, /** * Hash Table에서 원하는 키값만을 가지는 필터된 새로운 Hash Table을 구한다. * @param {HashTable} htUnfiltered * @param {Array} aKey * @return {HashTable} * @author senxation * @example getFilteredHashTable({ a : 1, b : 2, c : 3, d : 4 }, ["a", "c"]); ==> { a : 1, c : 3 } */ getFilteredHashTable : function(htUnfiltered, vKey) { if (!(vKey instanceof Array)) { return arguments.callee.call(this, htUnfiltered, [ vKey ]); } var waKey = jindo.$A(vKey); return jindo.$H(htUnfiltered).filter(function(vValue, sKey){ if (waKey.has(sKey) && vValue) { return true; } else { return false; } }).$value(); }, isBlankNode : function(elNode){ var isBlankTextNode = this.isBlankTextNode; var bEmptyContent = function(elNode){ if(!elNode) { return true; } if(isBlankTextNode(elNode)){ return true; } if(elNode.tagName == "BR") { return true; } if(elNode.innerHTML == " " || elNode.innerHTML == "") { return true; } return false; }; var bEmptyP = function(elNode){ if(elNode.tagName == "IMG" || elNode.tagName == "IFRAME"){ return false; } if(bEmptyContent(elNode)){ return true; } if(elNode.tagName == "P"){ for(var i=elNode.childNodes.length-1; i>=0; i--){ var elTmp = elNode.childNodes[i]; if(isBlankTextNode(elTmp)){ elTmp.parentNode.removeChild(elTmp); } } if(elNode.childNodes.length == 1){ if(elNode.firstChild.tagName == "IMG" || elNode.firstChild.tagName == "IFRAME"){ return false; } if(bEmptyContent(elNode.firstChild)){ return true; } } } return false; }; if(bEmptyP(elNode)){ return true; } for(var i=0, nLen=elNode.childNodes.length; i]*))", 'i'); var aResult = rx.exec(sHtmlTag); if (!aResult) { return ''; } var sAttrTmp = (aResult[1] || aResult[2] || aResult[3]); // for chrome 5.x bug! if (!!sAttrTmp) { sAttrTmp = sAttrTmp.replace(/[\"]/g, ''); } return sAttrTmp; }, /** * iframe 영역의 aling 정보를 다시 세팅하는 부분. * iframe 형태의 산출물을 에디터에 삽입 이후에 에디터 정렬기능을 추가 하였을때 ir_to_db 이전 시점에서 div태그에 정렬을 넣어주는 로직임. * 브라우저 형태에 따라 정렬 태그가 iframe을 감싸는 div 혹은 p 태그에 정렬이 추가된다. * @param {HTMLElement} el iframe의 parentNode * @param {Document} oDoc document */ // [COM-1151] SE2M_PreStringConverter 에서 수정하도록 변경 iframeAlignConverter : function(el, oDoc){ var sTagName = el.tagName.toUpperCase(); if(sTagName == "DIV" || sTagName == 'P'){ //irToDbDOM 에서 최상위 노드가 div 엘리먼트 이므로 parentNode가 없으면 최상의 div 노드 이므로 리턴한다. if(el.parentNode === null ){ return; } var elWYSIWYGDoc = oDoc; var wel = jindo.$Element(el); var sHtml = wel.html(); //현재 align을 얻어오기. var sAlign = jindo.$Element(el).attr('align') || jindo.$Element(el).css('text-align'); //if(!sAlign){ // P > DIV의 경우 문제 발생, 수정 화면에 들어 왔을 때 태그 깨짐 // return; //} //새로운 div 노드 생성한다. var welAfter = jindo.$Element(jindo.$('
', elWYSIWYGDoc)); welAfter.html(sHtml).attr('align', sAlign); wel.replace(welAfter); } }, /** * jindo.$JSON.fromXML을 변환한 메서드. * 소숫점이 있는 경우의 처리 시에 숫자로 변환하지 않도록 함(parseFloat 사용 안하도록 수정) * 관련 BTS : [COM-1093] * @param {String} sXML XML 형태의 문자열 * @return {jindo.$JSON} */ getJsonDatafromXML : function(sXML) { var o = {}; var re = /\s*<(\/?[\w:\-]+)((?:\s+[\w:\-]+\s*=\s*(?:"(?:\\"|[^"])*"|'(?:\\'|[^'])*'))*)\s*((?:\/>)|(?:><\/\1>|\s*))|\s*\s*|\s*>?([^<]*)/ig; var re2= /^[0-9]+(?:\.[0-9]+)?$/; var re3= /^\s+$/g; var ec = {"&":"&"," ":" ",""":"\"","<":"<",">":">"}; var fg = {tags:["/"],stack:[o]}; var es = function(s){ if (typeof s == "undefined") { return ""; } return s.replace(/&[a-z]+;/g, function(m){ return (typeof ec[m] == "string")?ec[m]:m; }); }; var at = function(s,c) { s.replace(/([\w\:\-]+)\s*=\s*(?:"((?:\\"|[^"])*)"|'((?:\\'|[^'])*)')/g, function($0,$1,$2,$3) { c[$1] = es(($2?$2.replace(/\\"/g,'"'):undefined)||($3?$3.replace(/\\'/g,"'"):undefined)); }); }; var em = function(o) { for(var x in o){ if (o.hasOwnProperty(x)) { if(Object.prototype[x]) { continue; } return false; } } return true; }; // $0 : 전체 // $1 : 태그명 // $2 : 속성문자열 // $3 : 닫는태그 // $4 : CDATA바디값 // $5 : 그냥 바디값 var cb = function($0,$1,$2,$3,$4,$5) { var cur, cdata = ""; var idx = fg.stack.length - 1; if (typeof $1 == "string" && $1) { if ($1.substr(0,1) != "/") { var has_attr = (typeof $2 == "string" && $2); var closed = (typeof $3 == "string" && $3); var newobj = (!has_attr && closed)?"":{}; cur = fg.stack[idx]; if (typeof cur[$1] == "undefined") { cur[$1] = newobj; cur = fg.stack[idx+1] = cur[$1]; } else if (cur[$1] instanceof Array) { var len = cur[$1].length; cur[$1][len] = newobj; cur = fg.stack[idx+1] = cur[$1][len]; } else { cur[$1] = [cur[$1], newobj]; cur = fg.stack[idx+1] = cur[$1][1]; } if (has_attr) { at($2,cur); } fg.tags[idx+1] = $1; if (closed) { fg.tags.length--; fg.stack.length--; } } else { fg.tags.length--; fg.stack.length--; } } else if (typeof $4 == "string" && $4) { cdata = $4; } else if (typeof $5 == "string" && $5.replace(re3, "")) { // [SMARTEDITORSUS-1525] 닫는 태그인데 공백문자가 들어있어 cdata 값을 덮어쓰는 경우 방지 cdata = es($5); } if (cdata.length > 0) { var par = fg.stack[idx-1]; var tag = fg.tags[idx]; if (re2.test(cdata)) { //cdata = parseFloat(cdata); }else if (cdata == "true" || cdata == "false"){ cdata = new Boolean(cdata); } if(typeof par =='undefined') { return; } if (par[tag] instanceof Array) { var o = par[tag]; if (typeof o[o.length-1] == "object" && !em(o[o.length-1])) { o[o.length-1].$cdata = cdata; o[o.length-1].toString = function(){ return cdata; }; } else { o[o.length-1] = cdata; } } else { if (typeof par[tag] == "object" && !em(par[tag])) { par[tag].$cdata = cdata; par[tag].toString = function() { return cdata; }; } else { par[tag] = cdata; } } } }; sXML = sXML.replace(/<(\?|\!-)[^>]*>/g, ""); sXML.replace(re, cb); return jindo.$Json(o); }, /** * 문자열내 자주 사용되는 특수문자 5개 (", ', &, <, >)를 HTML Entity Code 로 변경하여 반환 * @see http://www.w3.org/TR/html4/charset.html#entities * @param {String} sString 원본 문자열 * @returns {String} 변경된 문자열 * @example * replaceSpecialChar() or replaceSpecialChar(123) * // 결과: "" * * replaceSpecialChar("", ', &, <, >") * // 결과: &quot;, &#39;, &amp;, &lt;, &gt; */ replaceSpecialChar : function(sString){ return (typeof(sString) == "string") ? (sString.replace(/\&/g, "&").replace(/\"/g, """).replace(/\'/g, "'").replace(//g, ">")) : ""; }, /** * 문자열내 자주 사용되는 HTML Entity Code 5개를 원래 문자로 (", ', &, <, >)로 변경하여 반환 * @see http://www.w3.org/TR/html4/charset.html#entities * @param {String} sString 원본 문자열 * @returns {String} 변경된 문자열 * @example * restoreSpecialChar() or restoreSpecialChar(123) * // 결과: "" * * restoreSpecialChar("&quot;, &#39;, &amp;, &lt;, &gt;") * // 결과: ", ', &, <, > */ restoreSpecialChar : function(sString){ return (typeof(sString) == "string") ? (sString.replace(/"/g, "\"").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")) : ""; } }; /** * nhn.husky.AutoResizer * HTML모드와 TEXT 모드의 편집 영역인 TEXTAREA에 대한 자동확장 처리 */ nhn.husky.AutoResizer = jindo.$Class({ welHiddenDiv : null, welCloneDiv : null, elContainer : null, $init : function(el, htOption){ var aCopyStyle = [ 'lineHeight', 'textDecoration', 'letterSpacing', 'fontSize', 'fontFamily', 'fontStyle', 'fontWeight', 'textTransform', 'textAlign', 'direction', 'wordSpacing', 'fontSizeAdjust', 'paddingTop', 'paddingLeft', 'paddingBottom', 'paddingRight', 'width' ], i = aCopyStyle.length, oCss = { "position" : "absolute", "top" : -9999, "left" : -9999, "opacity": 0, "overflow": "hidden", "wordWrap" : "break-word" }; this.nMinHeight = htOption.nMinHeight; this.wfnCallback = htOption.wfnCallback; this.elContainer = el.parentNode; this.welTextArea = jindo.$Element(el); // autoresize를 적용할 TextArea this.welHiddenDiv = jindo.$Element('
'); this.wfnResize = jindo.$Fn(this._resize, this); this.sOverflow = this.welTextArea.css("overflow"); this.welTextArea.css("overflow", "hidden"); while(i--){ oCss[aCopyStyle[i]] = this.welTextArea.css(aCopyStyle[i]); } this.welHiddenDiv.css(oCss); this.nLastHeight = this.welTextArea.height(); }, bind : function(){ this.welCloneDiv = jindo.$Element(this.welHiddenDiv.$value().cloneNode(false)); this.wfnResize.attach(this.welTextArea, "keyup"); this.welCloneDiv.appendTo(this.elContainer); this._resize(); }, unbind : function(){ this.wfnResize.detach(this.welTextArea, "keyup"); this.welTextArea.css("overflow", this.sOverflow); if(this.welCloneDiv){ this.welCloneDiv.leave(); } }, _resize : function(){ var sContents = this.welTextArea.$value().value, bExpand = false, nHeight; if(sContents === this.sContents){ return; } this.sContents = sContents.replace(/&/g, '&').replace(//g, '>').replace(/ /g, ' ').replace(/\n/g, '
'); this.sContents += "
"; // 마지막 개행 뒤에
을 더 붙여주어야 늘어나는 높이가 동일함 this.welCloneDiv.html(this.sContents); nHeight = this.welCloneDiv.height(); if(nHeight < this.nMinHeight){ nHeight = this.nMinHeight; } this.welTextArea.css("height", nHeight + "px"); this.elContainer.style.height = nHeight + "px"; if(this.nLastHeight < nHeight){ bExpand = true; } this.wfnCallback(bExpand); } }); /** * 문자를 연결하는 '+' 대신에 java와 유사하게 처리하도록 문자열 처리하도록 만드는 object * @author nox * @example var sTmp1 = new StringBuffer(); sTmp1.append('1').append('2').append('3'); var sTmp2 = new StringBuffer('1'); sTmp2.append('2').append('3'); var sTmp3 = new StringBuffer('1').append('2').append('3'); */ if ('undefined' != typeof(StringBuffer)) { StringBuffer = {}; } StringBuffer = function(str) { this._aString = []; if ('undefined' != typeof(str)) { this.append(str); } }; StringBuffer.prototype.append = function(str) { this._aString.push(str); return this; }; StringBuffer.prototype.toString = function() { return this._aString.join(''); }; StringBuffer.prototype.setLength = function(nLen) { if('undefined' == typeof(nLen) || 0 >= nLen) { this._aString.length = 0; } else { this._aString.length = nLen; } }; /** * Installed Font Detector * @author hooriza * * @see http://remysharp.com/2008/07/08/how-to-detect-if-a-font-is-installed-only-using-javascript/ */ (function() { var oDummy = null, rx = /,/gi; IsInstalledFont = function(sFont) { var sDefFont = sFont == 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS'; if (!oDummy) { oDummy = document.createElement('div'); } var sStyle = 'position:absolute !important; font-size:200px !important; left:-9999px !important; top:-9999px !important;'; oDummy.innerHTML = 'mmmmiiiii'+unescape('%uD55C%uAE00'); oDummy.style.cssText = sStyle + 'font-family:"' + sDefFont + '" !important'; var elBody = document.body || document.documentElement; if(elBody.firstChild){ elBody.insertBefore(oDummy, elBody.firstChild); }else{ document.body.appendChild(oDummy); } var sOrg = oDummy.offsetWidth + '-' + oDummy.offsetHeight; oDummy.style.cssText = sStyle + 'font-family:"' + sFont.replace(rx, '","') + '", "' + sDefFont + '" !important'; var bInstalled = sOrg != (oDummy.offsetWidth + '-' + oDummy.offsetHeight); document.body.removeChild(oDummy); return bInstalled; }; })(); //{ /** * @fileOverview This file contains Husky plugin that takes care of loading css files dynamically * @name hp_SE2B_CSSLoader.js */ nhn.husky.SE2B_CSSLoader = jindo.$Class({ name : "SE2B_CSSLoader", bCssLoaded : false, // load & continue with the message right away. aInstantLoadTrigger : ["OPEN_QE_LAYER", "SHOW_ACTIVE_LAYER", "SHOW_DIALOG_LAYER", "START_SPELLCHECK"], // if a rendering bug occurs in IE, give some delay before continue processing the message. aDelayedLoadTrigger : ["MSG_SE_OBJECT_EDIT_REQUESTED", "OBJECT_MODIFY", "MSG_SE_DUMMY_OBJECT_EDIT_REQUESTED", "TOGGLE_TOOLBAR_ACTIVE_LAYER", "SHOW_TOOLBAR_ACTIVE_LAYER"], $init : function(){ this.htOptions = nhn.husky.SE2M_Configuration.SE2B_CSSLoader; // only IE's slow if(!jindo.$Agent().navigator().ie){ this.$ON_MSG_APP_READY = jindo.$Fn(function(){ this.loadSE2CSS(); }, this).bind() }else{ for(var i=0, nLen = this.aInstantLoadTrigger.length; i= 33 && oKeyInfo.keyCode <= 40){ this.oApp.exec("MSG_EDITING_AREA_RESIZE_STARTED", []); this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, this.aHeightChangeKeyMap[oKeyInfo.keyCode-33]]); this.oApp.exec("MSG_EDITING_AREA_RESIZE_ENDED", []); oEvent.stop(); } }, _mousedown : function(oEvent){ this.iStartHeight = oEvent.pos().clientY; this.iStartHeightOffset = oEvent.pos().layerY; this.$FnMouseMove.attach(document, "mousemove"); this.$FnMouseMove_Parent.attach(parent.document, "mousemove"); this.$FnMouseUp.attach(document, "mouseup"); this.$FnMouseUp.attach(parent.document, "mouseup"); this.iStartHeight = oEvent.pos().clientY; this.oApp.exec("MSG_EDITING_AREA_RESIZE_STARTED", [this.$FnMouseDown, this.$FnMouseMove, this.$FnMouseUp]); }, _mousemove : function(oEvent){ var iHeightChange = oEvent.pos().clientY - this.iStartHeight; this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, iHeightChange]); }, _mousemove_parent : function(oEvent){ var iHeightChange = oEvent.pos().pageY - (this.welIFrame.offset().top + this.iStartHeight); this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, iHeightChange]); }, _mouseup : function(oEvent){ this.$FnMouseMove.detach(document, "mousemove"); this.$FnMouseMove_Parent.detach(parent.document, "mousemove"); this.$FnMouseUp.detach(document, "mouseup"); this.$FnMouseUp.detach(parent.document, "mouseup"); this.oApp.exec("MSG_EDITING_AREA_RESIZE_ENDED", [this.$FnMouseDown, this.$FnMouseMove, this.$FnMouseUp]); } }); // Sample plugin. Use CTRL+T to toggle the toolbar nhn.husky.SE_ToolbarToggler = $Class({ name : "SE_ToolbarToggler", bUseToolbar : true, $init : function(oAppContainer, bUseToolbar){ this._assignHTMLObjects(oAppContainer, bUseToolbar); }, _assignHTMLObjects : function(oAppContainer, bUseToolbar){ oAppContainer = jindo.$(oAppContainer) || document; this.toolbarArea = cssquery.getSingle(".se2_tool", oAppContainer); //설정이 없거나, 사용하겠다고 표시한 경우 block 처리 if( typeof(bUseToolbar) == 'undefined' || bUseToolbar === true){ this.toolbarArea.style.display = "block"; }else{ this.toolbarArea.style.display = "none"; } }, $ON_MSG_APP_READY : function(){ this.oApp.exec("REGISTER_HOTKEY", ["ctrl+t", "SE_TOGGLE_TOOLBAR", []]); }, $ON_SE_TOGGLE_TOOLBAR : function(){ this.toolbarArea.style.display = (this.toolbarArea.style.display == "none")?"block":"none"; this.oApp.exec("MSG_EDITING_AREA_SIZE_CHANGED", []); } }); nhn.husky.HuskyCore.addLoadedFile("hp_SE2M_FindReplacePlugin$Lazy.js"); /** * @depends nhn.husky.SE2M_FindReplacePlugin * this.oApp.registerLazyMessage(["TOGGLE_FIND_REPLACE_LAYER","SHOW_FIND_LAYER","SHOW_REPLACE_LAYER","SHOW_FIND_REPLACE_LAYER"], ["hp_SE2M_FindReplacePlugin$Lazy.js","N_FindReplace.js"]); */ nhn.husky.HuskyCore.mixin(nhn.husky.SE2M_FindReplacePlugin, { //@lazyload_js TOGGLE_FIND_REPLACE_LAYER,SHOW_FIND_LAYER,SHOW_REPLACE_LAYER,SHOW_FIND_REPLACE_LAYER:N_FindReplace.js[ _assignHTMLElements : function(){ var oAppContainer = this.oApp.htOptions.elAppContainer; this.oApp.exec("LOAD_HTML", ["find_and_replace"]); // this.oEditingWindow = jindo.$$.getSingle("IFRAME", oAppContainer); this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_findAndReplace_layer", oAppContainer); this.welDropdownLayer = jindo.$Element(this.elDropdownLayer); var oTmp = jindo.$$("LI", this.elDropdownLayer); this.oFindTab = oTmp[0]; this.oReplaceTab = oTmp[1]; oTmp = jindo.$$(".container > .bx", this.elDropdownLayer); this.oFindInputSet = jindo.$$.getSingle(".husky_se2m_find_ui", this.elDropdownLayer); this.oReplaceInputSet = jindo.$$.getSingle(".husky_se2m_replace_ui", this.elDropdownLayer); this.elTitle = jindo.$$.getSingle("H3", this.elDropdownLayer); this.oFindInput_Keyword = jindo.$$.getSingle("INPUT", this.oFindInputSet); oTmp = jindo.$$("INPUT", this.oReplaceInputSet); this.oReplaceInput_Original = oTmp[0]; this.oReplaceInput_Replacement = oTmp[1]; this.oFindNextButton = jindo.$$.getSingle("BUTTON.husky_se2m_find_next", this.elDropdownLayer); this.oReplaceFindNextButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace_find_next", this.elDropdownLayer); this.oReplaceButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace", this.elDropdownLayer); this.oReplaceAllButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace_all", this.elDropdownLayer); this.aCloseButtons = jindo.$$("BUTTON.husky_se2m_cancel", this.elDropdownLayer); }, $LOCAL_BEFORE_FIRST : function(sMsg){ this._assignHTMLElements(); this.oFindReplace = new nhn.FindReplace(this.oEditingWindow); for(var i=0; i= this.nMaxLevel){ return nMaxCount; } } elChild = elChild.nextSibling; } return nMaxCount; }; return (countChildQuote(elNode) >= this.nMaxLevel); }, _unwrapBlock : function(tag){ var oSelection = this.oApp.getSelection(); var elCommonAncestor = oSelection.commonAncestorContainer; while(elCommonAncestor && elCommonAncestor.tagName != tag){elCommonAncestor = elCommonAncestor.parentNode;} if(!elCommonAncestor){return;} this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["CANCEL BLOCK QUOTE", {sSaveTarget:"BODY"}]); // [SMARTEDITORSUS-1782] 인용구가 제거되기 전에 선택 영역안에 있는 마지막 텍스트노드를 미리 찾아둔다. var oLastTextNode = oSelection.commonAncestorContainer; if(oLastTextNode.nodeType !== 3){ // 텍스트노드가 아니면 var aTextNodesInRange = oSelection.getTextNodes() || "", nLastIndex = aTextNodesInRange.length - 1; oLastTextNode = (nLastIndex > -1) ? aTextNodesInRange[nLastIndex] : null; } // 인용구내의 요소들을 바깥으로 모두 꺼낸 후 인용구요소를 제거 while(elCommonAncestor.firstChild){elCommonAncestor.parentNode.insertBefore(elCommonAncestor.firstChild, elCommonAncestor);} elCommonAncestor.parentNode.removeChild(elCommonAncestor); // [SMARTEDITORSUS-1782] 찾아둔 마지막 텍스트노드 끝으로 커서를 이동시킨다. if(oLastTextNode){ oSelection.selectNodeContents(oLastTextNode); oSelection.collapseToEnd(); oSelection.select(); } this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["CANCEL BLOCK QUOTE", {sSaveTarget:"BODY"}]); }, _wrapBlock : function(tag, className){ var oSelection, oLineInfo, oStart, oEnd, rxDontUseAsWhole = /BODY|TD|LI/i, oStartNode, oEndNode, oNode, elCommonAncestor, elCommonNode, elParentQuote, elInsertBefore, oFormattingNode, elNextNode, elParentNode, aQuoteChild, aQuoteCloneChild, i, nLen, oP, sBookmarkID; this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["BLOCK QUOTE", {sSaveTarget:"BODY"}]); oSelection = this.oApp.getSelection(); // var sBookmarkID = oSelection.placeStringBookmark(); // [SMARTEDITORSUS-430] 문자를 입력하고 Enter 후 인용구를 적용할 때 위의 문자들이 인용구 안에 들어가는 문제 // [SMARTEDITORSUS-1323] 사진 첨부 후 인용구 적용 시 첨부한 사진이 삭제되는 현상 if(oSelection.startContainer === oSelection.endContainer && oSelection.startContainer.nodeType === 1 && oSelection.startContainer.tagName === "P"){ if(nhn.husky.SE2M_Utils.isBlankNode(oSelection.startContainer) || nhn.husky.SE2M_Utils.isFirstChildOfNode("IMG", oSelection.startContainer.tagName, oSelection.startContainer) || nhn.husky.SE2M_Utils.isFirstChildOfNode("IFRAME", oSelection.startContainer.tagName, oSelection.startContainer)){ oLineInfo = oSelection.getLineInfo(true); }else{ oLineInfo = oSelection.getLineInfo(false); } }else{ oLineInfo = oSelection.getLineInfo(false); } oStart = oLineInfo.oStart; oEnd = oLineInfo.oEnd; if(oStart.bParentBreak && !rxDontUseAsWhole.test(oStart.oLineBreaker.tagName)){ oStartNode = oStart.oNode.parentNode; }else{ oStartNode = oStart.oNode; } if(oEnd.bParentBreak && !rxDontUseAsWhole.test(oEnd.oLineBreaker.tagName)){ oEndNode = oEnd.oNode.parentNode; }else{ oEndNode = oEnd.oNode; } oSelection.setStartBefore(oStartNode); oSelection.setEndAfter(oEndNode); oNode = this._expandToTableStart(oSelection, oEndNode); if(oNode){ oEndNode = oNode; oSelection.setEndAfter(oNode); } oNode = this._expandToTableStart(oSelection, oStartNode); if(oNode){ oStartNode = oNode; oSelection.setStartBefore(oNode); } oNode = oStartNode; // IE에서는 commonAncestorContainer 자체는 select 가능하지 않고, 하위에 commonAncestorContainer를 대체 하더라도 똑같은 영역이 셀렉트 되어 보이는 // 노드가 있을 경우 하위 노드가 commonAncestorContainer로 반환됨. // 그래서, 스크립트로 commonAncestorContainer 계산 하도록 함. // 예) //

TEST

를 선택 할 경우, TEST가 commonAncestorContainer로 잡힘 oSelection.fixCommonAncestorContainer(); elCommonAncestor = oSelection.commonAncestorContainer; if(oSelection.startContainer == oSelection.endContainer && oSelection.endOffset-oSelection.startOffset == 1){ elCommonNode = oSelection.startContainer.childNodes[oSelection.startOffset]; }else{ elCommonNode = oSelection.commonAncestorContainer; } elParentQuote = this._findParentQuote(elCommonNode); if(elParentQuote){ elParentQuote.className = className; // [SMARTEDITORSUS-1239] blockquote 태그교체시 style 적용 this._setStyle(elParentQuote, this.htQuoteStyles_view[className]); // --[SMARTEDITORSUS-1239] return; } while(!elCommonAncestor.tagName || (elCommonAncestor.tagName && elCommonAncestor.tagName.match(/UL|OL|LI|IMG|IFRAME/))){ elCommonAncestor = elCommonAncestor.parentNode; } // find the insertion position for the formatting tag right beneath the common ancestor container while(oNode && oNode != elCommonAncestor && oNode.parentNode != elCommonAncestor){oNode = oNode.parentNode;} if(oNode == elCommonAncestor){ elInsertBefore = elCommonAncestor.firstChild; }else{ elInsertBefore = oNode; } oFormattingNode = oSelection._document.createElement(tag); if(className){ oFormattingNode.className = className; // [SMARTEDITORSUS-1239] 에디터에서 인용구 5개이상 상입 시 에디터를 뚫고 노출되는 현상 // [SMARTEDITORSUS-1229] 인용구 여러 개 중첩하면 에디터 본문 영역을 벗어나는 현상 // blockquate style 적용 this._setStyle(oFormattingNode, this.htQuoteStyles_view[className]); } elCommonAncestor.insertBefore(oFormattingNode, elInsertBefore); oSelection.setStartAfter(oFormattingNode); oSelection.setEndAfter(oEndNode); oSelection.surroundContents(oFormattingNode); if(this._isExceedMaxDepth(oFormattingNode)){ alert(this.oApp.$MSG("SE2M_Quote.exceedMaxCount").replace("#MaxCount#", (this.nMaxLevel + 1))); this.oApp.exec("HIDE_ACTIVE_LAYER", []); elNextNode = oFormattingNode.nextSibling; elParentNode = oFormattingNode.parentNode; aQuoteChild = oFormattingNode.childNodes; aQuoteCloneChild = []; jindo.$Element(oFormattingNode).leave(); for(i = 0, nLen = aQuoteChild.length; i < nLen; i++){ aQuoteCloneChild[i] = aQuoteChild[i]; } for(i = 0, nLen = aQuoteCloneChild.length; i < nLen; i++){ if(!!elNextNode){ jindo.$Element(elNextNode).before(aQuoteCloneChild[i]); }else{ jindo.$Element(elParentNode).append(aQuoteCloneChild[i]); } } return; } oSelection.selectNodeContents(oFormattingNode); // insert an empty line below, so the text cursor can move there if(oFormattingNode && oFormattingNode.parentNode && oFormattingNode.parentNode.tagName == "BODY" && !oFormattingNode.nextSibling){ oP = oSelection._document.createElement("P"); //oP.innerHTML = unescape("
"); oP.innerHTML = " "; oFormattingNode.parentNode.insertBefore(oP, oFormattingNode.nextSibling); } // oSelection.removeStringBookmark(sBookmarkID); // Insert an empty line inside the blockquote if it's empty. // This is done to position the cursor correctly when the contents of the blockquote is empty in Chrome. if(nhn.husky.SE2M_Utils.isBlankNode(oFormattingNode)){ // [SMARTEDITORSUS-1751] 현재 undo/redo 기능을 사용하지 않고 ie7은 주요브라우저에서 제외되었기 때문에 다른 이슈들 처리시 복잡도를 줄이기 위해 코멘트처리함 // [SMARTEDITORSUS-645] 편집영역 포커스 없이 인용구 추가했을 때 IE7에서 박스가 늘어나는 문제 //oFormattingNode.innerHTML = " "; // [SMARTEDITORSUS-1567] P 태그로 감싸주지 않으면 크롬에서 blockquote 태그에 정렬이 적용되는데 IR_TO_DB 컨버터에서 style을 리셋하고 있기 때문에 저장되는 시점에 정렬이 제거된다. // [SMARTEDITORSUS-1229] 인용구 여러 개 중첩하면 에디터 본문 영역을 벗어나는 현상 oFormattingNode.innerHTML = " "; // [SMARTEDITORSUS-1741] 커서가 p태그 안으로 들어가도록 세팅 oSelection.selectNodeContents(oFormattingNode.firstChild); oSelection.collapseToStart(); oSelection.select(); } //oSelection.select(); setTimeout(jindo.$Fn(function(oSelection){ sBookmarkID = oSelection.placeStringBookmark(); oSelection.select(); oSelection.removeStringBookmark(sBookmarkID); this.oApp.exec("FOCUS"); // [SMARTEDITORSUS-469] [SMARTEDITORSUS-434] 에디터 로드 후 최초 삽입한 인용구 안에 포커스가 가지 않는 문제 },this).bind(oSelection), 0); this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["BLOCK QUOTE", {sSaveTarget:"BODY"}]); return oFormattingNode; }, _expandToTableStart : function(oSelection, oNode){ var elCommonAncestor = oSelection.commonAncestorContainer; var oResultNode = null; var bLastIteration = false; while(oNode && !bLastIteration){ if(oNode == elCommonAncestor){bLastIteration = true;} if(/TBODY|TFOOT|THEAD|TR/i.test(oNode.tagName)){ oResultNode = this._getTableRoot(oNode); break; } oNode = oNode.parentNode; } return oResultNode; }, _getTableRoot : function(oNode){ while(oNode && oNode.tagName != "TABLE"){oNode = oNode.parentNode;} return oNode; }, _setStyle : function(el, sStyle) { el.setAttribute("style", sStyle); el.style.cssText = sStyle; } //@lazyload_js] }); nhn.husky.HuskyCore.addLoadedFile("hp_SE2M_SCharacter$Lazy.js"); /** * @depends nhn.husky.SE2M_SCharacter * this.oApp.registerLazyMessage(["TOGGLE_SCHARACTER_LAYER"], ["hp_SE2M_SCharacter$Lazy.js"]); */ nhn.husky.HuskyCore.mixin(nhn.husky.SE2M_SCharacter, { //@lazyload_js TOGGLE_SCHARACTER_LAYER[ _assignHTMLObjects : function(oAppContainer){ oAppContainer = jindo.$(oAppContainer) || document; this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_seditor_sCharacter_layer", oAppContainer); this.oTextField = jindo.$$.getSingle("INPUT", this.elDropdownLayer); this.oInsertButton = jindo.$$.getSingle("BUTTON.se2_confirm", this.elDropdownLayer); this.aCloseButton = jindo.$$("BUTTON.husky_se2m_sCharacter_close", this.elDropdownLayer); this.aSCharList = jindo.$$("UL.husky_se2m_sCharacter_list", this.elDropdownLayer); var oLabelUL = jindo.$$.getSingle("UL.se2_char_tab", this.elDropdownLayer); this.aLabel = jindo.$$(">LI", oLabelUL); }, $LOCAL_BEFORE_FIRST : function(sFullMsg){ this.bIE = jindo.$Agent().navigator().ie; this._assignHTMLObjects(this.oApp.htOptions.elAppContainer); this.charSet = []; this.charSet[0] = unescape('FF5B FF5D 3014 3015 3008 3009 300A 300B 300C 300D 300E 300F 3010 3011 2018 2019 201C 201D 3001 3002 %B7 2025 2026 %A7 203B 2606 2605 25CB 25CF 25CE 25C7 25C6 25A1 25A0 25B3 25B2 25BD 25BC 25C1 25C0 25B7 25B6 2664 2660 2661 2665 2667 2663 2299 25C8 25A3 25D0 25D1 2592 25A4 25A5 25A8 25A7 25A6 25A9 %B1 %D7 %F7 2260 2264 2265 221E 2234 %B0 2032 2033 2220 22A5 2312 2202 2261 2252 226A 226B 221A 223D 221D 2235 222B 222C 2208 220B 2286 2287 2282 2283 222A 2229 2227 2228 FFE2 21D2 21D4 2200 2203 %B4 FF5E 02C7 02D8 02DD 02DA 02D9 %B8 02DB %A1 %BF 02D0 222E 2211 220F 266D 2669 266A 266C 327F 2192 2190 2191 2193 2194 2195 2197 2199 2196 2198 321C 2116 33C7 2122 33C2 33D8 2121 2668 260F 260E 261C 261E %B6 2020 2021 %AE %AA %BA 2642 2640').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); this.charSet[1] = unescape('%BD 2153 2154 %BC %BE 215B 215C 215D 215E %B9 %B2 %B3 2074 207F 2081 2082 2083 2084 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 FFE6 %24 FFE5 FFE1 20AC 2103 212B 2109 FFE0 %A4 2030 3395 3396 3397 2113 3398 33C4 33A3 33A4 33A5 33A6 3399 339A 339B 339C 339D 339E 339F 33A0 33A1 33A2 33CA 338D 338E 338F 33CF 3388 3389 33C8 33A7 33A8 33B0 33B1 33B2 33B3 33B4 33B5 33B6 33B7 33B8 33B9 3380 3381 3382 3383 3384 33BA 33BB 33BC 33BD 33BE 33BF 3390 3391 3392 3393 3394 2126 33C0 33C1 338A 338B 338C 33D6 33C5 33AD 33AE 33AF 33DB 33A9 33AA 33AB 33AC 33DD 33D0 33D3 33C3 33C9 33DC 33C6').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); this.charSet[2] = unescape('3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 326A 326B 326C 326D 326E 326F 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 327A 327B 24D0 24D1 24D2 24D3 24D4 24D5 24D6 24D7 24D8 24D9 24DA 24DB 24DC 24DD 24DE 24DF 24E0 24E1 24E2 24E3 24E4 24E5 24E6 24E7 24E8 24E9 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 246A 246B 246C 246D 246E 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 320A 320B 320C 320D 320E 320F 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 321A 321B 249C 249D 249E 249F 24A0 24A1 24A2 24A3 24A4 24A5 24A6 24A7 24A8 24A9 24AA 24AB 24AC 24AD 24AE 24AF 24B0 24B1 24B2 24B3 24B4 24B5 2474 2475 2476 2477 2478 2479 247A 247B 247C 247D 247E 247F 2480 2481 2482').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); this.charSet[3] = unescape('3131 3132 3133 3134 3135 3136 3137 3138 3139 313A 313B 313C 313D 313E 313F 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 314A 314B 314C 314D 314E 314F 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 315A 315B 315C 315D 315E 315F 3160 3161 3162 3163 3165 3166 3167 3168 3169 316A 316B 316C 316D 316E 316F 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 317A 317B 317C 317D 317E 317F 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 318A 318B 318C 318D 318E').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); this.charSet[4] = unescape('0391 0392 0393 0394 0395 0396 0397 0398 0399 039A 039B 039C 039D 039E 039F 03A0 03A1 03A3 03A4 03A5 03A6 03A7 03A8 03A9 03B1 03B2 03B3 03B4 03B5 03B6 03B7 03B8 03B9 03BA 03BB 03BC 03BD 03BE 03BF 03C0 03C1 03C3 03C4 03C5 03C6 03C7 03C8 03C9 %C6 %D0 0126 0132 013F 0141 %D8 0152 %DE 0166 014A %E6 0111 %F0 0127 I 0133 0138 0140 0142 0142 0153 %DF %FE 0167 014B 0149 0411 0413 0414 0401 0416 0417 0418 0419 041B 041F 0426 0427 0428 0429 042A 042B 042C 042D 042E 042F 0431 0432 0433 0434 0451 0436 0437 0438 0439 043B 043F 0444 0446 0447 0448 0449 044A 044B 044C 044D 044E 044F').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); this.charSet[5] = unescape('3041 3042 3043 3044 3045 3046 3047 3048 3049 304A 304B 304C 304D 304E 304F 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 305A 305B 305C 305D 305E 305F 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 306A 306B 306C 306D 306E 306F 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 307A 307B 307C 307D 307E 307F 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 308A 308B 308C 308D 308E 308F 3090 3091 3092 3093 30A1 30A2 30A3 30A4 30A5 30A6 30A7 30A8 30A9 30AA 30AB 30AC 30AD 30AE 30AF 30B0 30B1 30B2 30B3 30B4 30B5 30B6 30B7 30B8 30B9 30BA 30BB 30BC 30BD 30BE 30BF 30C0 30C1 30C2 30C3 30C4 30C5 30C6 30C7 30C8 30C9 30CA 30CB 30CC 30CD 30CE 30CF 30D0 30D1 30D2 30D3 30D4 30D5 30D6 30D7 30D8 30D9 30DA 30DB 30DC 30DD 30DE 30DF 30E0 30E1 30E2 30E3 30E4 30E5 30E6 30E7 30E8 30E9 30EA 30EB 30EC 30ED 30EE 30EF 30F0 30F1 30F2 30F3 30F4 30F5 30F6').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); var funcInsert = jindo.$Fn(this.oApp.exec, this.oApp).bind("INSERT_SCHARACTERS", [this.oTextField.value]); jindo.$Fn(funcInsert, this).attach(this.oInsertButton, "click"); this.oApp.exec("SET_SCHARACTER_LIST", [this.charSet]); for(var i=0; i 1){return;} this.oApp.exec("SELECT_SCHARACTER", [sChar]); weEvent.stop(); }, $ON_SELECT_SCHARACTER : function(schar){ this.oTextField.value += schar; if(this.oTextField.createTextRange){ var oTextRange = this.oTextField.createTextRange(); oTextRange.collapse(false); oTextRange.select(); }else{ if(this.oTextField.selectionEnd){ this.oTextField.selectionEnd = this.oTextField.value.length; this.oTextField.focus(); } } }, $ON_INSERT_SCHARACTERS : function(){ this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["INSERT SCHARACTER"]); this.oApp.exec("PASTE_HTML", [this.oTextField.value]); this.oApp.exec("FOCUS"); this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["INSERT SCHARACTER"]); this.oApp.exec("HIDE_ACTIVE_LAYER", []); }, $ON_CHANGE_SCHARACTER_SET : function(nSCharSet){ for(var i=0; i"); if(this.bIE){ button = jindo.$("