1/* RARA Naver map renderer shared by write, published-post and member-list pages. */ 2(function(window, document){ 3 'use strict'; 4 5 /* ì§ìê²ì API í¤ì NAVER Cloud Maps í¤ë ìë¡ ë¤ë¥¸ ì격ì¦ëª ì ëë¤. */ 6 var MAP_KEY = String(window.raraNaverMapClientId || '').replace(/^\s+|\s+$/g, ''); 7 var SDK_URL = MAP_KEY ? ('https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=' + encodeURIComponent(MAP_KEY) + '&submodules=geocoder') : ''; 8 var DEFAULT_PLACE = { 9 name: 'ë¤ì´ë² 1784', 10 query: 'ë¤ì´ë² 1784', 11 roadAddress: '경기ë ì±ë¨ì ë¶ë¹êµ¬ ì ìì¼ë¡ 95', 12 address: '경기ë ì±ë¨ì ë¶ë¹êµ¬ ì ìë 178-1', 13 x: '127.1054328', 14 y: '37.3595963', 15 mapUrl: 'https://map.naver.com/p/search/' + encodeURIComponent('ë¤ì´ë² 1784') 16 }; 17 var sdkPromise = null; 18 var observer = null; 19 var PUBLISHED_CONTENT_SELECTOR = '#bo_v_con, .member-view-content, .member-notice-content'; 20 21 function toText(value){ 22 return String(value == null ? '' : value).replace(/\s+/g, ' ').trim(); 23 } 24 25 function escapeHtml(value){ 26 return String(value == null ? '' : value) 27 .replace(/&/g, '&') 28 .replace(/</g, '<') 29 .replace(/>/g, '>') 30 .replace(/"/g, '"') 31 .replace(/'/g, '''); 32 } 33 34 function normalizeCoord(x, y){ 35 var lng = parseFloat(String(x || '').replace(/,/g, '')); 36 var lat = parseFloat(String(y || '').replace(/,/g, '')); 37 if(!isFinite(lng) || !isFinite(lat)) return null; 38 if(Math.abs(lng) > 180) lng = lng / 10000000; 39 if(Math.abs(lat) > 90) lat = lat / 10000000; 40 if(lng < 120 || lng > 135 || lat < 30 || lat > 45) return null; 41 return {lat:lat, lng:lng}; 42 } 43 44 function placeEndpoint(){ 45 if(window.raraJoditPlaceSearchUrl) return window.raraJoditPlaceSearchUrl; 46 var imageProxy = window.raraJoditImageProxyUrl || ''; 47 if(imageProxy && imageProxy.indexOf('image_proxy.php') !== -1){ 48 return imageProxy.replace('image_proxy.php', 'naver_place_search.php'); 49 } 50 return '/plugin/editor/jodit/naver_place_search.php'; 51 } 52 53 function staticMapEndpoint(){ 54 if(window.raraJoditStaticMapUrl) return window.raraJoditStaticMapUrl; 55 var imageProxy = window.raraJoditImageProxyUrl || ''; 56 if(imageProxy && imageProxy.indexOf('image_proxy.php') !== -1){ 57 return imageProxy.replace('image_proxy.php', 'naver_static_map.php'); 58 } 59 return '/plugin/editor/jodit/naver_static_map.php'; 60 } 61 62 function mapUrl(name){ 63 return 'https://map.naver.com/p/search/' + encodeURIComponent(name || ''); 64 } 65 66 function staticMapUrl(place){ 67 place = place || {}; 68 var x = place.x || place.mapx || ''; 69 var y = place.y || place.mapy || ''; 70 if(!normalizeCoord(x, y)) return ''; 71 return staticMapEndpoint() + '?x=' + encodeURIComponent(x) + '&y=' + encodeURIComponent(y) + '&level=19&name=' + encodeURIComponent(place.name || place.query || ''); 72 } 73 74 function cardPlace(card){ 75 var body = card ? card.querySelector('.rara-naver-place-body') : null; 76 var titleNode = body ? (body.querySelector('.rara-naver-place-title, strong') || body.firstElementChild) : null; 77 var addressNode = body ? (body.querySelector('.rara-naver-place-address, em') || (titleNode && titleNode.nextElementSibling)) : null; 78 var name = toText(card && card.getAttribute('data-rara-place-name')); 79 if(!name || name === 'ì¥ì') name = toText(titleNode ? titleNode.textContent : ''); 80 var address = toText(card && card.getAttribute('data-rara-place-address')); 81 if(!address) address = toText(addressNode ? addressNode.textContent : ''); 82 var savedMapUrl = card ? (card.getAttribute('data-rara-place-url') || card.getAttribute('href') || '') : ''; 83 return { 84 name:name || 'ì¥ì', 85 query:name || 'ì¥ì', 86 roadAddress:address, 87 x:card ? (card.getAttribute('data-rara-place-mapx') || '') : '', 88 y:card ? (card.getAttribute('data-rara-place-mapy') || '') : '', 89 mapUrl:savedMapUrl || mapUrl(name) 90 }; 91 } 92 93 function placeholderMarkup(place){ 94 place = place || {}; 95 var image = staticMapUrl(place); 96 if(image){ 97 return '<img class="rara-naver-place-static-map" src="' + escapeHtml(image) + '" alt="' + escapeHtml(place.name || place.query || 'ì¥ì') + ' ì§ë" loading="lazy" decoding="async">'; 98 } 99 return '<span class="rara-naver-place-grid" aria-hidden="true"></span><span class="rara-naver-place-pin" aria-hidden="true"><i></i></span>'; 100 } 101 102 function coordFromStaticImage(card){ 103 var img = card ? card.querySelector('.rara-naver-place-map img[src*="naver_static_map.php"]') : null; 104 if(!img) return null; 105 try { 106 var url = new URL(img.getAttribute('src'), window.location.href); 107 return normalizeCoord(url.searchParams.get('x'), url.searchParams.get('y')); 108 } catch(e) { 109 return null; 110 } 111 } 112 113 function ensureSdk(){ 114 if(window.naver && window.naver.maps) return Promise.resolve(window.naver.maps); 115 if(!SDK_URL) return Promise.reject(new Error('NAVER Maps Client ID is not configured'));
116 if(sdkPromise) return sdkPromise; 117 sdkPromise = new Promise(function(resolve, reject){ 118 var existing = document.querySelector('script[data-rara-naver-map-sdk]'); 119 if(existing){ 120 if(window.naver && window.naver.maps){ 121 resolve(window.naver.maps); 122 return; 123 } 124 existing.addEventListener('load', function(){ 125 if(window.naver && window.naver.maps) resolve(window.naver.maps); 126 else reject(new Error('Naver Maps SDK unavailable')); 127 }, {once:true}); 128 existing.addEventListener('error', reject, {once:true}); 129 return; 130 } 131 var script = document.createElement('script'); 132 script.src = SDK_URL; 133 script.async = true; 134 script.defer = true; 135 script.setAttribute('data-rara-naver-map-sdk', '1'); 136 script.onload = function(){ 137 if(window.naver && window.naver.maps) resolve(window.naver.maps); 138 else reject(new Error('Naver Maps SDK unavailable')); 139 }; 140 script.onerror = reject; 141 (document.head || document.documentElement).appendChild(script); 142 }); 143 return sdkPromise; 144 } 145 146 function ensureStyle(){ 147 if(document.getElementById('rara-naver-map-render-style')) return; 148 var style = document.createElement('style'); 149 style.id = 'rara-naver-map-render-style'; 150 style.textContent = [ 151 '.rara-naver-place-card{display:block!important;width:100%!important;max-width:802px!important;margin:28px auto!important;border:1px solid #e1e5e9!important;border-radius:8px!important;background:#fff!important;color:#172033!important;text-decoration:none!important;box-shadow:0 10px 30px rgba(15,23,42,.08)!important;box-sizing:border-box!important;overflow:hidden!important;text-align:left!important;}', 152 '.rara-naver-place-map{position:relative!important;display:block!important;width:100%!important;height:360px!important;min-height:360px!important;border-bottom:1px solid #eef0f2!important;background:#e8f1ea!important;overflow:hidden!important;}', 153 '.rara-naver-map-canvas,.rara-naver-map-fallback{position:absolute!important;inset:0!important;display:flex!important;width:100%!important;height:100%!important;min-height:100%!important;flex-direction:column!important;align-items:center!important;justify-content:center!important;gap:8px!important;background:linear-gradient(145deg,#edf7f1 0%,#e5f1e9 100%)!important;}', 154 '.rara-naver-map-fallback:before{content:"";position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,.7) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.7) 1px,transparent 1px);background-size:64px 64px;pointer-events:none;}', 155 '.rara-naver-map-fallback-brand,.rara-naver-map-fallback-text{position:relative;z-index:1;display:block;text-align:center;}', 156 '.rara-naver-map-fallback-brand{color:#03c75a;font-size:24px;font-weight:700;letter-spacing:-.04em;}', 157 '.rara-naver-map-fallback-text{color:#64748b;font-size:14px;font-weight:500;}', 158 '.rara-naver-place-map.is-map-ready>.rara-naver-place-grid,.rara-naver-place-map.is-map-ready>.rara-naver-place-logo,.rara-naver-place-map.is-map-ready>.rara-naver-place-pin{display:none!important;}', 159 '.rara-naver-place-static-map{display:block!important;width:100%!important;height:100%!important;max-width:none!important;margin:0!important;object-fit:cover!important;}', 160 '.rara-naver-place-body{display:flex!important;min-height:103px!important;padding:22px 26px 24px!important;flex-direction:column!important;align-items:flex-start!important;justify-content:center!important;gap:8px!important;background:#fff!important;box-sizing:border-box!important;text-align:left!important;}', 161 '.rara-naver-place-body>.rara-naver-place-title,.rara-naver-place-body>strong,.rara-naver-place-body>:first-child{display:block!important;width:100%!important;margin:0!important;color:#172033!important;font-size:19px!important;font-weight:650!important;line-height:1.4!important;text-align:left!important;box-sizing:border-box!important;}', 162 '.rara-naver-place-body>.rara-naver-place-address,.rara-naver-place-body>em,.rara-naver-place-body>:nth-child(2){display:block!important;width:100%!important;margin:0!important;color:#697386!important;font-size:14px!important;font-style:normal!important;font-weight:400!important;line-height:1.55!important;text-align:left!important;box-sizing:border-box!important;}', 163 '.rara-map-loading,.rara-map-error{position:absolute;
163left:50%;top:50%;z-index:5;transform:translate(-50%,-50%);display:inline-flex;align-items:center;justify-content:center;min-width:148px;height:40px;padding:0 16px;border:1px solid rgba(226,232,240,.9);border-radius:999px;background:rgba(255,255,255,.95);box-shadow:0 8px 24px rgba(15,23,42,.12);color:#475569;font-size:14px;font-weight:600;white-space:nowrap;box-sizing:border-box;}', 164 '.rara-map-loading:before{content:"";width:14px;height:14px;margin-right:8px;border:2px solid #cbd5e1;border-top-color:#03c75a;border-radius:50%;animation:rara-map-spin .8s linear infinite;box-sizing:border-box;}', 165 '.rara-map-error{color:#0a7f3f;}', 166 '.rara-map-error:before{content:none;}', 167 '.jodit-wysiwyg .rara-naver-map-canvas,.jodit-wysiwyg .rara-naver-map-fallback{pointer-events:none!important;}', 168 '.rara-place-preview-map .rara-naver-map-canvas,.rara-place-preview-map .rara-naver-map-fallback{pointer-events:auto!important;}', 169 '@keyframes rara-map-spin{to{transform:rotate(360deg)}}', 170 '@media(max-width:768px){.rara-naver-place-card{max-width:100%!important;margin:22px auto!important;border-radius:6px!important}.rara-naver-place-map{height:260px!important;min-height:260px!important}.rara-naver-place-body{min-height:94px!important;padding:18px 20px 20px!important}.rara-naver-place-body>.rara-naver-place-title,.rara-naver-place-body>strong,.rara-naver-place-body>:first-child{font-size:17px!important}}', 171 '@media(max-width:768px){#bo_v_con.rara-has-place-card,.member-view-content.rara-has-place-card{overflow:visible!important}}', 172 '@media(max-width:768px){#bo_v_con .rara-naver-place-card,#bo_v_con .rara-naver-place-card[data-rara-block-align],.member-view-content .rara-naver-place-card,.member-view-content .rara-naver-place-card[data-rara-block-align]{display:block!important;width:100vw!important;width:100dvw!important;max-width:100vw!important;max-width:100dvw!important;margin-top:28px!important;margin-right:calc(50% - 50vw)!important;margin-right:calc(50% - 50dvw)!important;margin-bottom:28px!important;margin-left:calc(50% - 50vw)!important;margin-left:calc(50% - 50dvw)!important;border:0!important;border-radius:0!important;background:#fff!important;box-shadow:none!important;overflow:hidden!important}}', 173 '@media(max-width:768px){#bo_v_con .rara-naver-place-map,.member-view-content .rara-naver-place-map{width:100%!important;height:auto!important;min-height:0!important;aspect-ratio:20/9!important;border-bottom:1px solid #e6e6e6!important;background:#eef2ef!important;isolation:isolate!important;touch-action:pan-y!important;-webkit-user-select:none!important;user-select:none!important}}', 174 '@media(max-width:768px){#bo_v_con .rara-naver-place-map .rara-naver-map-canvas,#bo_v_con .rara-naver-place-map .rara-naver-map-canvas *,#bo_v_con .rara-naver-place-map .rara-naver-map-fallback,#bo_v_con .rara-naver-place-map .rara-naver-map-fallback *,#bo_v_con .rara-naver-place-map .rara-naver-place-static-map,.member-view-content .rara-naver-place-map .rara-naver-map-canvas,.member-view-content .rara-naver-place-map .rara-naver-map-canvas *,.member-view-content .rara-naver-place-map .rara-naver-map-fallback,.member-view-content .rara-naver-place-map .rara-naver-map-fallback *,.member-view-content .rara-naver-place-map .rara-naver-place-static-map{pointer-events:none!important;touch-action:pan-y!important;-webkit-user-select:none!important;user-select:none!important}}', 175 '@media(max-width:768px){#bo_v_con .rara-naver-place-map::after,.member-view-content .rara-naver-place-map::after{content:""!important;position:absolute!important;inset:0!important;z-index:2147483646!important;display:block!important;background:transparent!important;pointer-events:auto!important;touch-action:pan-y!important;-webkit-tap-highlight-color:transparent!important}}', 176 '@media(max-width:768px){#bo_v_con .rara-naver-place-body,.member-view-content .rara-naver-place-body{min-height:116px!important;padding:18px 20px 20px!important;gap:5px!important;border-bottom:1px solid #e0e0e0!important;background:#fff!important;justify-content:flex-start!important}}', 177 '@media(max-width:768px){#bo_v_con .rara-naver-place-body>.rara-naver-place-title,#bo_v_con .rara-naver-place-body>strong,#bo_v_con .rara-naver-place-body>:first-child,.member-view-content .rara-naver-place-body>.rara-naver-place-title,.member-view-content .rara-naver-place-body>strong,.member-view-content .rara-naver-place-body>:first-child{color:#333!important;font-size:18px!important;font-weight:600!important;line-height:1.35!important;letter-spacing:-.035em!important}}', 178 '@media(max-width:768px){#bo_v_con .rara-naver-place-body>.rara-naver-place-address,#bo_v_con .rara-naver-place-body>em,#bo_v_con .rara-naver-place-body>:nth-child(2),.member-view-content .rara-naver-place-body>.rara-naver-place-address,.member-view-content .rara-naver-place-body>em,.member-view-content .rara-naver-place-body>:nth-child(2){color:#767676!important;font-size:14px!important;line-height:1.5!important;letter-spacing:-.02em!important}}', 179 '@media(min-width:769px){#bo_v_con .rara-naver-place-card,.member-view-content .rara-naver-place-card{position:relative!important;left:auto!important;display:block!important;width:100%!important;max-width:100%!important;margin:28px auto!important;border:1px solid #e0e0e0!important;border-radius:0!important;background:#fff!important;box-shadow:0 1px 2px rgba(0,0,0,.12)!important;transform:none!important;overflow:hidden!important;box-sizing:border-box!important}}', 180 '@media(min-width:769px){#bo_v_con .rara-naver-place-map,.member-view-content .rara-naver-place-map{width:100%!important;height:auto!important;min-height:0!important;aspect-ratio:20/9!important;border-bottom:0!important;background:#f4f5f3!important}}', 181 '@media(min-width:769px){#bo_v_con .rara-naver-place-body,.member-view-content .rara-naver-place-body{height:75px!important;min-height:75px!important;padding:14px 22px 15px!important;gap:0!important;border:0!important;background:#fff!important;justify-content:flex-start!important}}', 182 '@media(min-width:769px){#bo_v_con .rara-naver-place-body>.rara-naver-place-title,#bo_v_con .rara-naver-place-body>strong,#bo_v_con .rara-naver-place-body>:first-child,.member-view-content .rara-naver-place-body>.rara-naver-place-title,.member-view-content .rara-naver-place-body>strong,.member-view-content .rara-naver-place-body>:first-child{color:#333!important;font-size:18px!important;font-weight:400!important;line-height:24px!important;letter-spacing:-.025em!important}}', 183 '@media(min-width:769px){#bo_v_con .rara-naver-place-body>.rara-naver-place-address,#bo_v_con .rara-naver-place-body>em,#bo_v_con .rara-naver-place-body>:nth-child(2),.member-view-content .rara-naver-place-body>.rara-naver-place-address,.member-view-content .rara-naver-place-body>em,.member-view-content .rara-naver-place-body>:nth-child(2){margin-top:4px!important;color:#999!important;font-size:13px!important;line-height:18px!important;letter-spacing:-.02em!important}}', 184 '@media(min-width:1920px){#bo_v_con .rara-naver-place-card.rara-public-place-card,.member-view-content .rara-naver-place-card.rara-public-place-card{width:100%!important;max-width:100%!important;margin-top:28px!important;margin-bottom:28px!important;border-width:.052083333vw!important;box-shadow:0 .052083333vw .104166667vw rgba(0,0,0,.12)!important}}', 185 '@media(min-width:1920px){#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body,.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body{height:3.90625vw!important;min-height:3.90625vw!important;padding:.729166667vw 1.145833333vw .78125vw!important}}', 186 '@media(min-width:1920px){#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>.rara-naver-place-title,#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>strong,#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>:first-child,.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>.rara-naver-place-title,.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>strong,.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>:first-child{font-size:.9375vw!important;line-height:1.25vw!important}}', 187 '@media(min-width:1920px){#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>.rara-naver-place-address,#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>em,#bo_v_con .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>:nth-child(2),.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>.rara-naver-place-address,.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>em,.member-view-content .rara-naver-place-card.rara-public-place-card .rara-naver-place-body>:nth-child(2){margin-top:.208333333vw!important;font-size:.677083333vw!important;line-height:.9375vw!important}}' 188 ].join(''); 189 /* Public detail and company landing pages must share one place-card style. */ 190 style.textContent = style.textContent.replace( 191 /\.member-view-content/g, 192 ':is(.member-view-content,.member-notice-content)' 193 ); 194 document.head.appendChild(style); 195 } 196 197 function setMessage(target, text, cls){ 198 if(!target) return; 199 target.classList.remove('is-map-ready'); 200 target.innerHTML = '<span class="' + (cls || 'rara-map-loading') + '">' + escapeHtml(text) + '</span>'; 201 } 202 203 function renderOfficialStaticMap(target, coords, name, token){ 204 if(!target || !coords || typeof window.fetch !== 'function') return Promise.resolve(false); 205 var url = staticMapUrl({x:coords.lng, y:coords.lat, name:name || 'ì¥ì'}); 206 if(!url) return Promise.resolve(false); 207 return window.fetch(url, {credentials:'same-origin', cache:'force-cache'}).then(function(response){ 208 var type = String(response.headers && response.headers.get ? response.headers.get('content-type')
208: '').toLowerCase(); 209 if(!response.ok || !/^image\/(?:png|jpe?g|webp)/.test(type)) return false; 210 return response.blob().then(function(blob){ 211 if(target.__raraMapToken !== token) return false; 212 var objectUrl = window.URL && window.URL.createObjectURL ? window.URL.createObjectURL(blob) : ''; 213 if(!objectUrl) return false; 214 target.classList.add('is-map-ready'); 215 target.innerHTML = '<img class="rara-naver-place-static-map" src="' + escapeHtml(objectUrl) + '" alt="' + escapeHtml(name || 'ì¥ì') + ' ë¤ì´ë² ì§ë" loading="eager" decoding="async">'; 216 var image = target.querySelector('.rara-naver-place-static-map'); 217 if(image) image.addEventListener('load', function(){ window.URL.revokeObjectURL(objectUrl); }, {once:true}); 218 return true; 219 }); 220 }).catch(function(){ return false; }); 221 } 222 223 function fallbackMap(target, coords, name, token){ 224 if(!target || target.__raraMapToken !== token) return; 225 if(!coords){ 226 setMessage(target, 'ìì¹ ì 보를 íì¸í ì ììµëë¤', 'rara-map-error'); 227 return; 228 } 229 target.classList.add('is-map-ready'); 230 target.innerHTML = '<span class="rara-naver-map-fallback" role="img" aria-label="' + escapeHtml(name || 'ì¥ì') + ' ë¤ì´ë² ì§ë">' + 231 '<strong class="rara-naver-map-fallback-brand">NAVER ì§ë</strong>' + 232 '<span class="rara-naver-map-fallback-text">ì¥ì ì ë³´ ìììì ë¤ì´ë² ì§ë를 íì¸í ì ììµëë¤.</span>' + 233 '</span>'; 234 } 235 236 function lockMapCanvasFocus(canvas){ 237 if(!canvas || !canvas.setAttribute) return; 238 canvas.setAttribute('aria-hidden', 'true'); 239 canvas.setAttribute('tabindex', '-1'); 240 canvas.querySelectorAll('[tabindex],a[href],button,input,select,textarea').forEach(function(node){ 241 node.setAttribute('tabindex', '-1'); 242 }); 243 } 244 245 function renderMap(target, coords, name, owner){ 246 if(!target || !coords) return Promise.reject(new Error('No coordinates')); 247 ensureStyle(); 248 var editorStaticOnly = !!(owner && owner.nodeType === 1 && isEditorCard(owner)); 249 var publicMobileStatic = !!(owner && owner.nodeType === 1 && isPublishedMobileCard(owner)); 250 var publicDesktopMap = !!(owner && owner.nodeType === 1 && publishedBoundary(owner) && !publicMobileStatic); 251 var token = (target.__raraMapToken || 0) + 1; 252 target.__raraMapToken = token; 253 if(publicMobileStatic){ 254 var accessiblePlace = cardPlace(owner); 255 target.classList.add('rara-map-touch-locked'); 256 target.setAttribute('role', 'img'); 257 target.setAttribute('aria-label', (name || accessiblePlace.name || 'ì¥ì') + 258 (accessiblePlace.roadAddress ? ', ' + accessiblePlace.roadAddress : '') + ' ìì¹ ì§ë'); 259 target.removeAttribute('tabindex'); 260 } else { 261 target.classList.remove('rara-map-touch-locked'); 262 target.removeAttribute('role'); 263 target.removeAttribute('aria-label'); 264 } 265 setMessage(target, 'ì§ë ë¶ë¬ì¤ë ì¤'); 266 /* 267 * Keep contenteditable static so Jodit never serializes NAVER's tile DOM. 268 * Mobile published content intentionally uses the official static image so 269 * a swipe always scrolls the article instead of panning or zooming the map. 270 */ 271 if(editorStaticOnly){ 272 return renderOfficialStaticMap(target, coords, name, token).then(function(rendered){ 273 if(rendered || target.__raraMapToken !== token) return null; 274 target.classList.add('is-map-ready'); 275 target.innerHTML = placeholderMarkup({x:coords.lng, y:coords.lat, name:name || 'ì¥ì'}); 276 return null; 277 }); 278 } 279 280 var staticFirst = publicMobileStatic 281 ? renderOfficialStaticMap(target, coords, name, token) 282 : Promise.resolve(false); 283 284 return staticFirst.then(function(rendered){ 285 if(rendered || target.__raraMapToken !== token) return null; 286 return ensureSdk(); 287 }).then(function(maps){ 288 if(!maps || target.__raraMapToken !== token) return null; 289 target.classList.add('is-map-ready'); 290 target.innerHTML = ''; 291 var canvas = document.createElement('span'); 292 canvas.className = 'rara-naver-map-canvas'; 293 canvas.setAttribute('data-rara-interactive-map', '1'); 294 if(publicMobileStatic){ 295 lockMapCanvasFocus(canvas); 296 } 297 target.appendChild(canvas); 298 var center = new maps.LatLng(coords.lat, coords.lng); 299 var publishedInteractionLimited = publicMobileStatic || publicDesktopMap; 300 var zoomControlOptions = {position:maps.Position.TOP_RIGHT}; 301 if(publicDesktopMap && maps.ZoomControlStyle && maps.ZoomControlStyle.SMALL !== undefined){ 302 zoomControlOptions.style = maps.ZoomControlStyle.SMALL; 303 } 304 var map = new maps.Map(canvas, { 305 center:center, 306 zoom:publicDesktopMap ? 17 : 19, 307 minZoom:7, 308 draggable:!publicMobileStatic, 309 scrollWheel:!publishedInteractionLimited, 310 pinchZoom:!publishedInteractionLimited, 311 keyboardShortcuts:!publishedInteractionLimited, 312 disableDoubleClickZoom:publishedInteractionLimited, 313 disableDoubleTapZoom:publishedInteractionLimited, 314 disableTwoFingerTapZoom:publishedInteractionLimited, 315 zoomControl:!publicMobileStatic, 316 zoomControlOptions:zoomControlOptions, 317 mapDataControl:false, 318 scaleControl:publicDesktopMap, 319 logoControl:true, 320 logoControlOptions:{position:maps.Position.BOTTOM_LEFT} 321 }); 322 if(publicMobileStatic) lockMapCanvasFocus(canvas); 323 var markerOptions = {position:center, map:map, title:name || 'ì¥ì'}; 324 if(publicDesktopMap && maps.Point){ 325 var markerScale = Math.max(1, Number(window.innerWidth || 0) / 1920); 326 var markerPx = function(value){ return (value * markerScale).toFixed(3) + 'px'; }; 327 var markerIcon = { 328 content:'<span aria-hidden="true" style="position:relative;display:block;width:' + markerPx(34) + ';height:' + markerPx(42) + '"><span style="position:absolute;
328left:' + markerPx(2) + ';top:' + markerPx(1) + ';width:' + markerPx(30) + ';height:' + markerPx(30) + ';border:' + markerPx(1) + ' solid #00a84f;border-radius:50% 50% 50% 0;background:#03c75a;box-shadow:0 ' + markerPx(1) + ' ' + markerPx(2) + ' rgba(0,0,0,.22);box-sizing:border-box;transform:rotate(-45deg)"></span><span style="position:absolute;left:' + markerPx(12) + ';top:' + markerPx(12) + ';width:0;height:0;border-left:' + markerPx(5) + ' solid transparent;border-right:' + markerPx(5) + ' solid transparent;border-top:' + markerPx(7) + ' solid #fff"></span></span>', 329 anchor:new maps.Point(17 * markerScale, 38 * markerScale) 330 }; 331 if(maps.Size) markerIcon.size = new maps.Size(34 * markerScale, 42 * markerScale); 332 markerOptions.icon = markerIcon; 333 } 334 var marker = new maps.Marker(markerOptions); 335 if(owner){ 336 owner.__raraNaverMap = map; 337 owner.__raraNaverMarker = marker; 338 } 339 target.__raraNaverMap = map; 340 target.__raraNaverMarker = marker; 341 setTimeout(function(){ 342 if(target.__raraMapToken !== token) return; 343 if(publicMobileStatic) lockMapCanvasFocus(canvas); 344 try { maps.Event.trigger(map, 'resize'); } catch(e){} 345 try { map.setCenter(center); } catch(e){} 346 }, 140); 347 setTimeout(function(){ 348 if(target.__raraMapToken !== token || !canvas.isConnected) return; 349 if(publicMobileStatic) lockMapCanvasFocus(canvas); 350 /* ì¸ì¦ ì¤í¨ ì íì¬ ì§ëë¡ ë°ê¾¸ì§ ìê³ NAVER ì ì© ìë´ íë©´ì ì ì§í©ëë¤. */ 351 if(!canvas.firstElementChild){ 352 fallbackMap(target, coords, name, token); 353 } 354 }, 1200); 355 return map; 356 }).catch(function(){ 357 return renderOfficialStaticMap(target, coords, name, token).then(function(rendered){ 358 if(!rendered) fallbackMap(target, coords, name, token); 359 return null; 360 }); 361 }); 362 } 363 364 function fetchCoords(card){ 365 if(card.__raraMapFetchPromise) return card.__raraMapFetchPromise; 366 var place = cardPlace(card); 367 var endpoint = placeEndpoint(); 368 var separator = endpoint.indexOf('?') === -1 ? '?' : '&'; 369 card.__raraMapFetchPromise = fetch(endpoint + separator + 'mode=resolve&query=' + encodeURIComponent(place.name) + '&address=' + encodeURIComponent(place.roadAddress || ''), { 370 credentials:'same-origin', 371 cache:'no-store' 372 }).then(function(res){ 373 if(!res.ok) throw new Error('Place resolve failed: ' + res.status); 374 return res.json(); 375 }).then(function(data){ 376 if(!data || data.success === false) return null; 377 var first = data && data.list ? data.list[0] : null; 378 if(!first) return null; 379 card.setAttribute('data-rara-place-name', first.name || place.name); 380 card.setAttribute('data-rara-place-address', first.roadAddress || first.address || ''); 381 card.setAttribute('data-rara-place-mapx', first.x || first.mapx || ''); 382 card.setAttribute('data-rara-place-mapy', first.y || first.mapy || ''); 383 card.setAttribute('data-rara-place-url', first.mapUrl || mapUrl(first.name || place.name)); 384 var bodyName = card.querySelector('.rara-naver-place-body strong'); 385 var bodyAddr = card.querySelector('.rara-naver-place-body em'); 386 if(bodyName && first.name) bodyName.textContent = first.name; 387 if(bodyAddr && (first.roadAddress || first.address)) bodyAddr.textContent = first.roadAddress || first.address; 388 return normalizeCoord(first.x || first.mapx, first.y || first.mapy); 389 }).catch(function(){ return null; }).then(function(coords){ 390 if(!coords) card.__raraMapFetchPromise = null; 391 return coords; 392 }); 393 return card.__raraMapFetchPromise; 394 } 395 396 function geocodeCoords(card){ 397 var place = cardPlace(card); 398 var query = place.roadAddress || place.name; 399 if(!query) return Promise.resolve(null); 400 return ensureSdk().then(function(maps){ 401 return new Promise(function(resolve){ 402 if(!maps || !maps.Service || typeof maps.Service.geocode !== 'function'){ 403 resolve(null); 404 return; 405 } 406 maps.Service.geocode({query:query}, function(status, response){ 407 if(status !== maps.Service.Status.OK || !response || !response.v2 || !response.v2.addresses || !response.v2.addresses[0]){ 408 resolve(null); 409 return; 410 } 411 var address = response.v2.addresses[0]; 412 card.setAttribute('data-rara-place-mapx', address.x || ''); 413 card.setAttribute('data-rara-place-mapy', address.y || ''); 414 resolve(normalizeCoord(address.x, address.y)); 415 }); 416 }); 417 }).catch(function(){ return null; }); 418 } 419 420 function isEditorCard(card){ 421 return !!(card && card.closest && card.closest('.jodit-wysiwyg')); 422 } 423 424 function publishedBoundary(card){ 425 if(!card || !card.closest || isEditorCard(card)) return null; 426 return card.closest(PUBLISHED_CONTENT_SELECTOR); 427 } 428 429 function isPublishedMobileCard(card){ 430 if(!publishedBoundary(card)) return false;
431 return !!(window.matchMedia && window.matchMedia('(max-width: 768px)').matches); 432 } 433 434 function markPublishedCard(card){ 435 var boundary = publishedBoundary(card); 436 if(!boundary) return; 437 boundary.classList.add('rara-has-place-card'); 438 card.classList.add('rara-public-place-card'); 439 } 440 441 function hasMapTarget(card){ 442 return !!(card && card.querySelector && card.querySelector('.rara-naver-place-map')); 443 } 444 445 function isNaverSdkImage(image){ 446 var src = image ? String(image.getAttribute('src') || image.src || '').toLowerCase() : ''; 447 if(!src) return false; 448 return /(?:pstatic\.net|naver\.net|naver\.com)/.test(src) && 449 /(?:\/styles\/basic\/|\/static\/maps\/|maptile|map\/tile|zoom-|sprite|mantle)/.test(src); 450 } 451 452 function isLeakedMapCard(card){ 453 if(!card || hasMapTarget(card)) return false; 454 var href = String(card.getAttribute('href') || card.getAttribute('data-rara-place-url') || '').toLowerCase(); 455 if(href.indexOf('map.naver.com') === -1) return false; 456 var images = card.querySelectorAll('img'); 457 if(!images.length) return false; 458 return Array.prototype.every.call(images, isNaverSdkImage); 459 } 460 461 function contentBoundary(node, root){ 462 var boundary = node && node.closest ? node.closest('.jodit-wysiwyg, ' + PUBLISHED_CONTENT_SELECTOR) : null; 463 if(boundary) return boundary; 464 if(root && root.nodeType === 1) return root; 465 return document.body || document.documentElement; 466 } 467 468 function directChildOf(node, boundary){ 469 var current = node; 470 while(current && current.parentElement && current.parentElement !== boundary){ 471 current = current.parentElement; 472 } 473 return current; 474 } 475 476 function isLeakedSdkTree(node){ 477 if(!node || !node.querySelectorAll) return false; 478 var cards = node.querySelectorAll('.rara-naver-place-card'); 479 if(!cards.length) return false; 480 var leaked = 0; 481 for(var i = 0; i < cards.length; i++){ 482 if(hasMapTarget(cards[i])) return false; 483 if(isLeakedMapCard(cards[i])) leaked++; 484 } 485 if(!leaked) return false; 486 var images = node.querySelectorAll('img'); 487 if(!images.length || !Array.prototype.every.call(images, isNaverSdkImage)) return false; 488 return !toText(node.textContent || ''); 489 } 490 491 function isLeakedSdkFragment(node){ 492 if(!node || !node.querySelectorAll) return false; 493 if(isLeakedSdkTree(node)) return true; 494 if(node.querySelector('.rara-naver-place-map')) return false; 495 var images = node.querySelectorAll('img'); 496 if(!images.length || !Array.prototype.every.call(images, isNaverSdkImage)) return false; 497 var text = toText(node.textContent || '').replace(/ì§ë 컨í¸ë¡¤ë¬ ë²ë¡|ë¶ëì°|거리|ì,ë©´,ë|ì,êµ°,구|ì,ë|êµê°/g, ''); 498 return !toText(text); 499 } 500 501 /* Repair content saved before the editor-only static preview was introduced. */ 502 function repairLeakedMapArtifacts(root){ 503 var scope = root && root.querySelectorAll ? root : document; 504 var validCards = Array.prototype.filter.call(scope.querySelectorAll('.rara-naver-place-card'), hasMapTarget); 505 validCards.forEach(function(card){ 506 if(card.querySelector('.rara-naver-place-body')) return; 507 var boundary = contentBoundary(card, scope); 508 var top = directChildOf(card, boundary); 509 var cursor = top ? top.nextElementSibling : null; 510 var artifacts = []; 511 for(var i = 0; cursor && i < 8; i++){ 512 var next = cursor.nextElementSibling; 513 if(isLeakedSdkFragment(cursor)){ 514 artifacts.push(cursor); 515 cursor = next; 516 continue; 517 } 518 if(cursor.matches && cursor.matches('.rara-naver-place-body')){ 519 card.appendChild(cursor); 520 break; 521 } 522 break; 523 }
524 artifacts.forEach(function(node){ 525 if(node.parentNode) node.parentNode.removeChild(node); 526 }); 527 }); 528 529 var leakedCards = Array.prototype.filter.call(scope.querySelectorAll('.rara-naver-place-card'), isLeakedMapCard); 530 var removals = []; 531 leakedCards.forEach(function(card){ 532 var boundary = contentBoundary(card, scope); 533 var top = directChildOf(card, boundary); 534 var target = isLeakedSdkTree(top) ? top : card; 535 if(removals.indexOf(target) === -1) removals.push(target); 536 }); 537 removals.forEach(function(node){ 538 if(node.parentNode) node.parentNode.removeChild(node); 539 }); 540 return removals.length; 541 } 542 543 function normalizeCard(card){ 544 if(!card) return; 545 markPublishedCard(card); 546 var place = cardPlace(card); 547 var body = card.querySelector('.rara-naver-place-body'); 548 if(body){ 549 var titleNode = body.querySelector('.rara-naver-place-title, strong') || body.firstElementChild; 550 var addressNode = body.querySelector('.rara-naver-place-address, em') || (titleNode && titleNode.nextElementSibling); 551 if(titleNode && titleNode.classList) titleNode.classList.add('rara-naver-place-title'); 552 if(addressNode && addressNode.classList) addressNode.classList.add('rara-naver-place-address'); 553 } 554 if(!card.getAttribute('data-rara-place-name')) card.setAttribute('data-rara-place-name', place.name); 555 if(!card.getAttribute('data-rara-place-address') && place.roadAddress) card.setAttribute('data-rara-place-address', place.roadAddress); 556 if(!card.getAttribute('data-rara-place-url')) card.setAttribute('data-rara-place-url', place.mapUrl || mapUrl(place.name)); 557 card.setAttribute('data-rara-map-version', '2'); 558 if(card.tagName && card.tagName.toLowerCase() === 'a'){ 559 card.setAttribute('draggable', 'false'); 560 if(isEditorCard(card)){ 561 card.removeAttribute('href'); 562 card.removeAttribute('target'); 563 card.removeAttribute('rel'); 564 card.setAttribute('role', 'group'); 565 } else { 566 card.setAttribute('href', place.mapUrl || mapUrl(place.name)); 567 card.setAttribute('target', '_blank'); 568 card.setAttribute('rel', 'noopener noreferrer'); 569 card.setAttribute('aria-label', place.name + ' ë¤ì´ë² ì§ë ì´ê¸°'); 570 } 571 } 572 } 573 574 function renderCard(card){ 575 if(!card || card.__raraMapRendering) return; 576 var target = card.querySelector('.rara-naver-place-map'); 577 if(!target) return; 578 normalizeCard(card); 579 card.__raraMapRendering = true; 580 var place = cardPlace(card); 581 var coords = normalizeCoord(place.x, place.y) || coordFromStaticImage(card); 582 var finish = function(found){ 583 if(!found){ 584 var retries = parseInt(card.__raraMapRetryCount || 0, 10) || 0; 585 if(retries < 1){ 586 card.__raraMapRetryCount = retries + 1; 587 setMessage(target, 'ìì¹ ì 보를 ë¤ì íì¸íê³ ììµëë¤', 'rara-map-loading'); 588 setTimeout(function(){ 589 card.__raraMapRendering = false; 590 renderCard(card); 591 }, 1200); 592 return; 593 } 594 setMessage(target, 'ìì¹ ì 보를 íì¸í ì ììµëë¤', 'rara-map-error'); 595 return; 596 } 597 card.__raraMapRetryCount = 0; 598 renderMap(target, found, cardPlace(card).name, card); 599 }; 600 if(coords){ 601 finish(coords); 602 return; 603 } 604 fetchCoords(card).then(function(fetched){ 605 if(fetched) return fetched; 606 return geocodeCoords(card); 607 }).then(finish); 608 } 609 610 function renderPreview(target, place){ 611 place = place || DEFAULT_PLACE; 612 var coords = normalizeCoord(place.x || place.mapx, place.y || place.mapy); 613 if(coords) return renderMap(target, coords, place.name || place.query || 'ì¥ì', target); 614 setMessage(target, 'ìì¹ ì 보를 ì°¾ë ì¤'); 615 return fetch(placeEndpoint() + '?query=' + encodeURIComponent(place.name || place.query || ''), { 616 credentials:'same-origin', cache:'no-store' 617 }).then(function(res){ return res.json(); }).then(function(data){ 618 var first = data && data.list ? data.list[0] : null; 619 var found = first ? normalizeCoord(first.x || first.mapx, first.y || first.mapy) : null; 620 if(!found) throw new Error('No coordinates'); 621 return renderMap(target, found, first.name || place.name || 'ì¥ì', target); 622 }).catch(function(){ 623 setMessage(target, 'ìì¹ ì 보를 íì¸í ì ììµëë¤', 'rara-map-error'); 624 return null; 625 }); 626 } 627 628 function renderAll(root){ 629 ensureStyle(); 630 repairLeakedMapArtifacts(root || document); 631 (root || document).querySelectorAll('.rara-naver-place-card').forEach(renderCard); 632 } 633 634 function prepareEditorDomForSave(root){ 635 var scope = root && root.editor ? root.editor : root; 636 if(!scope || !scope.querySelectorAll) return; 637 repairLeakedMapArtifacts(scope); 638 scope.querySelectorAll('.rara-naver-place-card').forEach(function(card){ 639 var target = card.querySelector('.rara-naver-place-map'); 640 if(!target) return; 641 normalizeCard(card); 642 target.__raraMapToken = (target.__raraMapToken || 0) + 1; 643 target.classList.remove('is-map-ready'); 644 target.innerHTML = placeholderMarkup(cardPlace(card)); 645 }); 646 } 647 648 function serializeForSave(root, html){ 649 var scope = root && root.editor ? root.editor : root; 650 var container = document.createElement('div'); 651 if(typeof html === 'string') container.innerHTML = html; 652 else if(scope && typeof scope.innerHTML === 'string') container.innerHTML = scope.innerHTML; 653 else return ''; 654 repairLeakedMapArtifacts(container); 655 container.querySelectorAll('.rara-naver-place-card').forEach(function(card){ 656 var target = card.querySelector('.rara-naver-place-map'); 657 if(!target) return; 658 normalizeCard(card); 659 target.classList.remove('is-map-ready'); 660 target.innerHTML = placeholderMarkup(cardPlace(card)); 661 }); 662 container.querySelectorAll('.rara-v3-selected,.rara-v3-dragging').forEach(function(node){ 663 node.classList.remove('rara-v3-selected', 'rara-v3-dragging'); 664 node.removeAttribute('data-rara-v3-selected'); 665 }); 666 return container.innerHTML; 667 } 668 669 function prepareForSave(root, html){ 670 return serializeForSave(root, html); 671 } 672 673 function bindClickGuard(){ 674 if(window.__raraNaverMapClickGuard) return; 675 window.__raraNaverMapClickGuard = true; 676 document.addEventListener('click', function(e){ 677 var card = e.target && e.target.closest ? e.target.closest('.rara-naver-place-card') : null; 678 if(!card) return; 679 if(isEditorCard(card) || (e.target.closest && e.target.closest('.rara-naver-place-map'))){ 680 e.preventDefault(); 681 } 682 }, true); 683 document.addEventListener('dragstart', function(e){ 684 var card = e.target && e.target.closest ? e.target.closest('.rara-naver-place-card') : null; 685 if(card) e.preventDefault(); 686 }, true); 687 } 688 689 function observe(){ 690 if(observer || !window.MutationObserver) return; 691 observer = new MutationObserver(function(mutations){ 692 mutations.forEach(function(mutation){ 693 mutation.addedNodes && Array.prototype.forEach.call(mutation.addedNodes, function(node){ 694 if(node.nodeType !== 1) return; 695 if(node.matches && node.matches('.rara-naver-place-card')) renderCard(node); 696 else if(node.querySelectorAll) renderAll(node); 697 }); 698 }); 699 }); 700 observer.observe(document.body || document.documentElement, {childList:true, subtree:true}); 701 } 702
703 function init(){ 704 bindClickGuard(); 705 renderAll(document); 706 observe(); 707 if(!window.__raraNaverMapSubmitBound){ 708 window.__raraNaverMapSubmitBound = true; 709 document.addEventListener('submit', function(e){ 710 var form = e.target; 711 var area = form && form.querySelector ? form.querySelector('.jodit-wysiwyg') : null; 712 if(!area) return; 713 /* Sanitize the live map before the image-link serializer clones it. */ 714 prepareEditorDomForSave(area); 715 var source = typeof window.raraSerializeImageLinks === 'function' 716 ? window.raraSerializeImageLinks(area) 717 : area.innerHTML; 718 var content = prepareForSave(area, source); 719 var textarea = form.querySelector('textarea[name="wr_content"]'); 720 if(textarea && content) textarea.value = content; 721 }, true); 722 } 723 } 724 725 if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); 726 else init(); 727 728 window.RaraNaverMapRender = { 729 renderAll:renderAll, 730 renderCard:renderCard, 731 renderPreview:renderPreview, 732 prepareForSave:prepareForSave, 733 serializeForSave:serializeForSave, 734 defaultPlace:function(){ 735 return { 736 name:DEFAULT_PLACE.name, 737 query:DEFAULT_PLACE.query, 738 roadAddress:DEFAULT_PLACE.roadAddress, 739 address:DEFAULT_PLACE.address, 740 x:DEFAULT_PLACE.x, 741 y:DEFAULT_PLACE.y, 742 mapUrl:DEFAULT_PLACE.mapUrl 743 }; 744 } 745 }; 746 window.__raraNaverMapRenderVersion = 'v18-unified-public-boundaries-20260803'; 747})(window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.