001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
// (2022.9.23, 차재복, Cha Jae Bok, http://www.ktword.co.kr) // 호출 : [navi_base.js] li_create() function detailSeparateView(e) { e.preventDefault(); // bullet let bullet = this.querySelector("a[class='bullet']"); if (bullet.innerText == '▷') bullet.innerText = '▽'; else if (bullet.innerText == '▽') bullet.innerText = '▷'; // div let out = this.parentNode.querySelector("div[class='detail_view']"); if (out && out.childElementCount > 0) { itemShowHide(out, null); // common_utils.js return; } // ajax promise 호출 (서버로부터 데이터 가져오기) const no = this.dataset.no; const choice = 'ajaxFetchDetailSeparate'; let url = '/test/navigation/naviFetch.php?choice='+choice+'&no=' + no; let method = 'get'; ajaxPromise(url, method) // common_utils.js // 가져온 결과에 대한 데이터 가공 .then( response => { // (개발,디버깅) // console.log(response.data); if(response.err_msg) { alert(response.err_msg); console.log(url); } displayPerSection(out, response); }, error => { alert(error); } ); } // 기능 : 화면(out)에 각 섹션(1.,2.,...)별로 div 생성 및 그 안에 서버응답 내용을 보여주기 // 호출 : [navi_base_edit.js] editDivHandle(li,response) function displayPerSection(out, responseAll) { // out div show out.style.display = 'block'; const response = responseAll.separated; const name = responseAll.name; const fullname = responseAll.fullname; const date = responseAll.date; // button 생성 const length = Object.keys(response).length; createSectionbuttons(out, response); // navi_base_moreShow.js // 맨끝 button 대신 본문 링크 const span = document.createElement('span'); span.style.marginLeft = '25px'; const li = out.closest('li'); // const title = li.querySelector('.title').innerText; span.innerHTML = "<a href='/test/view/view.php?no="+li.dataset.no+"' target='_blank' title='" + fullname +"'>" + name + "</a>" + " (" + date + ")"; out.appendChild(span); // 개별 section별 div 생성 for(i=0; i<length; i++) { // div 생성 및 스타일링 const div = document.createElement('div'); // div 식별자 div.setAttribute('class','secDiv'); div.dataset.dno = i; // div 스타일링 div.style.marginLeft = '5px'; div.style.border = '1px solid gray'; div.style.paddingLeft = '10px'; // 첫 section div 외 모두 보이지 않기 (hide) if(i != 0) div.style.display = 'none'; // 서버 응답을 div에 출력 div.innerHTML = "<pre style='font-size:13px; line-height:170%'>" + response[i] + "</pre>"; // div 출력 중 a 요소들에 스타일링 div.querySelectorAll('a').forEach( elem => { elem.style.textDecoration = 'none' }); // 수식 mathjax 있으면, mathjax Typeset 설정 const startDisp = response[i].indexOf('[#'), endDisp = response[i].indexOf('#]',startDisp+1), startInline = response[i].indexOf('{#'), endInline = response[i].indexOf('#}',startInline+1); if ( (startDisp > -1 && endDisp > -1) || (startInline > -1 && endInline > -1) ) { MathJax.Hub.Queue(["Typeset",MathJax.Hub,div]); // mathjax_config.php } // out에 div 자식 추가 out.appendChild(div); } } // 호출 : [navi_base.js] li_create() function detailSourceView(span) { // 현재 부모 li, 소스 출력할 div, bullet(▽/▷) a 취득 const li = span.closest('li'); const out = li.querySelector('.detail_view'); const bullet = li.querySelector('a.bullet'); // div 산하 자식 여부에 따라, show/hide 및 ▽/▷ 결정 if(out.childNodes.length > 0) { if (out.style.display == "none") { out.style.display = "block"; bullet.innerText = '▽'; } else { out.style.display = "none"; bullet.innerText = '▷'; } return; } else { out.style.display = "block"; bullet.innerText = '▽'; } // ajax promise 호출 (서버로부터 데이터 가져오기) // const id = e.target.closest('li').dataset.id; const id = li.dataset.id; const choice = 'ajaxFetchSourceView'; const url = '/test/navigation/naviFetch.php?choice='+choice+'&id=' + id; const method = 'get'; return ajaxPromise(url, method) // common_utils.js // 가져온 결과에 대한 데이터 가공 .then( response => { // (개발,디버깅) if(response.err_msg) { alert(response.err_msg); console.log(url); } //console.log(response); if(!response.data.code) { alert('서버로부터 빈 소스 파일 만 도착함'); return; } // 상,하 div 생성 const highDiv = document.createElement('div'); highDiv.style.display = 'flex'; highDiv.style.width = '100%'; const lowDiv = document.createElement('div'); lowDiv.style.display = 'flex'; lowDiv.style.width = '100%'; out.appendChild(highDiv); out.appendChild(lowDiv); // 소스 배열로 가공 const rawStr = response.data.code; const splitedSrcArr = rawStr.split(/\r\n|\n/g); // 줄 수, 몫, 나머지 계산 const N = 30; // 30줄씩 끊어서 보여주기 const lineLength = splitedSrcArr.length // rawStr.match(/\n/g).length; const quotient = parseInt(lineLength / N); // 몫 const remainder = lineLength % N; // 나머지 // 줄번호 배열로 가공 const splitedNumArr = Array.from( { length : lineLength }, (_, index) => index + 1); // section별 button 생성 const objData = {}; let i; for(i=0; i<quotient; i++) objData[i] = (N*i + 1) + '~' + (N*i + N); if(i==quotient && remainder>0) objData[i] = (N*i + 1) + '~' + (N*i + (i<quotient ? N : remainder)); createSectionbuttons(highDiv, objData); // navi_base_moreShow.js // section button 이후 파일명 const fileNameSpan = document.createElement('span'); fileNameSpan.style.marginLeft = '20px'; fileNameSpan.innerText = response.data.file; /* fileNameSpan.innerHTML = "<a href='/test/open_src/view_src.php?dir="+response.data.dir +"&file="+response.data.file+"' target='_blank'>" +response.data.file+"</a>"; */ highDiv.appendChild(fileNameSpan); // section button 이후 날짜 const dateSpan = document.createElement('span'); dateSpan.style.marginLeft = '15px'; dateSpan.innerText = '('+response.data.dateInTbl+')'; highDiv.appendChild(dateSpan); if(response.data.dateDiff < 0) { const delayDateSpan = document.createElement('span'); highDiv.appendChild(delayDateSpan); delayDateSpan.style.color = 'red'; delayDateSpan.style.marginLeft = '10px'; if(glob_var.user_type != '종합관리자') { // delayDateSpan.innerText = '( 수정중 )'; } else { delayDateSpan.style.cursor = 'pointer'; delayDateSpan.innerText = ': 구 버젼 '; delayDateSpan.addEventListener('click', () => { const updateChoice = 'sourceFileUpdate'; const updateUrl = '/test/navigation/naviUpdate.php?choice='+updateChoice+'&id='+id; const updateMethod = 'get'; ajaxPromise(updateUrl, updateMethod) .then( response => { if(response.affected) { alert('파일 정보 최신으로 업데이트됨'); // out(detail_view) 산하 자식 노드 제거 while (out.hasChildNodes()) out.removeChild(out.firstChild); // collapseSpan 클릭 후 처럼, detail_view에 source 내용 다시 보이기 const collapseSpan = li.querySelector('span.collapse'); detailSourceView(collapseSpan); } }, error => { alert(error); } ); }); } } // 개별 section별 div 생성 for(let i=0; i<=quotient; i++) { // section별 div 생성 및 스타일링 const secDiv = document.createElement('div'); // secDiv 식별자 secDiv.setAttribute('class','secDiv'); secDiv.dataset.dno = i; // secDiv 스타일링 secDiv.style.margin = '5px'; secDiv.style.border = '1px solid gray'; // secDiv.style.padding = '0px 10px'; // (Default) 첫 section div 외 모두 보이지 않기 (hide) if(i != 0) secDiv.style.display = 'none'; else secDiv.style.display = 'flex'; lowDiv.appendChild(secDiv); // lowDiv에 소스 번호 div (left),내용 출력용 div (right) 생성 및 스타일링 const lNumDiv = document.createElement('div'); lNumDiv.style.width = '15x'; // '30px'; lNumDiv.style.borderRight = '1px red solid'; const rSrcDiv = document.createElement('div'); rSrcDiv.style.width = '85%'; // '650px'; // rSrcDiv.style.overflowX = 'scroll'; [lNumDiv,rSrcDiv].map( elem => { elem.style.display = 'block'; elem.style.float = 'left'; elem.style.boxSizing = 'border-box'; elem.style.whiteSpace = 'pre'; elem.style.padding = '15px 5px 15px 10px'; elem.style.lineHeight = '15px'; // 소스 코드이므로, 줄 간격 일정 필요 elem.style.fontFamily = 'Consolas,monospace'; // 소스 코드이므로, 이에 적합한 폰트 필요 }); secDiv.appendChild(lNumDiv); secDiv.appendChild(rSrcDiv); // 줄번호 및 소스 div에 출력 lNumDiv.textContent = splitedNumArr.slice(N*i,N*i+(i<quotient ? N : remainder)).join('\n'); rSrcDiv.textContent = splitedSrcArr.slice(N*i,N*i+(i<quotient ? N : remainder)).join('\n'); } }, error => { alert(error); } ); } // 기능 : 섹션별 버튼 생성 // 호출 : [navi_base_moreShow.js] displayPerSection(), detailSourceView() function createSectionbuttons(out, objData) { // button const length = Object.keys(objData).length; for(i=0; i<length; i++) { const button = document.createElement('button'); // section button 식별자 button.setAttribute('class','secBtn'); button.dataset.bno = i; // button에 스타일링 button.style.fontSize = '13px'; button.style.marginLeft = '3px'; // (Default) 초기 버튼 텍스트 강조 if(i == 0) button.style.fontWeight = 'bold'; // button 내 섹션별 제목 텍스트 삽입 let striped = stripHtml(objData[i]).trim(); let cutLen = striped.indexOf('\n') cutLen = cutLen > 0 ? cutLen : 20; striped = striped.substr(0, cutLen); button.innerText = cutStr4Hangul(striped, 0, 20).trim() + ' ... '; // button 리스너 처리기 button.addEventListener('click', (e) => { out.querySelectorAll('button').forEach( elem => { elem.style.fontWeight = 'normal'; }); e.target.style.fontWeight = 'bold'; let divs = out.closest('div.detail_view').querySelectorAll('div.secDiv'); for(i=0;i<divs.length;i++){ if(divs[i].dataset.dno == e.target.dataset.bno) { divs[i].style.display = 'flex'; } else { divs[i].style.display = 'none'; } } }); out.appendChild(button); } } // html 텍스트 내 html 태그들 만 제거 function stripHtml(html) { const tmp = document.createElement("div"); tmp.innerHTML = html; return tmp.innerText; }