1(function (window, document) { 2 const videoOpts = { first: true } 3 4 function setVideoOptions () { 5 if (!videoOpts.width) { 6 const opts = JSON.parse( 7 document.getElementById('xaringanExtra-webcam-options').textContent 8 ) 9 videoOpts.width = opts.width || 200 10 videoOpts.height = opts.height || 200 11 videoOpts.margin = opts.margin || '1em' 12 } 13 } 14 15 function createVideoElement () { 16 setVideoOptions() 17 const vid = document.createElement('video') 18 vid.setAttribute('autoplay', true) 19 vid.setAttribute('draggable', true) 20 vid.id = 'xaringanExtra-webcam' 21 vid.width = videoOpts.width 22 vid.height = videoOpts.height 23 vid.style.position = 'absolute' 24 vid.style.top = 0 25 vid.style.right = 0 26 vid.style.margin = videoOpts.margin 27 vid.style.zIndex = 1000 28 vid.style.transform = 'rotateY(180deg)' 29 vid.style.cursor = 'move' 30 return vid 31 } 32 33 function findVideo () { 34 return document.getElementById('xaringanExtra-webcam') 35 } 36 37 function alertNoVideo () { 38 let msg = 'Webcam is not available in this browser.' 39 if (!/^https/.test(window.location.protocol)) { 40 msg += '\n\nAre you using https for this site?' 41 } 42 window.alert(msg) 43 } 44 45 function startVideo () { 46 if (document.body.classList.contains('remark-presenter-mode')) { 47 return 48 } 49 50 if (findVideo()) { 51 return 52 } 53 54 if (!navigator.mediaDevices) { 55 alertNoVideo() 56 return 57 } 58 59 navigator 60 .mediaDevices 61 .getUserMedia({ 62 audio: false, 63 video: { 64 facingMode: 'user', 65 width: { ideal: videoOpts.width }, 66 height: { ideal: videoOpts.height } 67 } 68 }) 69 .then(function (stream) { 70 const vid = createVideoElement() 71 videoOpts.el = vid 72 document.body.appendChild(vid) 73 74 vid.addEventListener('dragstart', dragStart, false) 75 document.body.addEventListener('dragover', dragOver, false) 76 document.body.addEventListener('drop', dragDrop, false) 77 vid.srcObject = stream 78 }) 79 .catch(err => { 80 if (videoOpts.first) { 81 alertNoVideo() 82 videoOpts.first = false 83 } 84 console.error(err) 85 }) 86 } 87 88 function stopVideo () { 89 const vid = findVideo() 90 if (!vid) return 91 vid.srcObject.getTracks().forEach(track => track.stop()) 92 vid.parentNode.removeChild(vid) 93 videoOpts.el = null 94 document.body.removeEventListener('dragover', dragOver) 95 document.body.removeEventListener('drop', dragDrop) 96 } 97 98 function toggleVideo () { 99 findVideo() ? stopVideo() : startVideo() 100 } 101 102 function moveVideo (top = '0px', right = '0px', bottom = null, left = null) { 103 videoOpts.el.style.top = top 104 videoOpts.el.style.right = right 105 videoOpts.el.style.bottom = bottom 106 videoOpts.el.style.left = left 107 } 108 109 function moveVideoNextCorner () { 110 const { top, right, bottom, left } = videoOpts.el.style 111 if (top === '0px' && right === '0px') { 112 moveVideo(null, '0px', '0px', null) 113 } else if (right === '0px' && bottom === '0px') { 114 moveVideo(null, null, '0px', '0px') 115 } else if (bottom === '0px' && left === '0px') { 116 moveVideo('0px', null, null, '0px') 117 } else if (top === '0px' && left === '0px') { 118 moveVideo('0px', '0px', null, null) 119 } else { 120 moveVideo() 121 } 122 } 123 124 function dragStart (event) { 125 const { clientX, clientY } = event 126 const style = window.getComputedStyle(event.target, null) 127 const x = parseInt(style.getPropertyValue('left'), 10) - clientX 128 const y = parseInt(style.getPropertyValue('top'), 10) - clientY 129 event.dataTransfer.setData('text', `${x},${y}`) 130 } 131 132 function dragOver (event) { 133 event.preventDefault() 134 return false 135 } 136 137 function dragDrop (event) { 138 let offset = event.dataTransfer.getData('text') 139 offset = offset.split(',').map(x => parseInt(x, 10)) 140 const { clientX, clientY } = event 141 moveVideo(`${clientY + offset[1]}px`, null, null, `${clientX + offset[0]}px`) 142 event.preventDefault() 143 return false 144 } 145 146 document.addEventListener('keydown', function (ev) { 147 if (ev.code === 'KeyW') { 148 ev.shiftKey ? moveVideoNextCorner() : toggleVideo() 149 } 150 }) 151})(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.