1<!DOCTYPE html> 2<html lang="zh-CN"> 3 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <meta name="keywords" content="IP,IPæ¥è¯¢,IPå°åæ¥è¯¢,IPå°å,IPå°åå½å±å°,æ¥è¯¢æ¬æºIP,ç½éæµè¯"> 8 <meta name="description" 9 content="A better IP Toolbox. Easy to check what's your IPs, IP informations, check for DNS leaks, examine WebRTC connections, and test website availability"> 10 <meta name="apple-mobile-web-app-capable" content="yes"> 11 <meta name="theme-color" content="#f8f9fa"> 12 <meta name="background-color" content="#ffffff"> 13 <link rel="apple-touch-icon" href="res/img/logo-512.png"> 14 <title>æ¥çæç IP å°å | æ¥è¯¢æ¬æº IP å°ååå½å±å° | æ¥ç WebRTC è¿æ¥ IP ï½ DNS æ³é²æ£æµ | ç½éæµè¯ | Jason Ng é¿ç¦ 弿ºä½å </title> 15 <!-- Bootstrap CSS --> 16 <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" rel="stylesheet" 17 integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> 18 <link rel="stylesheet" 19 href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.2/font/bootstrap-icons.min.css"> 20 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.5.0/css/flag-icon.min.css"> 21 <link rel="icon" href="favicon.ico"> 22 <link rel="stylesheet" href="res/css/style.css"> 23 <link rel="manifest" href="manifest.json"> 24</head> 25 26<body> 27 28 <!-- Loading --> 29 <div id="loading" class="d-flex justify-content-center jn-loading"> 30 <div class="spinner-border text-success" role="status"> 31 <span class="visually-hidden">Loading...</span> 32 </div> 33 </div> 34 35 <div id="app" v-cloak> 36 <!-- Nav --> 37 <nav id="navbar-top" class="navbar navbar-expand-lg bg-body-tertiary px-3 mb-3 jn-navbar-top" 38 :class="{ 'dark-mode-nav navbar-dark bg-dark': isDarkMode }"> 39 <div class="jn-logo"> 40 <a class="navbar-brand" :class="{ 'text-white': isDarkMode }" href="/" @click="handleLogoClick"><i 41 class="bi bi-box-seam-fill"></i> 42 {{currentTexts.nav.Title}}</a> 43 <div class="form-check form-switch"> 44 <input class="form-check-input" type="checkbox" role="switch" id="darkModeSwitch" @change="toggleDarkMode" 45 :checked="isDarkMode"> 46 <label class="form-check-label" for="darkModeSwitch"> 47 <i v-if="isDarkMode" class="bi bi-moon-stars"></i> 48 <i v-else class="bi bi-brightness-high"></i> 49 </label> 50 </div> 51 52 </div> 53 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" 54 aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation"> 55 <span class="navbar-toggler-icon bg-transparent "></span> 56 </button> 57 <div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup"> 58 <div class="navbar-nav "> 59 <a class="nav-link" :class="{ 'text-white jn-deactive': isDarkMode }" 60 href="#IPInfo">{{currentTexts.nav.IPinfo}}</a> 61 <a class="nav-link" :class="{ 'text-white jn-deactive': isDarkMode }" 62 href="#Connectivity">{{currentTexts.nav.Connectivity}}</a> 63 <a class="nav-link" :class="{ 'text-white jn-deactive': isDarkMode }" 64 href="#WebRTC">{{currentTexts.nav.WebRTC}}</a> 65 <a class="nav-link" :class="{ 'text-white jn-deactive': isDarkMode }" 66 href="#DNSLeakTest">{{currentTexts.nav.DNSLeakTest}}</a> 67 <a class="nav-link" :class="{ 'text-white jn-deactive': isDarkMode }" 68 href="#SpeedTest">{{currentTexts.nav.SpeedTest}}</a> 69 </div> 70 </div> 71 </nav> 72 73 74 75 76 <!-- Proxy Alerts --> 77 <div class="toast-container position-fixed bottom-0 end-0 p-3 jn-toast"> 78 <div id="toast" class="toast" :class="{ 'dark-mode': isDarkMode }" role="alert" ref="toast" aria-live="assertive" 79 aria-atomic="true"> 80 <div class="toast-header" :class="{ 'dark-mode-title': isDarkMode }"> 81 <strong class="me-auto" :class="alertStyle">{{ alertTitle }}</strong> 82 <button type="button" class="btn-close" :class="{ 'dark-mode-close-button': isDarkMode }" 83 data-bs-dismiss="toast" aria-label="Close"></button> 84 </div> 85 <div class="toast-body"> 86 {{ alertMessage }} 87 </div> 88 </div> 89 </div> 90 91 92 <div id="mainpart" class="container mt-5 jn-container"> 93 <div data-bs-spy="scroll" data-bs-target="#navbar-top" data-bs-root-margin="0px 0px -40%" 94 data-bs-smooth-scroll="true" class="rounded-2" tabindex="0"> 95 <!-- IP Infos --> 96 <div class="ip-data-section mb-4">
97 <div class="row" :class="{'jn-title2': !isMobile,'jn-title':isMobile}"> 98 <h2 id="IPInfo" class="col-4" :class="{'mobile-h2' : isMobile }">ðµï¸ 99 {{currentTexts.ipInfos.Title}}</h2> 100 <div class="form-check form-switch col-8 jn-radio"> 101 102 <div :class="{'col-4': isMobile }"> 103 <input v-if="isMobile" class="form-check-input" type="checkbox" id="collapseSwitch" 104 @change="toggleCollapse" :checked="!isCardsCollapsed"> 105 <label v-if="isMobile" class="form-check-label" for="collapseSwitch"> <i 106 class="bi bi-list-columns-reverse"></i></label> 107 </div> 108 109 <div> 110 <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault" 111 @change="toggleMaps" :checked="isMapShown" :disabled="!bingMapAPIKEY"> 112 113 <label class="form-check-label" for="flexSwitchCheckDefault"> 114 <i :class="['bi', bingMapAPIKEY ? 'bi bi-map-fill':'bi bi-map']"></i> 115 </label> 116 <i :class="['bi ', !bingMapAPIKEY ? 'bi-plus-circle-fill':'bi-pencil-square']" data-bs-toggle="modal" 117 data-bs-target="#addBingMapKey"></i> 118 119 </div> 120 121 122 123 </div> 124 </div> 125 <div class="text-secondary"> 126 <p>{{currentTexts.ipInfos.Notes}}</p> 127 </div> 128 <div class="jn-card-deck"> 129 <div class="row"> 130 <div v-for="card in ipDataCards" :key="card.id" :ref="card.id" 131 :class="{'jn-opacity': !card.asn, 'col-xl-4': true, 'col-lg-6': true, 'col-md-6': true, 'mb-4': true}"> 132 <div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 133 <div class="card-header jn-ip-title" :class="{ 'dark-mode-title': isDarkMode }" 134 style="font-weight: bold;"> 135 <span>{{currentTexts.ipInfos.Source}}: {{ card.source }}</span> 136 <button @click="refreshCard(card)" 137 :class="['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']"> 138 <i class="bi bi-arrow-clockwise"></i></button> 139 </div> 140 141 <!-- Google Maps iFrame --> 142 <!-- <iframe class="card-img-top" :src="card.mapUrl" width="100%" height="200" frameborder="0" 143 style="border:0;" allowfullscreen> 144 </iframe> --> 145 146 <img v-if="isMapShown" v-if="!isMobile" :src="card.mapUrl" class="card-img-top jn-map-image" 147 alt="Map"> 148 <div class="card-body"> 149 <ul class="list-group list-group-flush"> 150 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }"><span 151 class="jn-text">ð¥ï¸ {{currentTexts.ipInfos.IP}}</span>: {{ card.ip 152 }} 153 </li> 154 <li class="list-group-item jn-list-group-item" 155 :class="{ 'dark-mode': isDarkMode,'mobile-list': isMobile && isCardsCollapsed }"> 156 <span class="jn-text">ð {{currentTexts.ipInfos.Country}}</span>: {{ 157 card.country_name }} <span v-if="card.country_code" 158 :class="'flag-icon flag-icon-' + card.country_code.toLowerCase()"></span> 159 </li> 160 <li v-show="!isMobile || !isCardsCollapsed" class="list-group-item jn-list-group-item" 161 :class="{ 'dark-mode': isDarkMode }"><span class="jn-text">ðï¸ 162 {{currentTexts.ipInfos.Region}}</span>: {{ card.region 163 }}</li> 164 <li v-show="!isMobile || !isCardsCollapsed" class="list-group-item jn-list-group-item" 165 :class="{ 'dark-mode': isDarkMode }"><span class="jn-text">ð 166 {{currentTexts.ipInfos.City}}</span>: {{ card.city }} 167 </li> 168 <li v-show="!isMobile || !isCardsCollapsed" class="list-group-item jn-list-group-item" 169 :class="{ 'dark-mode': isDarkMode }"><span class="jn-text">ð¢ 170 {{currentTexts.ipInfos.ISP}}</span>: {{ card.isp }} 171 </li> 172 <li v-show="!isMobile || !isCardsCollapsed" class="list-group-item jn-list-group-item" 173 :class="{ 'dark-mode': isDarkMode }"><span class="jn-text">ð¶ 174 {{currentTexts.ipInfos.ASN}}</span>: <a v-if="card.asnlink" :href="card.asnlink" 175 target="_blank">{{ 176 card.asn }}</a><a v-else>{{ 177 card.asn 178 }}</a></li> 179 </ul> 180 </div> 181 182 </div> 183 </div> 184 </div> 185 </div> 186 </div> 187 188 <!-- Connectivity --> 189 <div class="availability-test-section mb-4"> 190 <div class="jn-title2"> 191 <h2 id="Connectivity" :class="{'mobile-h2' : isMobile }">ð¦ {{currentTexts.connectivity.Title}}</h2> 192 <button @click="checkAllConnectivity(false)" 193 :class="['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']"><i 194 class="bi bi-arrow-clockwise"></i></button> 195 </div> 196 <div class="text-secondary"> 197 <p>{{currentTexts.connectivity.Note}}</p> 198 </div> 199 <div class="row"> 200 <div v-for="test in connectivityTests" :key="test.id" class="col-6 col-md-3 mb-4"> 201 <div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 202 <div class="card-body"> 203 <h5 class="card-title"><i class="bi" :class="'bi-'+test.icon"></i> {{ test.name }}</h5> 204 205 <p class="card-text" 206 :class="{'text-info': test.status === currentTexts.connectivity.StatusWait, 'text-success': test.status.includes(currentTexts.connectivity.StatusAvailable), 'text-danger': test.status === currentTexts.connectivity.StatusUnavailable || test.status === currentTexts.connectivity.StatusTimeout}" 207 v-html="test.status"> 208 </p> 209 </div> 210 </div> 211 </div> 212 </div> 213 </div> 214 215 <!-- WebRTC Test --> 216 <div class="webrtc-test-section mb-4"> 217 <div class="jn-title2"> 218 <h2 id="WebRTC" :class="{'mobile-h2' : isMobile }">ð¥ {{currentTexts.webrtc.Title}}</h2> 219 <button @click="checkAllWebRTC" :class="['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']"><i 220 class="bi bi-arrow-clockwise"></i></button> 221 </div> 222 <div class="text-secondary"> 223 <p>{{currentTexts.webrtc.Note}}</p> 224 </div> 225 <div class="row"> 226 <div v-for="stun in stunServers" :key="stun.id" class="col-6 col-md-3 mb-4"> 227 <div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 228 <div class="card-body"> 229 <h5 class="card-title"><i class="bi bi-sign-merge-left-fill"></i> {{ stun.name }}</h5> 230 <p class="card-text text-secondary" style="font-size: 10pt;">{{ stun.url }}</p> 231 <p class="card-text" 232 :class="{'text-info': stun.ip === currentTexts.webrtc.StatusWait, 'text-success': stun.ip.includes('.'|':'), 'text-danger': stun.ip === currentTexts.webrtc.StatusError}" 233 v-html="stun.ip"></p> 234 </p> 235 </div> 236 </div> 237 </div> 238 </div> 239 </div> 240 241 <!-- DNS Leaks Test --> 242 <div class="dnsleak-test-section mb-4"> 243 <div class="jn-title2"> 244 <h2 id="DNSLeakTest" :class="{'mobile-h2' : isMobile }">ð {{currentTexts.dnsleaktest.Title}}</h2> 245 <button @click="checkAllDNSLeakTest" 246 :class="['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']"><i 247 class="bi bi-arrow-clockwise"></i></button> 248 </div> 249 <div class="text-secondary"> 250 <p>{{currentTexts.dnsleaktest.Note}}</p> 251 <p>{{currentTexts.dnsleaktest.Note2}}</p> 252 </div> 253 <div class="row"> 254 <div v-for="leak in leakTest" :key="leak.id" class="col-6 col-md-3 mb-4"> 255 <div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 256 <div class="card-body"> 257 <h5 class="card-title"><i class="bi bi-server"></i> {{ leak.name }}</h5> 258 <p class="card-text text-secondary" style="font-size: 10pt;">{{currentTexts.dnsleaktest.Endpoint}}: {{ 259 leak.ip }}</p> 260 <p class="card-text" :class="[ 261 'text-success', 262 {'text-info': leak.geo === currentTexts.dnsleaktest.StatusWait, 'text-danger': leak.geo.includes('
262China') || leak.geo.includes('china')} 263 ]">{{currentTexts.dnsleaktest.EndpointCountry}}: {{ leak.geo }}</p> 264 </p> 265 </div> 266 </div> 267 </div> 268 </div> 269 </div> 270 271 272 <!-- Speed Test --> 273 <div class="speed-test-section mb-4"> 274 <div class="jn-title2"> 275 <h2 id="SpeedTest" :class="{'mobile-h2' : isMobile }">ð {{currentTexts.speedtest.Title}}</h2> 276 <button @click="startSpeedTest" :class="['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']" 277 :disabled="speedTestStatus === 'running'"> 278 <i :class="speedTestStatus === 'running' ? 'bi bi-slash-circle' : 'bi bi-caret-right-fill'"></i> 279 </button> 280 281 </div> 282 <div class="text-secondary"> 283 <p>{{currentTexts.speedtest.Note}}</p> 284 </div> 285 <div class="row"> 286 <div class="col-12"> 287 <div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 288 <div class="card-body"> 289 <div class="progress" style="height: 20px; margin: 4pt 0 10pt 0;" 290 :class="{ 'jn-opacity-0': speedTestStatus == 'idle', 'jn-progress-dark': isDarkMode }"> 291 <div class="progress-bar progress-bar-striped progress-bar-animated bg-success jn-progress" 292 role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" 293 id="speedtest-progress"> 294 </div> 295 </div> 296 <div class="row" v-if="speedTestStatus === 'finished'"> 297 <p id="score" class="speedtest-p">ð {{currentTexts.speedtest.score}} 298 {{currentTexts.speedtest.videoStreaming}} 299 <span :class="speedTest.streamingScore >= 50 ? 'text-success' : 'text-warning'"> 300 {{speedTest.streamingScore}} 301 </span> 302 {{currentTexts.speedtest.gaming}} 303 <span :class="speedTest.gamingScore >= 50 ? 'text-success' : 'text-warning'"> 304 {{speedTest.gamingScore}} 305 </span> 306 {{currentTexts.speedtest.rtc}} 307 <span :class="speedTest.rtcScore >= 50 ? 'text-success' : 'text-warning'"> 308 {{speedTest.rtcScore}} 309 </span> 310 {{currentTexts.speedtest.resultNote}} 311 </p> 312 </div> 313 314 <div class="row" style="margin-bottom: 10pt;"> 315 <div class="col-3 text-center"> 316 <h5 class="speedtest-h5">{{currentTexts.speedtest.Download}}</h5> 317 <p id="download-speed" class="speedtest-h5" :class="updateSpeedTestColor(speedTestStatus)"> 318 {{speedTest.downloadSpeed}} 319 <br />Mb/s 320 </p> 321 </div> 322 <div class="col-3 text-center"> 323 <h5 class="speedtest-h5">{{currentTexts.speedtest.Upload}}</h5> 324 <p id="upload-speed" class="speedtest-h5" :class="updateSpeedTestColor(speedTestStatus)"> 325 {{speedTest.uploadSpeed}} <br />Mb/s 326 </p> 327 </div> 328 <div class="col-3 text-center"> 329 <h5 class="speedtest-h5">{{currentTexts.speedtest.Latency}}</h5> 330 <p id="latency" class="speedtest-h5" :class="updateSpeedTestColor(speedTestStatus)"> 331 {{speedTest.latency}} <br />ms</p> 332 </div> 333 <div class="col-3 text-center"> 334 <h5 class="speedtest-h5">{{currentTexts.speedtest.Jitter}}</h5> 335 <p id="jitter" class="speedtest-h5" :class="updateSpeedTestColor(speedTestStatus)"> 336 {{speedTest.jitter}} <br />ms</p> 337 </div> 338 </div> 339 <div id="result"></div> 340 </div> 341 </div> 342 </div> 343 </div> 344 </div> 345 346 347 348 349 </div> 350 </div> 351 352 <!-- Info Mask BTN--> 353 <button v-if="isInfosLoaded" class="btn position-fixed" 354 :class="infoMaskLevel === 0 ? 'btn-success' : infoMaskLevel === 1 ? 'btn-warning' : 'btn-secondary'" 355 style="bottom: 66px; right: 20px; z-index: 1050;" @click="toggleInfoMask"> 356 <i :class="infoMaskLevel === 0 ? 'bi bi-eye' : 'bi bi-eye-slash'"></i> 357 </button> 358 359 <!-- Search BTN --> 360 <button class="btn btn-primary position-fixed" style="bottom: 20px; right: 20px; z-index: 1050;" 361 data-bs-toggle="modal" data-bs-target="#IPCheck"><i class="bi bi-search"></i></button> 362 363 <!-- Search Modal --> 364 <div class="modal fade" id="IPCheck" tabindex="-1" aria-labelledby="IPCheck" aria-hidden="true"> 365 <div class="modal-dialog modal-dialog-centered"> 366 <div class="modal-content" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 367 <div class="modal-header" :class="{ 'dark-mode-border': isDarkMode }"> 368 <h5 class="modal-title" id="IPCheck">{{currentTexts.ipcheck.Title}}</h5> 369 <button type="button" class="btn-close" :class="{ 'dark-mode-close-button': isDarkMode }" 370 data-bs-dismiss="modal" aria-label="Close"></button> 371 372 </div> 373 <div class="modal-body" :class="{ 'dark-mode': isDarkMode }"> 374 <input type="text" class="form-control mb-2" :class="{ 'dark-mode': isDarkMode }" 375 :placeholder="currentTexts.ipcheck.Placeholder" v-model="inputIP" @keyup.enter="submitQuery"> 376 <div v-if="modalQueryError" class="text-danger">{{ modalQueryError }}</div> 377 <div v-if="modalQueryResult" class="mt-2"> 378 <div class="card-body"> 379 <ul class="list-group list-group-flush"> 380 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }"><span 381 class="jn-text">ð¥ï¸ {{currentTexts.ipInfos.IP}}</span>: {{ 382 modalQueryResult.ip }} 383 </li> 384 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }">
385 <span class="jn-text">ð {{currentTexts.ipInfos.Country}}</span>: {{ 386 modalQueryResult.country_name }} <span v-if="modalQueryResult.country_code" 387 :class="'flag-icon flag-icon-' + modalQueryResult.country_code.toLowerCase()"></span> 388 </li> 389 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }"><span 390 class="jn-text">ðï¸ {{currentTexts.ipInfos.Region}}</span>: {{ 391 modalQueryResult.region 392 }}</li> 393 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }"><span 394 class="jn-text">ð {{currentTexts.ipInfos.City}}</span>: {{ 395 modalQueryResult.city }} 396 </li> 397 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }"><span 398 class="jn-text">ð¢ {{currentTexts.ipInfos.ISP}}</span>: {{ 399 modalQueryResult.isp }} 400 </li> 401 <li class="list-group-item jn-list-group-item" :class="{ 'dark-mode': isDarkMode }"><span 402 class="jn-text">ð¶ {{currentTexts.ipInfos.ASN}}</span>: <a v-if="modalQueryResult.asnlink" 403 :href="modalQueryResult.asnlink" target="_blank">{{ 404 modalQueryResult.asn }}</a><a v-else>{{ modalQueryResult.asn 405 }}</a></li> 406 </ul> 407 </div> 408 409 </div> 410 </div> 411 <div class="modal-footer" :class="{ 'dark-mode-border': isDarkMode }"> 412 <button type="button" class="btn btn-primary" 413 :class="{ 'btn-secondary': !isValidIP(inputIP), 'btn-primary': isValidIP(inputIP) }" @click="submitQuery" 414 :disabled="!isValidIP(inputIP)">{{currentTexts.ipcheck.Button}}</button> 415 416 </div> 417 418 </div> 419 </div> 420 </div> 421 422 <!-- BingMap Modal --> 423 <div class="modal fade" id="addBingMapKey" tabindex="-1" aria-labelledby="addBingMapKey" aria-hidden="true"> 424 <div class="modal-dialog modal-dialog-centered"> 425 <div class="modal-content" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 426 <div class="modal-header" :class="{ 'dark-mode-border': isDarkMode }"> 427 <h5 class="modal-title" id="addBingMapKey">{{currentTexts.addBingMapKey.Title}}</h5> 428 <button type="button" class="btn-close" :class="{ 'dark-mode-close-button': isDarkMode }" 429 data-bs-dismiss="modal" aria-label="Close"></button> 430 431 </div> 432 <div class="modal-body" :class="{ 'dark-mode': isDarkMode }"> 433 <p class="fw-light">{{ currentTexts.addBingMapKey.Note }}</p> 434 <input type="text" class="form-control mb-2" :class="{ 'dark-mode': isDarkMode }" 435 :placeholder="currentTexts.addBingMapKey.Placeholder" v-model="inputBingMapAPIKEY" 436 @keyup.enter="addBingMapKey"> 437 <div v-if="bingMapAPIKEYError" class="text-danger">{{ currentTexts.addBingMapKey.Error }}</div> 438 439 440 </div> 441 <div class="modal-footer" :class="{ 'dark-mode-border': isDarkMode }"> 442 <button v-if="bingMapAPIKEY" type="button" class="btn btn-danger" 443 @click="removeBingMapKey">{{currentTexts.addBingMapKey.removeButton}}</button> 444 445 <button type="button" class="btn btn-primary" 446 :class="{ 'btn-secondary': !isValidKey(inputBingMapAPIKEY), 'btn-primary': isValidKey(inputBingMapAPIKEY) }" 447 @click="addBingMapKey" 448 :disabled="!isValidKey(inputBingMapAPIKEY)">{{currentTexts.addBingMapKey.Button}}</button> 449 450 </div> 451 452 </div> 453 </div> 454 </div> 455 456 <!-- help modal --> 457 <div class="modal fade" id="helpModal" tabindex="-1" aria-labelledby="helpModal" aria-hidden="true"> 458 <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable"> 459 <div class="modal-content" :class="{ 'dark-mode dark-mode-border': isDarkMode }"> 460 <div class="modal-header" :class="{ 'dark-mode-border': isDarkMode }"> 461 <h5 class="modal-title" id="helpModal">{{currentTexts.helpModal.Title}}</h5> 462 <button type="button" class="btn-close" :class="{ 'dark-mode-close-button': isDarkMode }" 463 data-bs-dismiss="modal" aria-label="Close"></button> 464 465 </div> 466 <div class="modal-body" :class="{'dark-mode' : isDarkMode }"> 467 <p class="jn-help-note "><i class="bi bi-hand-thumbs-up-fill"></i> {{currentTexts.shortcutKeys.HelpNote}} 468 </p>
469 <div v-for="(key, index) in keyMap" :key="key.keys" class="row p-2 jn-shortcut-row" 470 :class="[ isDarkMode ? 'border-dark-subtle jn-dark-mode-help-border' : 'border-light-subtle', index === keyMap.length - 1 ? 'jn-border-bottom-none' : '' ]"> 471 <div class="col"> 472 <kbd :class="{ 'text-bg-light': isDarkMode }"> 473 {{ key.keys.replace(/[\[\]\\\(\)]/g, '') }} 474 </kbd> 475 </div> 476 <div class="col-8">{{ key.description }}</div> 477 </div> 478 </div> 479 </div> 480 </div> 481 </div> 482 483 484 <footer> 485 <p class="text-center">{{currentTexts.page.footerName}} <a :href="currentTexts.page.footerLink" class="link-dark" 486 target="_blank"><i class="bi bi-github" :class="{ 'dark-mode': isDarkMode }"></i></a> 487 </p> 488 </footer> 489 490 </div> 491 492 493
493<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js" 494 integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" 495 crossorigin="anonymous"></script>
495 496 497 <!-- å¼å ¥ Vue.js --> 498
498<script src="res/vue.js"></script>
498 499
499<script type="module" src="res/app.js"></script>
499 500 <!--
500<script type="module" src="res/cfSpeedTest.js"></script>
500 --> 501 502
502<script> 503 if ('serviceWorker' in navigator) { 504 window.addEventListener('load', function () { 505 navigator.serviceWorker.register('service-worker.js').then(function (registration) { 506 console.log('ServiceWorker Success : ', registration.scope); 507 }, function (err) { 508 console.log('ServiceWorker Error: ', err); 509 }); 510 }); 511 } 512 </script>
512 513
514<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"eea42aa2a4a0421b81d77a89273043a3","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 514
514
515<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a4668d02bd612af4',t:'MTc5MTMwOTQ3MQ=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
515</body> 516 517</html>
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.