// (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);
});
}
"본 웹사이트 내 모든 저작물은 원출처를 밝히는 한 자유롭게 사용(상업화포함) 가능합니다"