001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
// (2020.11.23, 차재복, Cha Jae Bok, http://www.ktword.co.kr) // 주어진 id의 자식 정보 가져오고, 이를 토대로 자손(ol) 생성하고, 산하 li들에 이벤트 설정 후, 이를 out에 뿌려줌 function subtree_create (id, out) { // sessionStorage에, 해당 id 존재 여부에 따라, 달리 실행 let sid = 'sid_' + id; // sessionStorage에, 해당 id 가 있으면 if (sessionStorage[sid]) { let data = JSON.parse(sessionStorage.getItem(sid)); // ol 요소 및 li 요소 생성 ol = ol_create(id, data, out); out.appendChild(ol); // sessionStorage에, 해당 id 가 없으면 } else { // 비동기 서버 fectch, 세션스토리지 store 실행 asyncFetchStore(id).then( response => { let data = JSON.parse(response); // ol 요소 및 li 요소 생성 ol = ol_create(id, data, out); out.appendChild(ol); }, error => { console.log('서버 데이터 fetct 및 sessionStorage로의 store 에러'); } ); } } // 부모 ol 산하 li들 모두에 이벤트 설정 function event4li (ol) { let len = ol.childNodes.length; for (let i = 0; i < len; i++) { event4Singleli (ol.childNodes[i]); } } // 특정 li에 만 이벤트 설정 function event4Singleli (li) { let a = li.firstElementChild; // li의 첫자식요소인 a 요소에 이벤트 설정하기 위함 // a 요소에 하부 child 없는 경우 (skip 함) if(a.dataset.child == 'none') return li; // a 요소에 하부 child 있는 경우 (이벤트 설정함) a.addEventListener('click', function(e) { e.preventDefault(); let id = e.target.id.slice(4); let li = e.target.parentNode; // 개별 li let oid = li.lastElementChild; // 개별 li 하부에 생성된 자식 ol (순서로는 마지막) if (this.innerText == '▷') { this.innerText = '▽'; // 자식 ol 생성 안되었으면 생성시킴 if (oid.nodeName != 'OL') subtree_create (id, li); // 자식 ol 이미 생성되었으면 보이게 함 else oid.style.display = 'block'; } else if (this.innerText == '▽') { this.innerText = '▷'; oid.style.display = 'none'; } }); return li; } // 요소 ol 의 생성 function ol_create (id, childrenData, out) { // childrenData : reform 테이블 레코드 집합 (data[sub_seq] : parent,id,sub_seq,title,yoyak,path2node,more_ptr,...) let ol = document.createElement('ol'); // ol에 id 및 자식 정보 저장 ol.id = 'olid_' + id; ol.dataset.data = JSON.stringify(childrenData); // ol에 style 적용 ol.style.display = 'block'; if (out.nodeName == 'LI') ol.style.paddingLeft = '25px'; else ol.style.paddingLeft = '1px'; // div 밑에 최초이므로, 좌측 padding 작게 줌 // 현재 생성된 ol 하에 li 요소들 생성 for(let index in childrenData) { // li 생성 let li = li_create(id, childrenData[index]); // http query 내 해당 id 존재하면, 펼쳐짐 const cur_id = li.dataset.id, child = li.dataset.child; if(glob_var.path2node && glob_var.path2node.includes(cur_id) && child > 0) { li.firstElementChild.innerText = '▽'; subtree_create (cur_id, li); } // li 내 a 요소에 이벤트 설정 li = event4Singleli (li); ol.appendChild(li); } return ol; } // 요소 li 의 생성 function li_create (par_id, data) { // par_id : parent id // data : reform 테이블 레코드 (parent,id,sub_seq,title,yoyak,path2node,more_ptr,...) let li = document.createElement('li'); li.id = 'lid_' + data.id; li.dataset.id = data.id; li.dataset.parent = data.parent; li.dataset.subseq = data.sub_seq; li.dataset.child = data.child; li.style = 'list-style-type: none; margin: 10px 5px;'; // navi a 요소 생성 let navi = navi_create(data.id, data.child); li.appendChild(navi); // 타이틀부 라인 span 생성 let liLineSpan = document.createElement('span'); liLineSpan.id = 'line_'+data.id; liLineSpan.setAttribute('name','line'); liLineSpan.dataset.parent = data.parent; liLineSpan.style.margin = '5px 5px 20px 25px'; liLineSpan.style.paddingBottom = '2px'; liLineSpan.style.lineHeight = '200%'; li.appendChild(liLineSpan); // 타이틀부 내 제목(title) 씀 let titleSpan = document.createElement('span'); titleSpan.id = 'title_'+data.id; titleSpan.innerHTML = data.title; liLineSpan.appendChild(titleSpan); // 타이틀부 내 제목 뒤에, 요약(yoyak) 보여주기 if (data.yoyak) { let yoyak = document.createElement('span'); yoyak.id = 'yoyak_'+data.id; yoyak.innerHTML = ' : '+data.yoyak; liLineSpan.appendChild(yoyak); } // more 상세내용 있으면 보여주기 if (data.more_type) { // line 밑 줄 liLineSpan.style.borderBottom = '1px red dotted'; // line 내 mouse cursor => pointer liLineSpan.style.cursor = 'pointer'; // line 내 dataset에 JSON 형식 저장 liLineSpan.dataset.dataObject = JSON.stringify(data); // (편집자용) title창에 호출 내역 보여주기 if (glob_var.user_type > 0) { liLineSpan.title = 'ex_run/navi_testing.js \n navi_more.js \n'+ 'more_type:'+data.more_type+'\n more_ptr:'+data.more_ptr+'\n func:'+data.func; } // eventListener 설정 liLineSpan.addEventListener("click", moreShow); // -> ex_run/navi_more.js // liLineSpan 직후 more 삽입 // moreShow(data, liLineSpan); // -> ex_run/navi_more.js } // (편집자용) editor 모드용 버튼 if (glob_var.user_type > 0 && typeof edit_btn !== 'undefined') { // edit 버튼 let editBtn = edit_btn(data); // -> reform/reform_editor.js li.appendChild(editBtn); // move 버튼 let moveBtn = move_btn(data); // -> reform/reform_edit_move.js li.appendChild(moveBtn); // gen/del 버튼 let gen_del_Btn = gen_del_btn(data); // -> reform/reform_edit_gen_del.js li.appendChild(gen_del_Btn); } return li; } // 요소 a 생성 및 이벤트처리 function navi_create (id, child) { // id : 현재 id, child : 자식 유무(=<0:없음,>0:있음) let a; // 자식 노드 있으면 if (child > 0) { a = document.createElement('a'); a.href = '?id=' + id; a.id = 'aid_' + id; a.style.fontWeight = 'bold'; a.style.color = 'red'; // *** a.style.backgroundColor = '#fbefef'; a.style.padding = '0.1em 0.3em'; // *** a.style.margin = '0.1em 0.2em'; // *** a.style.border = '1px gray solid'; // *** a.style.textDecoration = 'none'; // (편집자용) title창에 호출 내역 보여주기 if (glob_var.user_type > 0) { a.title = `navi_testing.js \n navi_create(${id},${child})`; } // 자식 노드 없으면 } else { a = document.createElement('span'); a.style.fontWeight = 'bold'; a.style.color = 'gray'; a.style.backgroundColor = '#fbefef'; a.style.textDecoration = 'none'; a.dataset.child = 'none'; } // a 링크 모양 크기 키우기 a.style.margin = "5px 5px"; a.style.border = "1px gray dotted"; a.style.padding = "0.1em 0.3em"; // a 요소에, ▷ 써넣음 let arrowText = document.createTextNode('▷'); a.appendChild(arrowText); return a; } /* // a 링크 텍스트에 target='_blank' 삽입하기 (<a href='xxx'> => <a href='xxx' target='_blank'>) function a_newTab(str) { let insert = "", insertAfter = 0; let first = str.indexOf("<a href="); let last = str.indexOf("'>", first); while (first > -1 && last > -1) { insert = " target='_blank'"; str = str.substring(0,last+1) + insert + str.substring(last+1); insertAfter = last + insert.length + 1; first = str.indexOf("<a href=", insertAfter); last = str.indexOf("'>", first); } return str; } */ // 비동기 xhr 실행으로 서버 데이터 가져오고, 이를 sessionStorage에 저장하는 비동기 처리 function asyncFetchStore(id) { return new Promise( (resolve, reject) => { let method = "get"; let url = "../reform/reform_item.php?ch=ol_v2&id=" + id; let xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { // 디버깅용 // console.log(xhr.responseText); // 서버에서 가져온 JSON 문자열을 객체화시킴 let data = JSON.parse(xhr.responseText); // console.log(data); if (data == null) return; // 각 sub_data를, 식별자별로(sid_[parent id]) sessionStorage에 저장 let current; for (let parent in data) { var sid = 'sid_' + parent; // sessionStorage 내 parent id 식별 var storage = JSON.stringify(data[parent]); sessionStorage.setItem(sid, storage); if (parent == id) current = storage; // 디버깅용 // console.log(storage); // console.log(data[parent]); } // 만일, sessionStorage에 저장 성공하면, 성공 결과(current sub data)를 리턴 if (sessionStorage[sid]) resolve(current); else reject('asyncFetchStore 실패'); } }; xhr.send(); }); } // 비동기 xhr 실행으로 서버 데이터 가져오고, 이를 sessionStorage에 저장하는 비동기 처리 ver.2 function asyncFetchStore_v2(id, parms) { return new Promise( (resolve, reject) => { let method = "post"; let url = "../reform/reform_item.php"; let sendData = ""; if (parms) { sendData = "ch="+parms.ch+"&id_list="+parms.id_list; } else { sendData = "id=" + id + "&ch=ol_v2"; } let xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { // 디버깅용 // console.log(xhr.responseText); // 서버에서 가져온 JSON 문자열을 객체화시킴 let data = JSON.parse(xhr.responseText); if (data == null) return; // 각 sub_data를, 식별자별로(sid_[parent id]) sessionStorage에 저장 let current; for (let parent in data) { var sid = 'sid_' + parent; // sessionStorage 내 parent id 식별 var storage = JSON.stringify(data[parent]); sessionStorage.setItem(sid, storage); if (parent == id) current = storage; // 디버깅용 // console.log(storage); // console.log(data[parent]); } // 만일, sessionStorage에 저장 성공하면, 성공 결과(current sub data)를 리턴 if (sessionStorage[sid]) resolve(current); else reject('asyncFetchStore 실패'); } }; xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send(sendData); }); }