1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> 5 <meta http-equiv="Pragma" content="no-cache" /> 6 <meta http-equiv="Expires" content="0" /> 7 <meta charset="utf-8"> 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 <title>Start Creating Beam</title> 10 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css"> 11 <link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/wenzhixin/multiple-select/e14b36de/multiple-select.css"> 12 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha512-SfTiTlX6kk+qitfevl/7LibUOeJWlt9rbyDn92a1DqWOw9vWG2MFoays0sgObmWazO5BQPiFucnnEAjpAB+/Sw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> 13 <link rel="stylesheet" href="fonts/icomoon/style.css"> 14 <link href="css/style.css" rel="stylesheet"> 15 <style> 16 video,canvas{ 17 margin: auto; 18 } 19 20 </style> 21 </head> 22 <body> 23 24 <div class="site-mobile-menu site-navbar-target" style="border-top-left-radius: 15px;"> 25 <div class="site-mobile-menu-header"> 26 <div class="site-mobile-menu-close mt-3"> 27 <span class="icon-close2 js-menu-toggle"></span> 28 </div> 29 </div> 30 <div class="site-mobile-menu-body"></div> 31 </div> 32 33 <header class="site-navbar site-navbar-target py-4" role="banner"> 34 <div class="container"> 35 <div class="row align-items-center position-relative"> 36 <div class="col-md-11"> 37 <ul class="progressbar"> 38 <li id="beam-step-1-tab" class="active">Record Beam</li> 39 <li id="beam-step-2-tab" class="">Select Beam Thumbnail</li> 40 <li id="beam-step-3-tab" class="">Recipient Detail</li> 41 <li id="beam-step-4-tab" class="">Send Beam</li> 42 </ul> 43 </div> 44 <div class="col-md-1 text-right"> 45 </div> 46 </div> 47 48 </div> 49 </header> 50 51 <div class="content"> 52 <div class="container"> 53 <div class="row"> 54 <div class="col-sm-12 site-brand"> 55 <img src="images/salesbeam.png" alt="" class="img-fluid"> 56 </div> 57 </div> 58 59 <div id="beam-mobile" class="row" style="display: none;"> 60 <div class="col-md-10 ml-auto mr-auto"> 61 <div class="card create-beam"> 62 <div class="row"> 63 <div class="col-md-12 text-center"> 64 <h2>The website demo is not available on the <span id="mobile-type">Iphone</span>. 65 <br>To use SalesBeam on your mobile phone please download the SalesBeam app.</h2> 66 </div> 67 </div> 68 <div class="row"> 69 70 <div class="wrapper"> 71 <a id="ios-mobile" href="https://apps.apple.com/mx/app/salesbeam/id1671438306" class="link-wrapper" style="display: none;"> 72 <div class="img-wrapper"> 73 <img src="images/apple-logo.png"> 74 </div> 75 <div class="content-wrapper"> 76 <h6>Download in the</h6> 77 <p>App Store</p> 78 </div> 79 </a> 80 81 <a id="android-mobile" href="https://play.google.com/store/apps/details?id=io.salesbeam.app" class="link-wrapper" style="display: none;"> 82 <div class="img-wrapper"> 83 <img src="images/google-play.png"> 84 </div> 85 <div class="content-wrapper"> 86 <h6>Android app on</h6> 87 <p>Google Play</p> 88 </div> 89 </a> 90 </div> 91 92 </div> 93 </div> 94 </div> 95 </div> 96 97 <div id="beam-step-2" class="row" style="display: none;"> 98 <div class="col-md-10 ml-auto mr-auto"> 99 <div class="card create-beam"> 100 <div class="row"> 101 <div class="col-md-12 text-center"> 102 <h2>Record your Beam</h2> 103 </div> 104 </div> 105 <div class="row"> 106 107 <div class="col-md-12 top30"> 108 <p class="text-center">Beams are up to 90 seconds</p> 109 <div class="text-center"> 110 <div id="countdown" style=" position: absolute;z-index: 999;font-size: 150px;left: 0;right: 0;height: fit-content;margin: auto;width: fit-content;top: 0;color: #f5f5f5;bottom: 0;"></div> 111 <video id="preview" width="615" height="460" autoplay muted style="display:none;"></video>
112 <canvas id="preview-canvas" style="display: none;"> 113 </div> 114 <div class="col-md-12 top30 text-center"> 115 <div id="beamVideoTimer" style="display: none;">Recording <span></span> seconds</div> 116 </div> 117 <div class="col-md-12 top30 text-center"> 118 <div id="startButton" class="btn btn-primary" style="display: none;">Start Recording</div> 119 <div id="stopButton" class="btn btn-primary" style="display: none;">Stop Recording</div> 120 </div> 121 <div class="col-md-12 top30 text-center"> 122 <button id="blur-btn" type="button" class="btn btn-info" style="padding: 5px 20px;min-height: auto;min-width: auto;margin: auto;">Blur Background</button> 123 <button id="unblur-btn" type="button" class="btn btn-light" style="padding: 5px 20px;min-height: auto;min-width: auto;display:none;margin: auto;">UnBlur Background</button> 124 </div> 125 </div> 126 127 </div> 128 </div> 129 </div> 130 </div> 131 132 <div id="beam-step-3" class="row" style="display: none;"> 133 <div class="col-lg-10 ml-auto mr-auto"> 134 <div class="card create-beam"> 135 <div class="row"> 136 <div class="col-md-12 text-center"> 137 <h2>Beam</h2> 138 </div> 139 </div> 140 <div class="row"> 141 142 <div class="col-md-12"> 143 <video id="recording" width="100%" height="auto" controls class="vdo-full"> 144 Your browser does not support the video tag. 145 </video> 146 </div> 147 <div id="preview-beam-box" class="col-md-12 top30 text-center"> 148 <p>Click play button to preview your Beam.</p> 149 150 <button type="button" id="recreate-your-beam" class="btn btn-outline">Re-Record Your Beam</button> 151 <button type="button" id="send-beam-next" class="btn btn-primary">Next</button> 152 </div> 153 154 </div> 155 </div> 156 </div> 157 </div> 158 159 <div id="beam-step-4" class="row" style="display: none;"> 160 <div class="col-lg-7 ml-auto mr-auto"> 161 <div class="card"> 162 <div class="row"> 163 <div class="col-md-12 text-center"> 164 <h2 style="border-bottom: 1px solid #000000;margin-bottom: 15px;">Select Your Beam Thumbnail</h2> 165 </div> 166 </div> 167 <div id="thumbnails-container" class="row"></div> 168 <h3 class="line-break"><span>OR</span></h3> 169 170 <!-- Upload image input--> 171 <div class="input-group mb-3 px-2 py-2 rounded-pill bg-white shadow-sm"> 172 <input id="upload" type="file" class="form-control border-0"> 173 <label id="upload-label" for="upload" class="font-weight-light text-muted">File name</label> 174 <div class="input-group-append"> 175 <label for="upload" class="btn btn-light m-0 rounded-pill px-4"> <i class="fa fa-cloud-upload mr-2 text-muted"></i><small class="text-uppercase font-weight-bold text-muted">Choose file</small></label> 176 </div> 177 </div> 178 <!-- Uploaded image area--> 179 <p id="upload-thumb-text" class="font-italic text-center">The image uploaded can be your default thumbnail (disabled in demo).</p> 180 <div id="upload-container" class="image-area row"></div> 181 182 <h3 class="line-break"><span>OR</span></h3> 183 <video id="take-picture-player" autoplay style="display:none; width: inherit;"></video> 184 <div id="takenPicture-container" class="row"></div> 185 <div class="col-md-12 top30 text-center"> 186 <button type="button" id="take-picture-button" class="btn btn-outline">Take Picture</button> 187 </div> 188 189 <div class="col-md-12 top30 text-center"> 190 <button type="button" id="go-to-step-5" class="btn btn-primary">Next</button> 191 </div> 192 </div> 193 </div> 194 </div> 195 196 <div id="beam-step-5" class="row" style="display: none;"> 197 <div class="col-lg-7 ml-auto mr-auto"> 198 <div class="card"> 199 <div class="col-md-12 text-left"> 200 <a class="back-to-step4" href="#"><i class="fa fa-arrow-left" aria-hidden="true"></i></a> 201 </div> 202 <div class="row"> 203 <div class="col-md-12 text-center"> 204 <h2>Select Recipient(s)</h2> 205 </div> 206 </div> 207 <div class="row"> 208 <div class="col-md-12 top30"><button id="recipient-detail-btn" type="button" class="btn btn-primary full">Customers & Prospects</button></div> 209 <div class="col-md-12 top30"><button id="co-worker-btn" type="button" class="btn btn-primary full">Co-Worker(s)</button></div> 210 </div> 211 </div> 212 </div> 213 </div> 214 215 <div id="beam-step-6-co-worker" class="row" style="display: none;"> 216 <div class="col-lg-6 ml-auto mr-auto"> 217 <div class="card"> 218 <div class="row"> 219 <div class="col-md-12 text-left"> 220 <a class="back-to-step5" href="#" style="font-size: 30px;"><i class="fa fa-arrow-left" aria-hidden="true"></i></a> 221 </div> 222 </div> 223 <div class="row"> 224 <div class="col-md-12 text-center"> 225 <h2>Co-Worker</h2> 226 <p>Please enter recipient details below.</p> 227 </div> 228 <div id="co-worker-errors" class="col-md-12 text-center"></div> 229 </div> 230 <div class="row"> 231 <div class="col-md-12"> 232 <label for="">Select department 233 <select id="department-list" class="form-select"> 234 <option value="">No selection</option> 235 <option value="1">Test Department 1</option> 236 <option value="2">Test Department 2</option> 237 <option value="3">Test Department 3</option> 238 </select> 239 </label> 240 </div> 241 <div class="col-md-12"> 242 <label for="">Select category 243 <select id="category-list" class="form-select"> 244 <option value="">No selection</option> 245 <option value="1">Test Category 1</option> 246 <option value="2">Test Category 2</option> 247 <option value="3">Test Category 3</option> 248 </select> 249 </label> 250 </div> 251 <div class="col-md-12"> 252 <label for="">Select employee(s) 253 <select id="nominee-list" class="form-select" multiple> 254 <option value="1">Test Employee 1</option> 255 <option value="2">Test Employee 2</option> 256 <option value="3">Test Employee 3</option> 257 </select> 258 </label> 259 </div> 260 261 <div class="col-md-12 top30"><button id="create-beam-co-worker" type="button" class="btn btn-primary full">Send Beam</button></div> 262 263 </div> 264 </div> 265 </div> 266 </div> 267 268 <div id="beam-step-6-recipient-detail" class="row" style="display: none;"> 269 <div class="col-lg-10 ml-auto mr-auto"> 270 <div class="card"> 271 <div class="row"> 272 <div class="col-md-12 text-left"> 273 <a class="back-to-step5" href="#" style="font-size: 30px;"><i class="fa fa-arrow-left" aria-hidden="true"></i></a> 274 </div> 275 </div> 276 <div class="row"> 277 <div class="col-md-12 text-center"> 278 <h2>Customers & Prospects</h2> 279 <p>Please enter recipient details below.</p> 280 </div> 281 <div id="recipient-detail-errors" class="col-md-12 text-center"></div> 282 </div> 283 <div class="row"> 284 <div class="col-md-6"> 285 <label for="">The subject line 286 <input id="recipient-subject" type="text" class="form-control" placeholder="subject"> 287 </label> 288 </div> 289 <div class="col-md-6"> 290 <label for="">Email address 291 <input id="recipient-email" type="email" class="form-control" placeholder="email"> 292 </label> 293 </div> 294 <div class="col-md-6"> 295 <label for="">Recipient's name 296 <input id="recipient-name" type="text" class="form-control" placeholder="name"> 297 </label> 298 </div> 299 <div class="col-md-6"> 300 <label for="">Name your beam (optional) 301 <input id="beam-name" type="text" class="form-control" placeholder="name of your beam"> 302 </label> 303 </div> 304 <div id="recipient-container" class="container"> 305 306 </div> 307 <div class="col-md-12"> 308 <label class="checkbox-inline"> 309 <a id="add-recipient" href="#"><img src="images/icon-add.png" class="add" alt="">Add Recipient</a> 310 </label> 311 <label class="checkbox-inline"> 312 <input id="beam-attach-file" type="checkbox" value="1">Attach a file. 313 </label> 314 <label class="checkbox-inline"> 315 <input id="beam-is-cc" type="checkbox" value="1">CC the Beam to your CRM. 316 </label> 317 </div> 318 319 <div id="display-attach-file" class="col-md-12" style="display:none;"> 320 <label for="">File 321 <input id="beam-file" type="file" class="form-control"> 322 </label> 323 </div> 324 325 <div class="col-md-12 top30 text-center"><button id="create-beam-recipient-detail" type="button" class="btn btn-primary half">Send Beam</button></div> 326 327 </div> 328 </div> 329 </div> 330 </div> 331 332 <div id="beam-step-7-sent" class="row" style="display: none;"> 333 <div class="col-lg-9 ml-auto mr-auto"> 334 <div id="go-home" class="card" style="display:none;"> 335 <div class="row"> 336 <div class="col-md-12 text-center"> 337 <h2 style="font-size: 35px;">Beam Created!</h2> 338 <div class="col-md-12"> 339 <p style="font-size: 20px;color: #858181;text-align: left;">
340 Thank you for trying SalesBeam - please copy and paste the link below to share your Beam. In order to enable automated email click <a href="https://salesbeam.io/web/register-free.html">here</a> to register for a SalesBeam trial. 341 </p> 342 </div> 343 <div id="beam-url-container" class="row" style="padding-bottom: 10px; margin-bottom: 8px;"> 344 <div class="col-md-8"> 345 <input id="beam-url" type="text" class="form-control" readonly=""> 346 </div> 347 <div class="col-md-4"> 348 <button id="copy-beam-url" type="button" class="btn btn-primary half" onclick="copyBeamUrl()" style="min-width: inherit;min-height: inherit;padding-left: inherit;padding-right: inherit;width: inherit;">Copy</button> 349 </div> 350 </div> 351 </div> 352 </div> 353 <div class="row"> 354 <div class="col-md-12 top30"><button id="go-home-btn" type="button" class="btn btn-outline full">Return to Start</button></div> 355 </div> 356 </div> 357 </div> 358 </div> 359 360 </div> 361 </div> 362 363 364
364<script src="https://code.jquery.com/jquery-3.6.1.js"></script>
364 365
365<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.min.js"></script>
365 366
366<script src="https://cdn.rawgit.com/wenzhixin/multiple-select/e14b36de/multiple-select.js"></script>
366 367
367<script src="js/jquery.sticky.js"></script>
367 368 369
369<script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]"></script>
369 370
370<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]"></script>
370 371 372
372<script src="js/main.js"></script>
372 373 <!--script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.min.js"></script>
373 374
374<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script--> 375 376
376<script> 377 378 var isMobile = false; 379 var userAgent = navigator.userAgent || navigator.vendor || window.opera; 380 381 if (/android/i.test(userAgent)) { 382 //window.location.href = "https://play.google.com/store/apps/details?id=io.salesbeam.app"; 383 $('#mobile-type').html("Android"); 384 $('#beam-mobile').show(); 385 $('#android-mobile').show(); 386 isMobile = true; 387 } 388 389 // iOS detection from: http://stackoverflow.com/a/9039885/177710 390 if ( (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) 391 || (navigator.maxTouchPoints && 392 navigator.maxTouchPoints > 2 && 393 /MacIntel/.test(navigator.platform)) 394 ) { 395 //window.location.href = "https://apps.apple.com/mx/app/salesbeam/id1671438306"; 396 $('#mobile-type').html("Iphone"); 397 $('#beam-mobile').show(); 398 $('#ios-mobile').show(); 399 isMobile = true; 400 } 401 402 if(!isMobile){ 403 let companyId = 1; 404 let employeeId = 1; 405 406 let apiWaUrl = "https://salesbeam.io:2096/"; 407 408 let step2 = document.getElementById("beam-step-2"); 409 let step3 = document.getElementById("beam-step-3"); 410 411 let preview = document.getElementById("preview"); 412 let recording = document.getElementById("recording"); 413 let startButton = document.getElementById("startButton"); 414 let stopButton = document.getElementById("stopButton"); 415 let beamVideoTimer = document.getElementById("beamVideoTimer"); 416//let downloadButton = document.getElementById("downloadButton"); 417 let logElement = document.getElementById("log"); 418 const blurBtn = document.getElementById('blur-btn'); 419 const unBlurBtn = document.getElementById('unblur-btn'); 420 const previewCanvas = document.getElementById('preview-canvas'); 421 const previeCanvasCtx = previewCanvas.getContext('2d'); 422 let mediaRecorderVideo = null; 423 424 let playerPicture = document.getElementById("take-picture-player"); 425 let capturePictureButton = document.getElementById("take-picture-button"); 426 let takenPictureContainer = document.getElementById("takenPicture-container"); 427 let imageCapturePicture; 428 let pictureStream; 429 let takingPicture = false; 430 let takenPicture = false; 431 let takenPictureIndex = null; 432 433 let timerI = 0; 434 let runningTimer = false; 435 let countTimer = null; 436 let countDownTimer = null; 437 438 let recordingTimeMS = 91000; 439 let recordedBlob = ''; 440 let recordedBlobMp4 = ''; 441 let chunks = []; 442 443 let beamType = ''; 444 let thumbnailsBlob = []; 445 446 let salesbeam_steps = [ 447 "beam-step-2", 448 "beam-step-3", 449 "beam-step-4", 450 "beam-step-5", 451 "beam-step-6-co-worker", 452 "beam-step-6-recipient-detail", 453 "beam-step-7-sent", 454 ]; 455 456 let salesbeam_tabs = [ 457 "beam-step-1-tab", 458 "beam-step-2-tab", 459 "beam-step-3-tab", 460 "beam-step-4-tab" 461 ]; 462 463 let beam_attach_file_element = document.getElementById('beam-file'); 464 let beam_attach_file_blob = ''; 465 466 function loadCamera(){ 467 preview.style.display = 'block'; 468 startButton.style.display = 'initial'; 469 navigator.mediaDevices 470 .getUserMedia({ 471 video: true, 472 audio: true, 473 }) 474 .then((stream) => { 475 mediaRecorderVideo = new MediaRecorder(stream, { mimeType : 'video/webm;codecs=h264' }); 476 window.mediaStream = stream; 477 window.mediaRecorder = mediaRecorderVideo; 478 479 preview.srcObject = stream; 480 preview.play(); 481 preview.captureStream = preview.captureStream || preview.mozCaptureStream; 482 return new Promise((resolve) => (preview.onplaying = resolve)); 483 }) 484 .catch((error) => { 485 startButton.style.display = 'none'; 486 if (error.name === "NotFoundError") { 487 alert("Camera or microphone not found. Can't record."); 488 } else { 489 console.log(error); 490 } 491 }); 492 } 493 494 function takePicture(){ 495 playerPicture.style.display = 'block'; 496 takenPictureContainer.style.display = 'none'; 497 navigator.mediaDevices 498 .getUserMedia({ 499 video: true, 500 audio: false, 501 })
502 .then((stream) => { 503 pictureStream = stream; 504 playerPicture.srcObject = pictureStream; 505 const trackPicture = stream.getVideoTracks()[0]; 506 imageCapturePicture = new ImageCapture(trackPicture); 507 }) 508 .catch((error) => { 509 startButton.style.display = 'none'; 510 if (error.name === "NotFoundError") { 511 alert("Camera or microphone not found. Can't record."); 512 } else { 513 console.log(error); 514 } 515 }); 516 } 517 518 function capturePicture(){ 519 imageCapturePicture.takePhoto() 520 .then(blob => createImageBitmap(blob)) 521 .then(imageBitmap => { 522 let canvas = document.createElement("canvas"); 523 let tempId = null; 524 if(!takenPicture){ 525 tempId = thumbnailsBlob.length; 526 takenPicture = true; 527 takenPictureIndex = tempId; 528 }else{ 529 tempId = takenPictureIndex; 530 } 531 canvas.width = imageBitmap.width; 532 canvas.height = imageBitmap.height; 533 $('canvas').removeClass("active"); 534 canvas.classList.add('active'); 535 canvas.setAttribute("id", tempId); 536 let ctx = canvas.getContext('2d') 537 ctx.drawImage(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height); 538 539 ctx.canvas.toBlob( 540 blob => { 541 thumbnailsBlob[tempId] = blob; 542 }, 543 "image/jpeg", 544 0.75 /* quality */ 545 ); 546 console.log(thumbnailsBlob); 547 let div = document.createElement("div"); 548 div.classList.add('thumbnail-div','col-md-12'); 549 div.appendChild(canvas); 550 551 let divcheck = document.createElement("div"); 552 divcheck.classList.add('form-check'); 553 554 div.appendChild(divcheck); 555 556 document.getElementById('takenPicture-container').innerHTML = ""; 557 document.getElementById('takenPicture-container').appendChild(div); 558 playerPicture.style.display = 'none'; 559 takenPictureContainer.style.display = 'block'; 560 playerPicture.pause(); 561 playerPicture.src = ""; 562 pictureStream.getTracks()[0].stop(); 563 }) 564 .catch(error => ChromeSamples.log(error)); 565 } 566 567 capturePictureButton.addEventListener('click', () => { 568 if(!takingPicture){
569 takePicture(); 570 takingPicture = true; 571 }else{ 572 capturePicture(); 573 takingPicture = false; 574 } 575 }); 576 577 function readFile(event) { 578 beam_attach_file_blob = event.target.result; 579 } 580 581 function changeFile() { 582 var file = beam_attach_file_element.files[0]; 583 var reader = new FileReader(); 584 reader.addEventListener('load', readFile); 585 reader.readAsText(file); 586 } 587 588 beam_attach_file_element.addEventListener('change', changeFile); 589 590 function copyBeamUrl() { 591 let copyText = document.getElementById("beam-url"); 592 copyText.select(); 593 document.execCommand("Copy"); 594 } 595 596 function videoTimer(timer = true) { 597 console.log('enter timer') 598 if (countTimer === null) { 599 beamVideoTimer.style.display = 'initial'; 600 runningTimer = true; 601 $("#beamVideoTimer span").html(timerI); 602 let tempTimerCounter = 0; 603 $('canvas').removeClass("active"); 604 countTimer = setInterval(function () { 605 timerI++; 606/////////////////// 607 if(timerI == 1 || (timerI % 5) == 0){ 608 const track = mediaStream.getVideoTracks()[0]; 609 let imageCapture = new ImageCapture(track); 610 imageCapture.grabFrame() 611 .then(imageBitmap => { 612 let canvas = document.createElement("canvas"); 613 canvas.width = imageBitmap.width; 614 canvas.height = imageBitmap.height; 615 if(timerI == 1){ 616 canvas.classList.add('active'); 617 } 618 canvas.setAttribute("id", tempTimerCounter); 619 let ctx = canvas.getContext('2d') 620 ctx.drawImage(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height); 621 622 ctx.canvas.toBlob( 623 blob => { 624 thumbnailsBlob[tempTimerCounter] = blob; 625 }, 626 "image/jpeg", 627 0.75 /* quality */ 628 ); 629 console.log(thumbnailsBlob); 630 let div = document.createElement("div"); 631 div.classList.add('thumbnail-div','col-md-6'); 632 div.appendChild(canvas); 633 document.getElementById('thumbnails-container').appendChild(div); 634 }) 635 .catch(error => console.log(error)); 636 tempTimerCounter++; 637 } 638////////////////// 639 $("#beamVideoTimer span").html(timerI); 640 }, 1000); 641 } else { 642 runningTimer = false; 643 clearInterval(countTimer); 644 timerI = 0; 645 $("#beamVideoTimer span").html(timerI); 646 countTimer = null; 647 } 648 } 649 650 function wait(delayInMS) { 651 return new Promise((resolve) => setTimeout(resolve, delayInMS)); 652 } 653 654 function startRecording(lengthInMS) { 655 //let data = []; 656 657 videoTimer(); 658 659 if(blurBtn.hidden){ 660 const audioTracks = window.mediaStream.getAudioTracks(); 661 var videoStream = previewCanvas.captureStream(30); 662 videoStream.addTrack(audioTracks[0]); 663 window.mediaRecorder = new MediaRecorder(videoStream); 664 }else{ 665 window.mediaRecorder = mediaRecorderVideo; 666 } 667 668 window.mediaRecorder.start(); 669 mediaRecorder.ondataavailable = (e) => { 670 chunks.push(e.data); 671 }; 672 673 let stopped = new Promise((resolve, reject) => { 674 window.mediaRecorder.onstop = async(evt) => { 675 videoTimer(false); 676 console.log( "duration:" + await measureDuration( chunks )); 677 //download(chunks); 678 //recordedBlobTest = new Blob(chunks, { type: "video/mp4" }); 679 chunks = []; 680 //recording.src = URL.createObjectURL(recordedBlobTest); 681 }; 682 683 window.mediaRecorder.onerror = (event) => reject(event.name); 684 685 }); 686 687 let recorded = wait(lengthInMS).then(() => { 688 console.log('enter default stop'); 689 console.log(runningTimer); 690 if (window.mediaRecorder.state === "recording" && timerI > 89) { 691 stopVideo(preview.srcObject); 692 console.log('apply default stop'); 693 } 694 }); 695 696 return Promise.all([stopped, recorded]).then(() => chunks); 697 698 } 699 700 function stopVideo(stream) { 701 window.mediaRecorder.stop();
702 window.mediaStream.getTracks().forEach((track) => track.stop()); 703//////////////// 704 step2.style.display = 'none'; 705 step3.style.display = 'block'; 706 preview.load(); 707///// 708 } 709 710 startButton.addEventListener( 711 "click", 712 (e) => { 713 e.preventDefault(); 714 //document.getElementById("#startButton").disabled = true; 715 var timeleft = 3; 716 countDownTimer = setInterval(function(){ 717 if(timeleft <= 0){ 718 clearInterval(countDownTimer); 719 document.getElementById("countdown").innerHTML = ""; 720 startButton.style.display = 'none'; 721 stopButton.style.display = 'initial'; 722 startRecording(recordingTimeMS); 723 }else{ 724 document.getElementById("countdown").innerHTML = timeleft; 725 } 726 timeleft -= 1; 727 }, 1000); 728 }, 729 false 730 ); 731 732 stopButton.addEventListener( 733 "click", 734 () => { 735 stopVideo(preview.srcObject); 736 }, 737 false 738 ); 739 740 /*========================================== 741 SHOW UPLOADED IMAGE 742 * ========================================== */ 743 744 let uploadedThumbnail = false; 745 let uploadedThumbnailIndex = null; 746 747 function readURL(input) { 748 if (input.files && input.files[0]) { 749 file = input.files[0]; 750 751 createImageBitmap(file).then( 752 function(imageBitmap) { 753 let canvas = document.createElement("canvas"); 754 console.log("length"); 755 console.log(thumbnailsBlob.length); 756 let tempId = null; 757 if(!uploadedThumbnail){ 758 tempId = thumbnailsBlob.length; 759 uploadedThumbnail = true; 760 uploadedThumbnailIndex = tempId; 761 }else{ 762 tempId = uploadedThumbnailIndex; 763 } 764 canvas.width = imageBitmap.width; 765 canvas.height = imageBitmap.height; 766 $('canvas').removeClass("active"); 767 canvas.classList.add('active'); 768 canvas.setAttribute("id", tempId); 769 let ctx = canvas.getContext('2d') 770 ctx.drawImage(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height); 771 772 ctx.canvas.toBlob( 773 blob => { 774 thumbnailsBlob[tempId] = blob; 775 }, 776 "image/jpeg", 777 0.75 /* quality */ 778 ); 779 console.log(thumbnailsBlob); 780 let div = document.createElement("div"); 781 div.classList.add('thumbnail-div','col-md-12'); 782 div.appendChild(canvas); 783 784 let divcheck = document.createElement("div"); 785 divcheck.classList.add('form-check'); 786 787 div.appendChild(divcheck); 788 789 document.getElementById('upload-container').innerHTML = ""; 790 document.getElementById('upload-container').appendChild(div); 791 }, 792 function(error) { 793 console.log(error); 794 }, 795 ) 796 797 } 798 } 799 800 $(function () { 801 $('#upload').on('change', function () { 802 readURL(input); 803 $('#upload-thumb-text').html("The image uploaded can be your default thumbnail."); 804 }); 805 }); 806 807 /*========================================== 808 SHOW UPLOADED IMAGE NAME 809 * ========================================== */ 810 var input = document.getElementById( 'upload' ); 811 var infoArea = document.getElementById( 'upload-label' ); 812 813 input.addEventListener( 'change', showFileName ); 814 function showFileName( event ) { 815 var input = event.srcElement; 816 var fileName = input.files[0].name; 817 infoArea.textContent = 'File name: ' + fileName; 818 } 819 820 $(document).ready(function(){ 821 822 loadCamera(); 823 loadBodyPix(); 824 goToStep("beam-step-2",'1'); 825 826 $("#recipient-detail-btn").click(function(e){ 827 e.preventDefault(); 828 829 beamType = 'recipient-detail'; 830 goToStep("beam-step-6-recipient-detail",'3'); 831 832 }); 833 834 $("#co-worker-btn").click(function(e){ 835 e.preventDefault(); 836 837 beamType = 'co-worker'; 838 839 goToStep("beam-step-6-co-worker",'3'); 840 841 }); 842 843 $("#create-beam-recipient-detail").click(function(e){ 844 845 let thumbnailId = $("#beam-step-4").find(".active").attr('id'); 846 let sendThumbnail = thumbnailsBlob[thumbnailId]; 847 $('#recipient-detail-errors').html(''); 848 let recipientsEmailsError = false; 849 let recipientsNamesError = false; 850 851 if($('#recipient-subject').val() == ''){ 852 $('#recipient-detail-errors').html('<div class="alert alert-danger" role="alert"> \ 853 Please enter subject \ 854 </div>'); 855 return; 856 } 857 858 if($('#recipient-email').val() == ''){ 859 $('#recipient-detail-errors').html('<div class="alert alert-danger" role="alert"> \ 860 Please enter email \ 861 </div>'); 862 return; 863 } 864 865 if($('#recipient-name').val() == ''){ 866 $('#recipient-detail-errors').html('<div class="alert alert-danger" role="alert"> \ 867 Please enter name \ 868 </div>'); 869 return; 870 } 871 872 $('.recipients-emails').each(function(index){ 873 if(!$(this).val() || $(this).val() == ''){ 874 $('#recipient-detail-errors').html('<div class="alert alert-danger" role="alert"> \ 875 Please enter recipient emails \ 876 </div>'); 877 recipientsEmailsError = true; 878 return; 879 } 880 }); 881 882 $('.recipients-names').each(function(index){ 883 if(!$(this).val() || $(this).val() == ''){ 884 $('#recipient-detail-errors').html('<div class="alert alert-danger" role="alert"> \ 885 Please enter recipient names \ 886 </div>'); 887 recipientsNamesError = true; 888 return; 889 } 890 }); 891 892 if(recipientsNamesError || recipientsEmailsError){ 893 return; 894 } 895 896 if($('#beam-attach-file').is(":checked") && $('#beam-file').get(0).files.length === 0){ 897 $('#recipient-detail-errors').html('<div class="alert alert-danger" role="alert"> \ 898 Please enter a file \ 899 </div>'); 900 return; 901 } 902 903 let is_attach_file = $('#beam-attach-file').is(":checked") == '1' ? '1' : '0'; 904 let is_attach_file_ext = $('#beam-attach-file').is(":checked") == '1' ? $('#beam-attach-file').val().split('.').pop() : ''; 905 let is_cc = $('#beam-is-cc').is(":checked") == '1' ? '1' : '0'; 906$.ajax({
907 type: 'POST', 908 async: false, 909 url: "https://salesbeam.io/wa/video-converter.php", 910 // Content type must much with the parameter you signed your URL with 911 contentType: 'binary/octet-stream', 912 // this flag is important, if not set, it will try to send data as a form 913 processData: false, 914 // the actual file is sent raw 915 data: recordedBlob, 916 success: function(dataFile, textStatus, xhr) { 917 mp4Url = dataFile; 918 fetch(mp4Url) 919 .then(res => res.blob()) // Gets the response and returns it as a blob 920 .then(blob => { 921 recordedBlobMp4 = blob; 922 $.post(apiWaUrl + "createBeamPresignedUrl", 923 { 924 companyId:1, 925 employeeId:1, 926 vedeoExtension:'mp4', 927 isAttachment: is_attach_file, 928 attachmentExtension: is_attach_file_ext 929 }, function(data, status) { 930 if(data.status == 1){ 931 932 if(is_attach_file == '1'){ 933 $.ajax({
934 type: 'PUT', 935 async: false, 936 url: data.data.attachmentPresignedUrl, 937 // Content type must much with the parameter you signed your URL with 938 contentType: 'binary/octet-stream', 939 // this flag is important, if not set, it will try to send data as a form 940 processData: false, 941 // the actual file is sent raw 942 data: beam_attach_file_blob, 943 success: function(dataFile, textStatus, xhr) { 944 console.log(dataFile); 945 }, 946 error: function(xhr, textStatus, errorThrown){ 947 alert('File NOT uploaded'); 948 console.log( textStatus); 949 } 950 }); 951 } 952 953 $.ajax({ 954 type: 'PUT', 955 async: false, 956 url: data.data.thumbPresignedUrl, 957 // Content type must much with the parameter you signed your URL with 958 contentType: 'binary/octet-stream', 959 // this flag is important, if not set, it will try to send data as a form 960 processData: false, 961 // the actual file is sent raw 962 data: sendThumbnail, 963 success: function(dataFile, textStatus, xhr) { 964 console.log(dataFile); 965 }, 966 error: function(xhr, textStatus, errorThrown){ 967 alert('File NOT uploaded'); 968 console.log( textStatus); 969 } 970 }); 971 972 $.ajax({
973 type: 'PUT', 974 async: false, 975 url: data.data.videoPresignedUrl, 976 // Content type must much with the parameter you signed your URL with 977 contentType: 'binary/octet-stream', 978 // this flag is important, if not set, it will try to send data as a form 979 processData: false, 980 // the actual file is sent raw 981 data: recordedBlobMp4, 982 success: function(dataFile, textStatus, xhr) { 983 console.log(dataFile); 984 985 let dataBeam = { 986 companyId:1, 987 employeeId:1, 988 subject: $('#recipient-subject').val(), 989 beamName: $('#beam-name').val(), 990 isAttachment: is_attach_file, 991 isCc: 0, 992 recipientName1: $('#recipient-name').val(), 993 recipientEmail1: "[email protected]", 994 attachmentFileName: data.data.attachmentFileName, 995 videoFileName: data.data.videoFileName, 996 thumbFileName: data.data.thumbFileName 997 }; 998 999 $.ajax({
1000 url: apiWaUrl + "createBeamWithoutFileWeb", 1001 type: "POST", 1002 async: false, 1003 data: dataBeam, 1004 success: function(dataBeam, textStatusBeam, xhrBeam) { 1005 console.log(dataBeam); 1006 $('#beam-url').val(dataBeam.message); 1007 beam_loaded(true); 1008 }, 1009 error: function(xhrBeam, textStatusBeam, errorThrownBeam){ 1010 1011 } 1012 }); 1013 }, 1014 error: function(xhr, textStatus, errorThrown){ 1015 alert('File NOT uploaded'); 1016 console.log( textStatus); 1017 } 1018 }); 1019 1020 } 1021 }); 1022 }); 1023 }, 1024 error: function(xhr, textStatus, errorThrown){ 1025 alert('File NOT uploaded'); 1026 console.log( textStatus); 1027 } 1028}); 1029//return; 1030 1031/////////////////////////////// 1032 1033 goToStep("beam-step-7-sent",'4'); 1034 1035 }); 1036 1037 $('#create-beam-co-worker').click(function(e){ 1038 e.preventDefault(); 1039 let thumbnailId = $("#beam-step-4").find(".active").attr('id'); 1040 let sendThumbnail = thumbnailsBlob[thumbnailId]; 1041 1042 console.log("sendThumbnail"); 1043 console.log(sendThumbnail); 1044 1045 $('#co-worker-errors').html(''); 1046 if($('#department-list').val() == ''){ 1047 $('#co-worker-errors').html('<div class="alert alert-danger" role="alert"> \ 1048 Please select a department \ 1049 </div>'); 1050 return; 1051 } 1052 1053 if($('#category-list').val() == ''){ 1054 $('#co-worker-errors').html('<div class="alert alert-danger" role="alert"> \ 1055 Please select a category \ 1056 </div>'); 1057 return; 1058 } 1059 1060 if($('#nominee-list').val() == ''){ 1061 $('#co-worker-errors').html('<div class="alert alert-danger" role="alert"> \ 1062 Please select a employee \ 1063 </div>'); 1064 return; 1065 } 1066 1067///////////////////////////// 1068 1069 $.post(apiWaUrl + "recognizePresignedUrl", 1070 { 1071 companyId:1, 1072 employeeId:1, 1073 vedeoExtension:'mp4', 1074 isAttachment: '0', 1075 attachmentExtension: '' 1076 }, function(data, status) { 1077 if(data.status == 1){ 1078 1079 $.ajax({
1080 type: 'PUT', 1081 async: false, 1082 url: data.data.thumbPresignedUrl, 1083 // Content type must much with the parameter you signed your URL with 1084 contentType: 'binary/octet-stream', 1085 // this flag is important, if not set, it will try to send data as a form 1086 processData: false, 1087 // the actual file is sent raw 1088 data: sendThumbnail, 1089 success: function(dataFile, textStatus, xhr) { 1090 console.log(dataFile); 1091 }, 1092 error: function(xhr, textStatus, errorThrown){ 1093 alert('File NOT uploaded'); 1094 console.log( textStatus); 1095 } 1096 }); 1097 1098 $.ajax({ 1099 type: 'PUT', 1100 async: false, 1101 url: data.data.videoPresignedUrl, 1102 // Content type must much with the parameter you signed your URL with 1103 contentType: 'binary/octet-stream', 1104 // this flag is important, if not set, it will try to send data as a form 1105 processData: false, 1106 // the actual file is sent raw 1107 data: recordedBlob, 1108 success: function(dataFile, textStatus, xhr) { 1109 console.log(dataFile); 1110 1111 let nominee_list = $("#nominee-list option:selected").map(function () { 1112 return $(this).val(); 1113 }).get().join(','); 1114 let dataBeam = { 1115 companyId:1, 1116 employeeId:1, 1117 departmentId: $('#department-list').val(), 1118 employeeEmail: '[email protected]', 1119 categoryId: $('#category-list').val(), 1120 videoFileName: data.data.videoFileName, 1121 thumbFileName: data.data.thumbFileName, 1122 nomineeIds: nominee_list, 1123 nominationType: 'V', 1124 note: "", 1125 ccTo: "" 1126 }; 1127 1128 $.ajax({
1129 url: apiWaUrl + "recognizeWithoutFile", 1130 type: "POST", 1131 async: false, 1132 data: dataBeam, 1133 success: function(dataBeam, textStatusBeam, xhrBeam) { 1134 console.log(dataBeam); 1135 beam_loaded(false); 1136 }, 1137 error: function(xhrBeam, textStatusBeam, errorThrownBeam){ 1138 1139 } 1140 }); 1141 1142 }, 1143 error: function(xhr, textStatus, errorThrown){ 1144 alert('File NOT uploaded'); 1145 console.log( textStatus); 1146 } 1147 }); 1148 1149 } 1150 }); 1151////////////////////////// 1152 goToStep("beam-step-7-sent",'4'); 1153 1154 }); 1155 1156 $("#recreate-your-beam").click(function(e){ 1157 e.preventDefault(); 1158 create_beam(); 1159 goToStep("beam-step-2",'1'); 1160 1161 }); 1162 1163 $("#go-to-step-5").click(function(e){ 1164 e.preventDefault(); 1165 goToStep("beam-step-5",'3'); 1166 1167 }); 1168 1169 $("#send-beam-next").click(function(e){ 1170 e.preventDefault(); 1171 goToStep("beam-step-4",'2'); 1172 }); 1173 1174 $("#add-recipient").click(function(e){ 1175 e.preventDefault(); 1176 1177 $("#recipient-container").append('<div class="recipient-element row"> \ 1178 <hr style="width: 100%;"> \ 1179 <div class="col-md-6"> \ 1180 <label for="">Name \ 1181 <input type="text" class="form-control recipients-names" placeholder="name"> \ 1182 </label> \ 1183 </div> \ 1184 <div class="col-md-6"> \ 1185 <label for="">Email \ 1186 <input type="email" class="form-control recipients-emails" placeholder="email"> \ 1187 </label> \ 1188 </div> \ 1189 <div class="col-md-12"> \ 1190 <a href="" class="remove-recipient">Remove</a> \ 1191 </div> \ 1192 </div>'); 1193 1194 }); 1195 1196 $("#beam-attach-file").change(function(e){ 1197 1198 if(this.checked) { 1199 $('#display-attach-file').show(); 1200 }else{ 1201 $('#beam-file').val(''); 1202 $('#display-attach-file').hide(); 1203 } 1204 1205 }); 1206 1207 $("#go-home-btn").click(function(e){ 1208 /* 1209 clear_video(); 1210 beam_loaded_restart(); 1211 goToStep("beam-step-2",'1'); 1212 */ 1213 location.reload(); 1214 }); 1215 1216 $('.back-to-step2').click(function(e){ 1217 e.preventDefault(); 1218 loadCamera(); 1219 goToStep("beam-step-2",'1'); 1220 1221 }); 1222 1223 $('.back-to-step4').click(function(e){ 1224 e.preventDefault(); 1225 goToStep("beam-step-4",'2'); 1226 1227 }); 1228 1229 $('.back-to-step5').click(function(e){ 1230 e.preventDefault(); 1231 goToStep("beam-step-5",'3'); 1232 1233 }); 1234 /* 1235 $('#back-to-step3').click(function(e){ 1236 e.preventDefault(); 1237 1238 if(beamType == 'recipient-detail'){ 1239 goToStep("beam-step-3-recipient-detail",'3'); 1240 }else{ 1241 goToStep("beam-step-3-co-worker",'3'); 1242 } 1243 1244 }); 1245 */ 1246 1247 }); 1248 1249 $('body').on('click', '.remove-recipient', function(e) { 1250 e.preventDefault(); 1251 $(this).parent().parent().remove(); 1252 }); 1253 1254 $('body').on('click', 'canvas', function() { 1255 if ($(this).hasClass("active")) { 1256 return; 1257 } 1258 $('canvas').removeClass("active"); 1259 $(this).addClass("active"); 1260 }); 1261 1262 let activeClass = 'active'; 1263 let visitedClass = 'visited'; 1264 1265 function tabClassActive(tabId){ 1266 let tab = $('#' + tabId); 1267 if (!tab.hasClass(activeClass)) { 1268 tab.addClass(activeClass); 1269 } 1270 if (tab.hasClass(visitedClass)) { 1271 tab.removeClass(visitedClass); 1272 } 1273 } 1274 1275 function tabClassVisited(tabId){ 1276 let tab = $('#' + tabId); 1277 if (!tab.hasClass(activeClass)) { 1278 tab.addClass(activeClass); 1279 } 1280 if (!tab.hasClass(visitedClass)) { 1281 tab.addClass(visitedClass); 1282 } 1283 } 1284 1285 function tabClassInit(tabId){ 1286 let tab = $('#' + tabId); 1287 if (tab.hasClass(activeClass)) { 1288 tab.removeClass(activeClass); 1289 } 1290 if (tab.hasClass(visitedClass)) { 1291 tab.removeClass(visitedClass); 1292 } 1293 } 1294 1295 function goToStep(step, tab){ 1296 $.each(salesbeam_steps, function(index, value){ 1297 if(value == step){ 1298 $('#' + step).show(); 1299 }else{ 1300 $('#' + value).hide(); 1301 } 1302 }); 1303 1304 $.each(salesbeam_tabs, function(index, value){ 1305 let currentTab = 'beam-step-' + tab + '-tab'; 1306 let currentTabIndex = +index +1; 1307 if(value == currentTab){ 1308 tabClassActive(currentTab); 1309 }else if(currentTabIndex < tab){ 1310 tabClassVisited(value); 1311 }else{ 1312 tabClassInit(value); 1313 } 1314 }); 1315 } 1316 1317 function create_beam(){ 1318 clear_video(); 1319 loadCamera(); 1320 } 1321 1322 function clear_video(){ 1323 $('#startButton').show(); 1324 $('#preview').hide(); 1325 $('#stopButton').hide(); 1326 $('#beamVideoTimer').hide(); 1327 recordedBlob = ''; 1328 } 1329 1330 function beam_loaded(showUrl){ 1331 $('#loading-beam').hide(); 1332 $('#go-home').show(); 1333 if(showUrl){ 1334 $('#beam-url-container').show(); 1335 }else{ 1336 $('#beam-url-container').hide(); 1337 } 1338 } 1339 1340 function beam_loaded_restart(){ 1341 $('#loading-beam').show(); 1342 $('#go-home').hide(); 1343 } 1344 1345 /***** 1346 * Blur 1347 *****/ 1348 preview.onplaying = () => { 1349 previewCanvas.height = preview.videoHeight; 1350 previewCanvas.width = preview.videoWidth; 1351 }; 1352 1353 blurBtn.addEventListener('click', e => { 1354 blurBtn.hidden = true; 1355 unBlurBtn.hidden = false; 1356 blurBtn.style.display = 'none'; 1357 unBlurBtn.style.display = 'block'; 1358 1359 preview.hidden = true; 1360 previewCanvas.hidden = false; 1361 preview.style.display = 'none'; 1362 previewCanvas.style.display = 'block'; 1363 1364 loadBodyPix(); 1365 }); 1366 1367 unBlurBtn.addEventListener('click', e => { 1368 blurBtn.hidden = false; 1369 unBlurBtn.hidden = true; 1370 blurBtn.style.display = 'block'; 1371 unBlurBtn.style.display = 'none'; 1372 1373 preview.hidden = false; 1374 previewCanvas.hidden = true; 1375 preview.style.display = 'block'; 1376 previewCanvas.style.display = 'none'; 1377 }); 1378 1379 function loadBodyPix() { 1380 options = { 1381 multiplier: 0.75,
1382 stride: 32, 1383 quantBytes: 4 1384 } 1385 bodyPix.load(options) 1386 .then(net => perform(net)) 1387 .catch(err => console.log(err)) 1388 } 1389 1390 async function perform(net) { 1391 //while (blurBtn.hidden) { 1392 while (true) { 1393 const segmentation = await net.segmentPerson(preview); 1394 1395 const backgroundBlurAmount = 8; 1396 const edgeBlurAmount = 2; 1397 const flipHorizontal = false; 1398 1399 bodyPix.drawBokehEffect( 1400 previewCanvas, preview, segmentation, backgroundBlurAmount, 1401 edgeBlurAmount, flipHorizontal); 1402 } 1403 } 1404 1405 function measureDuration( chunks ) { 1406 recordedBlob = new Blob( chunks, { type: "video/webm" } ); 1407 const vid = document.createElement( 'video' ); 1408 return new Promise( (res, rej) => { 1409 recording.onerror = rej; 1410 recording.onseeked = (evt) => res( recording.duration ); 1411 recording.onloadedmetadata = (evt) => { 1412 URL.revokeObjectURL( recording.src ); 1413 // for demo only, to show it's Infinity in Chrome 1414 console.log( 'before seek', recording.duration ); 1415 }; 1416 recording.src = URL.createObjectURL( recordedBlob ); 1417 recording.currentTime = 1e10; 1418 1419 } ); 1420 } 1421 1422 function download(blobData) { 1423 const url = window.URL.createObjectURL(blobData); 1424 1425 } 1426 } 1427 </script>
1427 1428 1429
1429<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":"1d855d887b924bf1b8e7add9620123a5","r":1,"spa":2}' crossorigin="anonymous"></script>
1429 1430</body> 1431</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.