"DigitalSinology(2026) NetworkGraph 02.lst"의 두 판 사이의 차이
CNUDH
JSH CNUGrad (토론 | 기여) |
JSH CNUGrad (토론 | 기여) |
||
| 1번째 줄: | 1번째 줄: | ||
| − | < | + | <html> |
| + | <head> | ||
| + | <meta charset="UTF-8"> | ||
| + | <style> | ||
| + | body{ | ||
| + | font-family:Arial,sans-serif; | ||
| + | background:#fdfdf9; | ||
| + | text-align:center; | ||
| + | padding:30px; | ||
| + | } | ||
| + | |||
| + | #visualization{ | ||
| + | width:100%; | ||
| + | max-width:1400px; | ||
| + | margin:auto; | ||
| + | } | ||
| + | |||
| + | .row{ | ||
| + | display:flex; | ||
| + | align-items:center; | ||
| + | margin:18px 0; | ||
| + | } | ||
| + | |||
| + | .chapter{ | ||
| + | width:60px; | ||
| + | text-align:right; | ||
| + | padding-right:15px; | ||
| + | font-weight:bold; | ||
| + | } | ||
| + | |||
| + | .timeline{ | ||
| + | flex:1; | ||
| + | position:relative; | ||
| + | height:25px; | ||
| + | border-top:2px solid #b5b5b5; | ||
| + | } | ||
| + | |||
| + | .tick-gray{ | ||
| + | position:absolute; | ||
| + | width:1px; | ||
| + | height:7px; | ||
| + | top:0; | ||
| + | background:#999; | ||
| + | cursor:pointer; | ||
| + | } | ||
| − | + | .tick-gray:hover { | |
| − | + | background: #555; | |
| + | width: 2px; | ||
| + | z-index: 5; | ||
| + | } | ||
| − | + | .tick-color{ | |
| + | position:absolute; | ||
| + | width:2px; | ||
| + | height:14px; | ||
| + | top:-12px; | ||
| + | cursor:pointer; | ||
| + | z-index: 10; | ||
| + | } | ||
| + | |||
| + | .tick-color:hover { | ||
| + | width: 4px; | ||
| + | z-index: 15; | ||
| + | } | ||
| + | |||
| + | .count{ | ||
| + | width:80px; | ||
| + | color:#555; | ||
| + | font-weight:bold; | ||
| + | padding-left:10px; | ||
| + | text-align:right; | ||
| + | } | ||
| + | |||
| + | .count .match{ | ||
| + | color:#e74c3c; | ||
| + | } | ||
| − | + | .che{background:#e74c3c;} | |
| − | + | .cheRen{background:#3498db;} | |
| − | + | .chePlace{background:#2ecc71;} | |
| + | .cheMoney{background:#f39c12;} | ||
| − | + | #legend{ | |
| + | margin:25px 0; | ||
| + | } | ||
| − | + | .legend-item{ | |
| + | margin:0 15px; | ||
| + | } | ||
| − | + | .box{ | |
| − | + | width:14px; | |
| + | height:14px; | ||
| + | display:inline-block; | ||
| + | margin-right:5px; | ||
| + | vertical-align:middle; | ||
| + | } | ||
| − | + | /* 툴팁 Z-index 극대화 */ | |
| + | #tooltip { | ||
| + | position: absolute; | ||
| + | display: none; | ||
| + | background: rgba(0, 0, 0, 0.85); | ||
| + | color: #fff; | ||
| + | padding: 15px; | ||
| + | border-radius: 6px; | ||
| + | font-size: 13px; | ||
| + | text-align: left; | ||
| + | width: max-content; | ||
| + | max-width: 500px; | ||
| + | line-height: 1.5; | ||
| + | box-shadow: 0 4px 8px rgba(0,0,0,0.4); | ||
| + | z-index: 999999 !important; | ||
| + | pointer-events: none; | ||
| + | word-break: keep-all; | ||
| + | box-sizing: border-box; | ||
| + | } | ||
| − | + | #tooltip strong { | |
| + | color: #ffd700; | ||
| + | } | ||
| − | + | .tooltip-sentence-info { | |
| − | + | color: #a8d8ea; | |
| − | < | + | font-weight: bold; |
| + | margin-bottom: 8px; | ||
| + | display: inline-block; | ||
| + | } | ||
| + | </style> | ||
| + | </head> | ||
| + | <body> | ||
| − | + | <h2>駱駝祥子 인력거(車) 관련 어휘 공간적 분포</h2> | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | < | ||
| − | == | + | <div id="legend"> |
| − | <span | + | <span class="legend-item"> |
| − | < | + | <span class="box che"></span> che |
| − | < | + | </span> |
| − | < | + | <span class="legend-item"> |
| − | + | <span class="box cheRen"></span> cheRen | |
| − | < | + | </span> |
| − | < | + | <span class="legend-item"> |
| − | + | <span class="box chePlace"></span> chePlace | |
| − | + | </span> | |
| − | + | <span class="legend-item"> | |
| − | + | <span class="box cheMoney"></span> cheMoney | |
| + | </span> | ||
</div> | </div> | ||
| − | |||
| − | |||
| − | |||
| − | < | + | <div id="visualization"></div> |
| − | < | + | |
| − | + | <div id="tooltip"></div> | |
| − | < | + | |
| + | <textarea id="xmlDataBlock" style="display:none;"> | ||
| + | |||
| + | </textarea> | ||
| + | |||
| + | <script> | ||
| + | window.addEventListener("DOMContentLoaded", function() { | ||
| + | // 1. 미디어위키 레이아웃 제약을 벗어나기 위해 툴팁을 body 최상단으로 강제 이동 | ||
| + | var tooltip = document.getElementById("tooltip"); | ||
| + | if (tooltip && tooltip.parentNode !== document.body) { | ||
| + | document.body.appendChild(tooltip); | ||
| + | } | ||
| + | |||
| + | // 2. textarea에서 XML 문자열을 안전하게 추출 | ||
| + | var xmlString = document.getElementById("xmlDataBlock").value.trim(); | ||
| + | |||
| + | if(xmlString === "") { | ||
| + | alert("HTML 코드 내의 <textarea id='xmlDataBlock'> 영역에 XML 데이터를 붙여넣어 주세요."); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | var parser = new DOMParser(); | ||
| + | var xmlDoc = parser.parseFromString(xmlString, "text/xml"); | ||
| + | |||
| + | if(xmlDoc.getElementsByTagName("parsererror").length > 0){ | ||
| + | alert("XML 데이터 구조에 오류가 있습니다. 원본 XML 파일의 내용을 다시 확인해 주세요."); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | draw(xmlDoc, tooltip); | ||
| + | }); | ||
| + | |||
| + | function draw(xmlDoc, tooltip){ | ||
| + | var container = document.getElementById("visualization"); | ||
| + | container.innerHTML = ""; | ||
| + | |||
| + | var chapters = xmlDoc.getElementsByTagName("Chapter"); | ||
| + | |||
| + | var globalTotalSentences = 0; | ||
| + | var globalTotalMatches = 0; | ||
| + | |||
| + | for(var i=0; i<chapters.length; i++){ | ||
| + | var chapter = chapters[i]; | ||
| + | var sentences = chapter.getElementsByTagName("Sentence"); | ||
| + | |||
| + | if(sentences.length === 0) continue; | ||
| + | |||
| + | globalTotalSentences += sentences.length; | ||
| − | + | var row = document.createElement("div"); | |
| − | + | row.className = "row"; | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | var label = document.createElement("div"); | |
| − | + | label.className = "chapter"; | |
| − | + | label.innerHTML = (i+1) + "장"; | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | === | + | var timeline = document.createElement("div"); |
| − | + | timeline.className = "timeline"; | |
| − | ===='' | + | |
| − | + | var count = 0; | |
| − | + | ||
| − | : | + | var moveHandler = function(e) { |
| − | + | var tooltipWidth = tooltip.offsetWidth; | |
| − | </ | + | var windowWidth = window.innerWidth; |
| + | var x = e.pageX; | ||
| + | var y = e.pageY; | ||
| + | |||
| + | if (x > windowWidth / 2) { | ||
| + | tooltip.style.left = (x - tooltipWidth - 20) + "px"; | ||
| + | } else { | ||
| + | tooltip.style.left = (x + 20) + "px"; | ||
| + | } | ||
| + | tooltip.style.top = (y + 15) + "px"; | ||
| + | }; | ||
| + | |||
| + | for(var s=0; s<sentences.length; s++){ | ||
| + | var sentence = sentences[s]; | ||
| + | var left = (s/(sentences.length-1))*100; | ||
| + | if(sentences.length === 1) left = 0; | ||
| + | |||
| + | var paragraph = sentence.parentNode; | ||
| + | var pId = paragraph && paragraph.tagName === "Paragraph" ? paragraph.getAttribute("id") : "정보 없음"; | ||
| + | var pTitle = paragraph && paragraph.tagName === "Paragraph" ? paragraph.getAttribute("title") : "단락 정보 없음"; | ||
| + | var sTitle = sentence.getAttribute("title") || "문장 정보 없음"; | ||
| + | |||
| + | var textOrElem = sentence.getElementsByTagName("TextOr")[0]; | ||
| + | var textTrElem = sentence.getElementsByTagName("TextTr")[0]; | ||
| + | |||
| + | var textOr = textOrElem ? textOrElem.textContent : "원문 데이터가 없습니다."; | ||
| + | var textTr = textTrElem ? textTrElem.textContent : "번역 데이터가 없습니다."; | ||
| + | |||
| + | var tooltipHtml = "<strong>[" + pTitle + "]</strong> (" + pId + ")<br>" + | ||
| + | "<span class='tooltip-sentence-info'>문장: " + sTitle + "</span><br><br>" + | ||
| + | "<strong>원문:</strong> " + textOr + "<br><br>" + | ||
| + | "<strong>번역:</strong> " + textTr; | ||
| + | |||
| + | var gray = document.createElement("div"); | ||
| + | gray.className = "tick-gray"; | ||
| + | gray.style.left = left + "%"; | ||
| + | |||
| + | gray.addEventListener("mouseover", (function(html) { | ||
| + | return function(e) { | ||
| + | tooltip.style.display = "block"; | ||
| + | tooltip.innerHTML = html; | ||
| + | }; | ||
| + | })(tooltipHtml)); | ||
| + | |||
| + | gray.addEventListener("mousemove", moveHandler); | ||
| + | |||
| + | gray.addEventListener("mouseout", function() { | ||
| + | tooltip.style.display = "none"; | ||
| + | }); | ||
| + | |||
| + | timeline.appendChild(gray); | ||
| + | |||
| + | var tagType = null; | ||
| + | |||
| + | if(sentence.getElementsByTagName("che").length > 0) tagType = "che"; | ||
| + | if(sentence.getElementsByTagName("cheRen").length > 0) tagType = "cheRen"; | ||
| + | if(sentence.getElementsByTagName("chePlace").length > 0) tagType = "chePlace"; | ||
| + | if(sentence.getElementsByTagName("cheMoney").length > 0) tagType = "cheMoney"; | ||
| + | |||
| + | if(tagType){ | ||
| + | count++; | ||
| + | |||
| + | var tick = document.createElement("div"); | ||
| + | tick.className = "tick-color " + tagType; | ||
| + | tick.style.left = left + "%"; | ||
| + | |||
| + | var colorTooltipHtml = "<strong>[" + pTitle + "]</strong> (" + pId + ") - <span style='color:#ff7f0e;'>발견태그: <" + tagType + "></span><br>" + | ||
| + | "<span class='tooltip-sentence-info'>문장: " + sTitle + "</span><br><br>" + | ||
| + | "<strong>원문:</strong> " + textOr + "<br><br>" + | ||
| + | "<strong>번역:</strong> " + textTr; | ||
| + | |||
| + | tick.addEventListener("mouseover", (function(html) { | ||
| + | return function(e) { | ||
| + | tooltip.style.display = "block"; | ||
| + | tooltip.innerHTML = html; | ||
| + | }; | ||
| + | })(colorTooltipHtml)); | ||
| + | |||
| + | tick.addEventListener("mousemove", moveHandler); | ||
| + | |||
| + | tick.addEventListener("mouseout", function() { | ||
| + | tooltip.style.display = "none"; | ||
| + | }); | ||
| + | |||
| + | timeline.appendChild(tick); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | globalTotalMatches += count; | ||
| − | + | var countDiv = document.createElement("div"); | |
| − | + | countDiv.className = "count"; | |
| − | + | countDiv.innerHTML = "<span class='match'>" + count + "</span> / " + sentences.length; | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | row.appendChild(label); | |
| − | + | row.appendChild(timeline); | |
| − | + | row.appendChild(countDiv); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| + | container.appendChild(row); | ||
| + | } | ||
| − | + | if (globalTotalSentences > 0) { | |
| − | + | var ratio = ((globalTotalMatches / globalTotalSentences) * 100).toFixed(2); | |
| − | + | ||
| − | = | + | var ratioDiv = document.createElement("div"); |
| − | + | ratioDiv.style.marginTop = "40px"; | |
| − | + | ratioDiv.style.fontSize = "28px"; | |
| − | + | ratioDiv.style.fontWeight = "bold"; | |
| − | + | ratioDiv.style.color = "#e74c3c"; | |
| − | + | ratioDiv.innerHTML = ratio + "%"; | |
| − | + | ||
| − | + | container.appendChild(ratioDiv); | |
| − | + | } | |
| − | + | } | |
| − | + | </script> | |
| − | + | </body> | |
| − | + | </html> | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | </ | ||
| − | </ | ||
2026년 6월 18일 (목) 15:19 판
駱駝祥子 인력거(車) 관련 어휘 공간적 분포
che
cheRen
chePlace
cheMoney