fix: 비주얼 매퍼 JS 문법 오류 수정
- iframe 스크립트를 template 태그 방식으로 변경 (EJS 백틱 이스케이프 충돌 해결) - Mixed Content 치환을 단순 문자열 replace로 변경 - regex 이스케이프 문제 수정
This commit is contained in:
+70
-65
@@ -109,6 +109,66 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script id="mapper-inject-script" type="text/template">
|
||||
(function(){
|
||||
var highlight = null;
|
||||
var overlay = document.createElement("div");
|
||||
overlay.id = "__mapper_overlay__";
|
||||
overlay.style.cssText = "position:fixed;pointer-events:none;border:3px solid #6366f1;background:rgba(99,102,241,.12);z-index:999999;transition:all .1s;display:none;border-radius:4px";
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
var label = document.createElement("div");
|
||||
label.style.cssText = "position:fixed;z-index:999999;background:#6366f1;color:#fff;font-size:11px;padding:2px 8px;border-radius:4px;pointer-events:none;display:none;font-family:monospace";
|
||||
document.body.appendChild(label);
|
||||
|
||||
document.addEventListener("mousemove", function(e) {
|
||||
var el = e.target;
|
||||
if (el.id === "__mapper_overlay__" || el === label) return;
|
||||
highlight = el;
|
||||
var r = el.getBoundingClientRect();
|
||||
overlay.style.display = "block";
|
||||
overlay.style.left = r.left + "px";
|
||||
overlay.style.top = r.top + "px";
|
||||
overlay.style.width = r.width + "px";
|
||||
overlay.style.height = r.height + "px";
|
||||
label.style.display = "block";
|
||||
label.style.left = r.left + "px";
|
||||
label.style.top = Math.max(0, r.top - 22) + "px";
|
||||
label.textContent = getSelector(el);
|
||||
});
|
||||
|
||||
document.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!highlight) return;
|
||||
var sel = getSelector(highlight);
|
||||
var text = (highlight.textContent || "").trim().substring(0, 80);
|
||||
var tag = highlight.tagName.toLowerCase();
|
||||
var href = highlight.getAttribute("href") || "";
|
||||
window.parent.postMessage({type:"element-selected", selector:sel, text:text, tag:tag, href:href}, "*");
|
||||
}, true);
|
||||
|
||||
function getSelector(el) {
|
||||
if (el.id && el.id.indexOf("__") !== 0) return "#" + el.id;
|
||||
var path = [];
|
||||
while (el && el.nodeType === 1) {
|
||||
var s = el.tagName.toLowerCase();
|
||||
if (el.id && el.id.indexOf("__") !== 0) { path.unshift("#" + el.id); break; }
|
||||
if (el.className && typeof el.className === "string") {
|
||||
var cls = el.className.trim().split(" ").filter(function(c){ return c && c.indexOf("__") !== 0 && c.length < 40; }).slice(0, 2);
|
||||
if (cls.length) s += "." + cls.join(".");
|
||||
}
|
||||
var sib = el.parentNode ? Array.from(el.parentNode.children).filter(function(c){ return c.tagName === el.tagName; }) : [];
|
||||
if (sib.length > 1) { s += ":nth-child(" + (Array.from(el.parentNode.children).indexOf(el) + 1) + ")"; }
|
||||
path.unshift(s);
|
||||
el = el.parentNode;
|
||||
if (path.length > 4) break;
|
||||
}
|
||||
return path.join(" > ");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var adsenseList = (__INIT__ || {}).adsense || [];
|
||||
var dataType = null;
|
||||
@@ -150,13 +210,15 @@ async function fetchPage() {
|
||||
var baseTag = '<base href="' + baseUrl.origin + '/">';
|
||||
html = html.replace(/<head([^>]*)>/i, '<head$1>' + baseTag);
|
||||
|
||||
// Mixed Content 방지: http → https 변환 (리소스 URL만)
|
||||
html = html.replace(/(src|href|action)=(["'])http:\/\//gi, '$1=$2https://');
|
||||
html = html.replace(/url\((['"]?)http:\/\//gi, 'url($1https://');
|
||||
// Mixed Content 방지: http → https 변환
|
||||
while (html.indexOf('src="http://') !== -1) html = html.replace('src="http://', 'src="https://');
|
||||
while (html.indexOf("src='http://") !== -1) html = html.replace("src='http://", "src='https://");
|
||||
while (html.indexOf('href="http://') !== -1) html = html.replace('href="http://', 'href="https://');
|
||||
while (html.indexOf("href='http://") !== -1) html = html.replace("href='http://", "href='https://");
|
||||
|
||||
// iframe에 매퍼 스크립트 주입
|
||||
var mapperScript = getMapperScript();
|
||||
html = html.replace(/<\\/body>/i, mapperScript + '</body>');
|
||||
html = html.replace(new RegExp('</' + 'body>', 'i'), mapperScript + '</' + 'body>');
|
||||
|
||||
frame.srcdoc = html;
|
||||
pageLoaded = true;
|
||||
@@ -169,67 +231,10 @@ async function fetchPage() {
|
||||
btn.disabled = false; btn.textContent = '페이지 가져오기';
|
||||
}
|
||||
|
||||
// === iframe 내부에 주입할 스크립트 ===
|
||||
// === iframe 내부에 주입할 스크립트 (template 태그에서 읽기) ===
|
||||
function getMapperScript() {
|
||||
return '<scr' + 'ipt>' +
|
||||
'(function(){' +
|
||||
' var highlight = null;' +
|
||||
' var overlay = document.createElement("div");' +
|
||||
' overlay.id = "__mapper_overlay__";' +
|
||||
' overlay.style.cssText = "position:fixed;pointer-events:none;border:3px solid #6366f1;background:rgba(99,102,241,.12);z-index:999999;transition:all .1s;display:none;border-radius:4px";' +
|
||||
' document.body.appendChild(overlay);' +
|
||||
'' +
|
||||
' var label = document.createElement("div");' +
|
||||
' label.style.cssText = "position:fixed;z-index:999999;background:#6366f1;color:#fff;font-size:11px;padding:2px 8px;border-radius:4px;pointer-events:none;display:none;font-family:monospace";' +
|
||||
' document.body.appendChild(label);' +
|
||||
'' +
|
||||
' document.addEventListener("mousemove", function(e) {' +
|
||||
' var el = e.target;' +
|
||||
' if (el.id === "__mapper_overlay__" || el === label) return;' +
|
||||
' highlight = el;' +
|
||||
' var r = el.getBoundingClientRect();' +
|
||||
' overlay.style.display = "block";' +
|
||||
' overlay.style.left = r.left + "px";' +
|
||||
' overlay.style.top = r.top + "px";' +
|
||||
' overlay.style.width = r.width + "px";' +
|
||||
' overlay.style.height = r.height + "px";' +
|
||||
' label.style.display = "block";' +
|
||||
' label.style.left = r.left + "px";' +
|
||||
' label.style.top = Math.max(0, r.top - 22) + "px";' +
|
||||
' label.textContent = getSelector(el);' +
|
||||
' });' +
|
||||
'' +
|
||||
' document.addEventListener("click", function(e) {' +
|
||||
' e.preventDefault();' +
|
||||
' e.stopPropagation();' +
|
||||
' if (!highlight) return;' +
|
||||
' var sel = getSelector(highlight);' +
|
||||
' var text = (highlight.textContent || "").trim().substring(0, 80);' +
|
||||
' var tag = highlight.tagName.toLowerCase();' +
|
||||
' var href = highlight.getAttribute("href") || "";' +
|
||||
' window.parent.postMessage({type:"element-selected", selector:sel, text:text, tag:tag, href:href, outerHTML: highlight.outerHTML.substring(0,300)}, "*");' +
|
||||
' }, true);' +
|
||||
'' +
|
||||
' function getSelector(el) {' +
|
||||
' if (el.id && !/^__/.test(el.id)) return "#" + el.id;' +
|
||||
' var path = [];' +
|
||||
' while (el && el.nodeType === 1) {' +
|
||||
' var sel = el.tagName.toLowerCase();' +
|
||||
' if (el.id && !/^__/.test(el.id)) { path.unshift("#" + el.id); break; }' +
|
||||
' if (el.className && typeof el.className === "string") {' +
|
||||
' var cls = el.className.trim().split(/\\s+/).filter(function(c){return c && !/^__/.test(c) && c.length < 40}).slice(0, 2);' +
|
||||
' if (cls.length) sel += "." + cls.join(".");' +
|
||||
' }' +
|
||||
' var sib = el.parentNode ? Array.from(el.parentNode.children).filter(function(c){return c.tagName===el.tagName}) : [];' +
|
||||
' if (sib.length > 1) { sel += ":nth-child(" + (Array.from(el.parentNode.children).indexOf(el) + 1) + ")"; }' +
|
||||
' path.unshift(sel);' +
|
||||
' el = el.parentNode;' +
|
||||
' if (path.length > 4) break;' +
|
||||
' }' +
|
||||
' return path.join(" > ");' +
|
||||
' }' +
|
||||
'})();' +
|
||||
'</scr' + 'ipt>';
|
||||
var code = document.getElementById('mapper-inject-script').textContent;
|
||||
return '<scr' + 'ipt>' + code + '</scr' + 'ipt>';
|
||||
}
|
||||
|
||||
// === 데이터 타입 선택 ===
|
||||
@@ -315,7 +320,7 @@ function updateJson() {
|
||||
// 컨테이너 기준 상대 셀렉터로 변환
|
||||
var sel = mappings[f].selector;
|
||||
if (containerSelector && sel.indexOf(containerSelector) === 0) {
|
||||
sel = sel.substring(containerSelector.length).replace(/^\\s*>\\s*/, '');
|
||||
sel = sel.substring(containerSelector.length).replace(/^\s*>\s*/, '');
|
||||
}
|
||||
rules.fields[f] = { selector: sel || mappings[f].selector, type: mappings[f].type || 'text' };
|
||||
if (mappings[f].attr) rules.fields[f].attr = mappings[f].attr;
|
||||
|
||||
Reference in New Issue
Block a user