| const S = `0${dt.getSeconds()}`.slice(-2) | const S = `0${dt.getSeconds()}`.slice(-2) | ||||
| return `${H}:${M}:${S}` | return `${H}:${M}:${S}` | ||||
| } | } | ||||
| const blockIndent = (text, iStart, iEnd, nLevels) => { | |||||
| // prop | |||||
| const startLine = text.slice(0, iStart).split('\n').length - 1 | |||||
| const endLine = text.slice(0, iEnd).split('\n').length - 1 | |||||
| const newText = text | |||||
| .split('\n') | |||||
| .map((line, i) => { | |||||
| if(i < startLine || i > endLine || nLevels === 0) { | |||||
| newLine = line | |||||
| } | |||||
| else if(nLevels > 0) { | |||||
| newLine = line.replace(/^/, ' ') | |||||
| } | |||||
| else if(nLevels < 0) { | |||||
| newLine = line.replace(/^ /, '') | |||||
| } | |||||
| if(i === startLine) { | |||||
| iStart = iStart + newLine.length - line.length | |||||
| } | |||||
| iEnd = iEnd + newLine.length - line.length | |||||
| return newLine | |||||
| }) | |||||
| .join('\n') | |||||
| return [newText, Math.max(0, iStart), Math.max(0, iEnd)] | |||||
| } | |||||
| const hotKey = (e) => { | const hotKey = (e) => { | ||||
| // if isDesktop, Enter posts, unless Shift+Enter | // if isDesktop, Enter posts, unless Shift+Enter | ||||
| // use isLandscape as proxy for isDesktop | // use isLandscape as proxy for isDesktop | ||||
| e.preventDefault() | e.preventDefault() | ||||
| Chat.sendPost() | Chat.sendPost() | ||||
| } | } | ||||
| // indent and dedent | |||||
| const modKey = e.ctrlKey || e.metaKey | |||||
| const {value: text, selectionStart: A, selectionEnd: B} = textbox | |||||
| if(e.key === 'Tab') { | |||||
| e.preventDefault() | |||||
| const regex = new RegExp(`([\\s\\S]{${A}})([\\s\\S]{${B - A}})`) | |||||
| textbox.value = text.replace(regex, (m, a, b) => a + ' '.repeat(4)) | |||||
| textbox.setSelectionRange(A + 4, A + 4) | |||||
| } | |||||
| if(']['.includes(e.key) && modKey) { | |||||
| e.preventDefault() | |||||
| const nLevels = {']': 1, '[': -1}[e.key] | |||||
| const [newText, newA, newB] = blockIndent(text, A, B, nLevels) | |||||
| textbox.value = newText | |||||
| textbox.setSelectionRange(newA, newB) | |||||
| } | |||||
| } | } | ||||
| const Video = { | const Video = { | ||||
| appendStream: ({username, stream}) => ({dom}) => { | appendStream: ({username, stream}) => ({dom}) => { |