J-한솔넷

네이버 스마트 에디터 분석 #2 본문

웹 개발관련

네이버 스마트 에디터 분석 #2

jhansol 2013. 2. 15. 04:22

네이버의 스마트 에디터를 분석하면서부터 자기 반성의 시간을 갖게됩니다. 참! 안일한 생각과 우물한 개구리처럼 살고 있었구나 싶습니다. 


스킨파일을 열어보면 각종 스크립트 파일이 포함되어 있는데, 여기서 가장 중심이 되는 파일이 smarteditor2.min.js이 아닌가 싶습니다. 이 파일을 처음 열었을 때는 달랑 한줄 밖에 없어 "이거 뭐야! 왜 이리 간단해! 이것 때문에 별도의 파일로 만들어 놨어?" 하는 황당하다는 느낌을 받았습니다. 그런데 즐 끝으로 커서를 옮겼을 때 317253열(헉!)!!!!!.... 일단 기가 질렸습니다.


파일의 용량을 줄이기 위해 매우 애쓴 흔적이 보였습니다. 불필요한 세미콜론(;), 공백 하나 없습니다. 아래 내용은 이 파일의 일부분을 직접 수작업하여 표시한 것입니다. 아래 내용은 이 파일의 빙산의 일각입니다. 이 파일이 스마트 에디터의 주 기능을 담당하는 것 같습니다.


아래 내용은 스마트 에디터에서 지정 내용을 검색하고 바꾸기(취환)하는 객체를 생성하는 부분입니다. 객체를 생성할 때에는 jindo 객체의 $Class() 메서드를 이용하여 생성하고 있습니다. 객체의 요소는 역시 JSON 표기법을 이용하여 정의하고 있습니다.


FindReplace 객체 안에 들어 있는 메서드는 아래와 같습니다. 일단 여기서는 어떤 메스드가 있는지만 살표보겠습니다.

$init()

find()

findNew()

findNext()

findReset();

replace();

replaceAll()

_isBlankTextNode()

_getNextNod()

_getNextTextNode();

_getFirstTextNode()

_addToTextMap()

_createTextMap()

replaceAll_js()



smarteditor2.min.js 파일의 FindReplace 객체 소스


var nSE2Version=9186;

if(typeof window.nhn=="undefined") {

window.nhn={};

}

nhn.FindReplace=jindo.$Class(

{

sKeyword:"",

window:null,

document:null,

bBrowserSupported:false,

bEOC:false,

$init:function(c){

this.sInlineContainer="SPAN|B|U|I|S|STRIKE";

this.rxInlineContainer=new RegExp("^("+this.sInlineContainer+")$");

this.window=c;

this.document=this.window.document;

if(this.document.domain!=this.document.location.hostname) {

var a=jindo.$Agent();

var b=a.navigator();

if(b.firefox&&b.version<3) {

this.bBrowserSupported=false;

this.find=function(){

return 3;

};

return;

}

}

this.bBrowserSupported=true;

},

find:function(a,c,d,e){

var f,b;

this.window.focus();

if(!a){

return 2;

}

this.bEOC=false;

f=this.findNext(a,c,d,e);

if(f){

return 0;

}

this.bEOC=true;

f=this.findNew(a,c,d,e);

if(f){

return 0;

}

return 1;

},

findNew:function(a,b,c,d){

this.findReset();

return this.findNext(a,b,c,d);

},

findNext:function(a,b,d,g){

var h;

b=b||false;

g=g||false;

d=d||false;

if(this.window.find){

var j=false;

return this.window.find(a,b,d,j,g);

}

if(this.document.body.createTextRange){

try{

var c=0;

if(d){

c+=1;

}

if(g){

c+=2;

}

if(b){

c+=4;

}

this.window.focus();

this._range=this.document.selection.createRangeCollection().item(0);

this._range.collapse(false);

h=this._range.findText(a,1,c);

this._range.select();

return h;

}catch(f){

return false;

}

}

return false;

},

findReset:function(){

if(this.window.find){

this.window.getSelection().removeAllRanges();

return;

}

if(this.document.body.createTextRange){

this._range=this.document.body.createTextRange();

this._range.collapse(true);this._range.select();

}

},

replace:function(c,f,b,d,g){

if(!c){

return 4;

}

var e=new nhn.HuskyRange(this.window);e.setFromSelection();

b=b||false;

var a,h=e.toString();

if(b){

a=(h==c);

}

else{

a=(h.toLowerCase()==c.toLowerCase());

}

if(!a){

return this.find(c,b,d,g)+2;

}

if(typeof f=="function"){

e=f(e);

}

else{

e.pasteText(f);

}

e.select();

return this.find(c,b,d,g);

},

replaceAll:function(e,h,j,b){

if(!e){

return -1;

}

var c=false;

var l;

var a=0;

var k=this.window;

if(this.find(e,j,c,b)!==0){

return a;

}

var g=new nhn.HuskyRange(this.window);

g.setFromSelection();

g.collapseToStart();

var m=this.window.document.createElement("SPAN");

m.innerHTML=unescape("%uFEFF");

g.insertNode(m);g.select();

var d=g.placeStringBookmark();

this.bEOC=false;

while(!this.bEOC){

l=this.replace(e,h,j,c,b);

if(l==0||l==1){

a++;

}

}

var f=function(){

var n=new nhn.HuskyRange(k);

n.setFromSelection();

g.moveToBookmark(d);

var o=g.compareBoundaryPoints(nhn.W3CDOMRange.START_TO_END,n);

if(o==1){

return false;

}

return true;

};

l=0;this.bEOC=false;

while(!f()&&l==0&&!this.bEOC){

l=this.replace(e,h,j,c,b);

if(l==0||l==1){

a++;

}

}

g.moveToBookmark(d);g.select();

g.removeStringBookmark(d);

setTimeout(function(){m.parentNode.removeChild(m)},0);

return a;

},

_isBlankTextNode:function(a){

if(a.nodeType==3&&a.nodeValue==""){

return true;

}return false;

},

_getNextNode:function(a,b){

if(!a||a.tagName=="BODY"){

return{elNextNode:null,bDisconnected:false}

}

if(a.nextSibling){

a=a.nextSibling;

while(a.firstChild){

if(a.tagName&&!this.rxInlineContainer.test(a.tagName)){

b=true;

}

a=a.firstChild;

}

return{elNextNode:a,bDisconnected:b}

}

return this._getNextNode(nhn.DOMFix.parentNode(a),b);

},

_getNextTextNode:function(a,b){

var c,a;

while(true){c=this._getNextNode(a,b);

a=c.elNextNode;

b=c.bDisconnected;

if(a&&a.nodeType!=3&&!this.rxInlineContainer.test(a.tagName)){

b=true;

}

if(!a||(a.nodeType==3&&!this._isBlankTextNode(a))){

break;

}

}

return{elNextText:a,bDisconnected:b};

},

_getFirstTextNode:function(){

var b=this.document.body.firstChild;

while(!!b&&b.firstChild){

b=b.firstChild;

}

if(!b){

return null;

}

if(b.nodeType!=3||this._isBlankTextNode(b)){

var a=this._getNextTextNode(b,false);

b=a.elNextText;

}

return b;

},

_addToTextMap:function(b,d,g,f){

var e=d[f].length;

for(var c=0,a=b.nodeValue.length;c<a;c++){

g[f][e+c]=[b,c];

}

d[f]+=b.nodeValue;

},

_createTextMap:function(){

var b=[];

var e=[];

var d=-1;

var a=this._getFirstTextNode();

var c={ elNextText:a,bDisconnected:true};

while(a){

if(c.bDisconnected){

d++;b[d]="";

e[d]=[];

}

this._addToTextMap(c.elNextText,b,e,d);

c=this._getNextTextNode(a,false);

a=c.elNextText}return{aTexts:b,aElTexts:e

}

},

replaceAll_js:function(t,w,s,u){

try{

var g=new Date();

var n=this._createTextMap();

var f=new Date();

var c=n.aTexts;

var k=n.aElTexts;

var h=0;

var a=t.length;

for(var r=0,l=c.length;r<l;r++){

var o=c[r];

for(var q=o.length-a;q>=0;q--){

var d=o.substring(q,q+a);

if(u&&(q>0&&o.charAt(q-1).match(/[a-zA-Z가-힣]/))){

continue;

}

if(d==t){

h++;

var b=new nhn.HuskyRange(this.window);

var p,m;

if(q+a<k[r].length){

p=k[r][q+a][0];

m=k[r][q+a][1];

}

else{

p=k[r][q+a-1][0];

m=k[r][q+a-1][1]+1}b.setEnd(p,m,true,true);

b.setStart(k[r][q][0],k[r][q][1],true);

if(typeof w=="function"){

b=w(b);

}

else{

b.pasteText(w);

}

q-=a;

}

continue;

}

}

return h;

}catch(v){

return h;

}

}

}

);