Make term work with webpack

box-drawing
cpsdqs 7 years ago
parent 540c93a4bd
commit ef249ebc79
Signed by untrusted user: cpsdqs
GPG Key ID: 3F59586BB7448DD1
  1. 2
      .eslintrc
  2. 3
      _build_js.sh
  3. 2
      dump_js_lang.php
  4. 28
      js/appcommon.js
  5. 6
      js/debug_screen.js
  6. 3
      js/demo.js
  7. 70
      js/event_emitter.js
  8. 12
      js/index.js
  9. 2
      js/lang.js
  10. 74
      js/modal.js
  11. 106
      js/notif.js
  12. 7
      js/soft_keyboard.js
  13. 25
      js/term.js
  14. 16
      js/term_conn.js
  15. 5
      js/term_input.js
  16. 40
      js/term_screen.js
  17. 14
      js/term_upload.js
  18. 50
      js/utils.js
  19. 18
      js/wifi.js
  20. 3
      package.json
  21. 6
      pages/term.php

@ -135,7 +135,7 @@
"no-unsafe-finally": "error", "no-unsafe-finally": "error",
"no-unsafe-negation": "error", "no-unsafe-negation": "error",
"no-unused-expressions": ["warn", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }], "no-unused-expressions": ["warn", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }],
"no-unused-vars": ["error", { "vars": "local", "args": "none", "ignoreRestSiblings": true }], "no-unused-vars": ["warn", { "vars": "local", "args": "none", "ignoreRestSiblings": true }],
"no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }], "no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }],
"no-useless-call": "error", "no-useless-call": "error",
"no-useless-computed-key": "error", "no-useless-computed-key": "error",

@ -6,6 +6,9 @@ echo 'Generating lang.js...'
php ./dump_js_lang.php php ./dump_js_lang.php
echo 'Processing JS...' echo 'Processing JS...'
npm run webpack
exit
if [[ $ESP_PROD ]]; then if [[ $ESP_PROD ]]; then
smarg= smarg=
demofile= demofile=

@ -18,5 +18,5 @@ foreach ($selected as $key) {
file_put_contents(__DIR__. '/js/lang.js', file_put_contents(__DIR__. '/js/lang.js',
"// Generated from PHP locale file\n" . "// Generated from PHP locale file\n" .
'let _tr = ' . json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) . ";\n\n" . 'let _tr = ' . json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) . ";\n\n" .
"function tr (key) { return _tr[key] || '?' + key + '?' }\n" "module.exports = function tr (key) { return _tr[key] || '?' + key + '?' }\n"
); );

@ -1,4 +1,20 @@
const $ = require('./lib/chibi') const $ = require('./lib/chibi')
const { mk, qs } = require('./utils')
const modal = require('./modal')
const notify = require('./notif')
/**
* Filter 'spacebar' and 'return' from keypress handler,
* and when they're pressed, fire the callback.
* use $(...).on('keypress', cr(handler))
*/
function cr (hdl) {
return function (e) {
if (e.which === 10 || e.which === 13 || e.which === 32) {
hdl()
}
}
}
/** Global generic init */ /** Global generic init */
$.ready(function () { $.ready(function () {
@ -60,8 +76,8 @@ $.ready(function () {
val -= step val -= step
} }
if (undef(min)) val = Math.max(val, +min) if (!Number.isFinite(min)) val = Math.max(val, +min)
if (undef(max)) val = Math.min(val, +max) if (!Number.isFinite(max)) val = Math.min(val, +max)
$this.val(val) $this.val(val)
if ('createEvent' in document) { if ('createEvent' in document) {
@ -96,8 +112,8 @@ $.ready(function () {
qs('.Box.errors').classList.remove('hidden') qs('.Box.errors').classList.remove('hidden')
} }
Modal.init() modal.init()
Notify.init() notify.init()
// remove tabindices from h2 if wide // remove tabindices from h2 if wide
if (window.innerWidth > 550) { if (window.innerWidth > 550) {
@ -108,7 +124,7 @@ $.ready(function () {
// brand works as a link back to term in widescreen mode // brand works as a link back to term in widescreen mode
let br = qs('#brand') let br = qs('#brand')
br && br.addEventListener('click', function () { br && br.addEventListener('click', function () {
location.href = '/' // go to terminal window.location.href = '/' // go to terminal
}) })
} }
}) })
@ -124,6 +140,8 @@ function showPage () {
pageShown = true pageShown = true
$('#content').addClass('load') $('#content').addClass('load')
} }
// HACKITY HACK: fix this later
window.showPage = showPage
// Auto reveal pages other than the terminal (sets window.noAutoShow) // Auto reveal pages other than the terminal (sets window.noAutoShow)
$.ready(function () { $.ready(function () {

@ -1,4 +1,6 @@
window.attachDebugScreen = function (screen) { const { mk } = require('./utils')
module.exports = function attachDebugScreen (screen) {
const debugCanvas = mk('canvas') const debugCanvas = mk('canvas')
const ctx = debugCanvas.getContext('2d') const ctx = debugCanvas.getContext('2d')
@ -73,7 +75,7 @@ window.attachDebugScreen = function (screen) {
let isDrawing = false let isDrawing = false
let drawLoop = function () { let drawLoop = function () {
if (isDrawing) requestAnimationFrame(drawLoop) if (isDrawing) window.requestAnimationFrame(drawLoop)
let { devicePixelRatio, width, height } = screen.window let { devicePixelRatio, width, height } = screen.window
let { width: cellWidth, height: cellHeight } = screen.getCellSize() let { width: cellWidth, height: cellHeight } = screen.getCellSize()

@ -1,3 +1,6 @@
const EventEmitter = require('events')
const { encode2B, encode3B, parse2B } = require('./utils')
class ANSIParser { class ANSIParser {
constructor (handler) { constructor (handler) {
this.reset() this.reset()

@ -1,70 +0,0 @@
if (!('EventEmitter' in window)) {
window.EventEmitter = class EventEmitter {
constructor () {
this._listeners = {}
}
/**
* Bind an event listener to an event
* @param {string} event - the event name
* @param {Function} listener - the event listener
*/
on (event, listener) {
if (!this._listeners[event]) this._listeners[event] = []
this._listeners[event].push({ listener })
}
/**
* Bind an event listener to be run only once the next time the event fires
* @param {string} event - the event name
* @param {Function} listener - the event listener
*/
once (event, listener) {
if (!this._listeners[event]) this._listeners[event] = []
this._listeners[event].push({ listener, once: true })
}
/**
* Remove an event listener
* @param {string} event - the event name
* @param {Function} listener - the event listener
*/
off (event, listener) {
let listeners = this._listeners[event]
if (listeners) {
for (let i in listeners) {
if (listeners[i].listener === listener) {
listeners.splice(i, 1)
break
}
}
}
}
/**
* Emits an event
* @param {string} event - the event name
* @param {...any} args - arguments passed to all listeners
*/
emit (event, ...args) {
let listeners = this._listeners[event]
if (listeners) {
let remove = []
for (let listener of listeners) {
try {
listener.listener(...args)
if (listener.once) remove.push(listener)
} catch (err) {
console.error(err)
}
}
// this needs to be done in this roundabout way because for loops
// do not like arrays with changing lengths
for (let listener of remove) {
listeners.splice(listeners.indexOf(listener), 1)
}
}
}
}
}

@ -1,17 +1,7 @@
require('./lib/chibi')
require('./lib/polyfills') require('./lib/polyfills')
require('./event_emitter')
require('./utils')
require('./modal') require('./modal')
require('./notif') require('./notif')
require('./appcommon') require('./appcommon')
require('./demo') require('./demo')
require('./lang')
require('./wifi') require('./wifi')
require('./term_conn') window.termInit = require('./term')
require('./term_input')
require('./term_screen')
require('./term_upload')
require('./debug_screen')
require('./soft_keyboard')
require('./term')

@ -5,4 +5,4 @@ let _tr = {
"wifi.enter_passwd": "Enter password for \":ssid:\"" "wifi.enter_passwd": "Enter password for \":ssid:\""
}; };
function tr (key) { return _tr[key] || '?' + key + '?' } module.exports = function tr (key) { return _tr[key] || '?' + key + '?' }

@ -1,44 +1,44 @@
const $ = require('./lib/chibi')
/** Module for toggling a modal overlay */ /** Module for toggling a modal overlay */
(function () { let modal = {}
let modal = {} let curCloseCb = null
let curCloseCb = null
modal.show = function (sel, closeCb) { modal.show = function (sel, closeCb) {
let $m = $(sel) let $m = $(sel)
$m.removeClass('hidden visible') $m.removeClass('hidden visible')
setTimeout(function () { setTimeout(function () {
$m.addClass('visible') $m.addClass('visible')
}, 1) }, 1)
curCloseCb = closeCb curCloseCb = closeCb
} }
modal.hide = function (sel) { modal.hide = function (sel) {
let $m = $(sel) let $m = $(sel)
$m.removeClass('visible') $m.removeClass('visible')
setTimeout(function () { setTimeout(function () {
$m.addClass('hidden') $m.addClass('hidden')
if (curCloseCb) curCloseCb() if (curCloseCb) curCloseCb()
}, 500) // transition time }, 500) // transition time
} }
modal.init = function () { modal.init = function () {
// close modal by click outside the dialog // close modal by click outside the dialog
$('.Modal').on('click', function () { $('.Modal').on('click', function () {
if ($(this).hasClass('no-close')) return // this is a no-close modal if ($(this).hasClass('no-close')) return // this is a no-close modal
modal.hide(this) modal.hide(this)
}) })
$('.Dialog').on('click', function (e) { $('.Dialog').on('click', function (e) {
e.stopImmediatePropagation() e.stopImmediatePropagation()
}) })
// Hide all modals on esc // Hide all modals on esc
$(window).on('keydown', function (e) { $(window).on('keydown', function (e) {
if (e.which === 27) { if (e.which === 27) {
modal.hide('.Modal') modal.hide('.Modal')
} }
}) })
} }
window.Modal = modal module.exports = modal
})()

@ -1,65 +1,65 @@
window.Notify = (function () { const $ = require('./lib/chibi')
let nt = {} const modal = require('./modal')
const sel = '#notif'
let $balloon
let timerHideBegin // timeout to start hiding (transition) let nt = {}
let timerHideEnd // timeout to add the hidden class const sel = '#notif'
let timerCanCancel let $balloon
let canCancel = false
let stopTimeouts = function () { let timerHideBegin // timeout to start hiding (transition)
clearTimeout(timerHideBegin) let timerHideEnd // timeout to add the hidden class
clearTimeout(timerHideEnd) let canCancel = false
}
nt.show = function (message, timeout, isError) {
$balloon.toggleClass('error', isError === true)
$balloon.html(message)
Modal.show($balloon)
stopTimeouts()
if (undef(timeout) || timeout === null || timeout <= 0) { let stopTimeouts = function () {
timeout = 2500 clearTimeout(timerHideBegin)
} clearTimeout(timerHideEnd)
}
timerHideBegin = setTimeout(nt.hide, timeout) nt.show = function (message, timeout, isError) {
$balloon.toggleClass('error', isError === true)
$balloon.html(message)
modal.show($balloon)
stopTimeouts()
canCancel = false if (!timeout || timeout <= 0) {
timerCanCancel = setTimeout(function () { timeout = 2500
canCancel = true
}, 500)
} }
nt.hide = function () { timerHideBegin = setTimeout(nt.hide, timeout)
let $m = $(sel)
$m.removeClass('visible')
timerHideEnd = setTimeout(function () {
$m.addClass('hidden')
}, 250) // transition time
}
nt.init = function () { canCancel = false
$balloon = $(sel) setTimeout(() => {
canCancel = true
}, 500)
}
// close by click outside nt.hide = function () {
$(document).on('click', function () { let $m = $(sel)
if (!canCancel) return $m.removeClass('visible')
nt.hide(this) timerHideEnd = setTimeout(function () {
}) $m.addClass('hidden')
}, 250) // transition time
}
// click caused by selecting, prevent it from bubbling nt.init = function () {
$balloon.on('click', function (e) { $balloon = $(sel)
e.stopImmediatePropagation()
return false
})
// stop fading if moused // close by click outside
$balloon.on('mouseenter', function () { $(document).on('click', function () {
stopTimeouts() if (!canCancel) return
$balloon.removeClass('hidden').addClass('visible') nt.hide(this)
}) })
}
// click caused by selecting, prevent it from bubbling
$balloon.on('click', function (e) {
e.stopImmediatePropagation()
return false
})
// stop fading if moused
$balloon.on('mouseenter', function () {
stopTimeouts()
$balloon.removeClass('hidden').addClass('visible')
})
}
return nt module.exports = nt
})()

@ -1,4 +1,6 @@
window.initSoftKeyboard = function (screen, input) { const { qs } = require('./utils')
module.exports = function (screen, input) {
const keyInput = qs('#softkb-input') const keyInput = qs('#softkb-input')
if (!keyInput) return // abort, we're not on the terminal page if (!keyInput) return // abort, we're not on the terminal page
@ -33,7 +35,6 @@ window.initSoftKeyboard = function (screen, input) {
// that deals with the input composition events. // that deals with the input composition events.
let lastCompositionString = '' let lastCompositionString = ''
let compositing = false
// sends the difference between the last and the new composition string // sends the difference between the last and the new composition string
let sendInputDelta = function (newValue) { let sendInputDelta = function (newValue) {
@ -96,12 +97,10 @@ window.initSoftKeyboard = function (screen, input) {
keyInput.addEventListener('compositionstart', e => { keyInput.addEventListener('compositionstart', e => {
lastCompositionString = '' lastCompositionString = ''
compositing = true
}) })
keyInput.addEventListener('compositionend', e => { keyInput.addEventListener('compositionend', e => {
lastCompositionString = '' lastCompositionString = ''
compositing = false
keyInput.value = '' keyInput.value = ''
}) })

@ -1,9 +1,18 @@
const { qs, mk } = require('./utils')
const Notify = require('./notif')
const TermScreen = require('./term_screen')
const TermConnection = require('./term_conn')
const TermInput = require('./term_input')
const TermUpload = require('./term_upload')
const initSoftKeyboard = require('./soft_keyboard')
const attachDebugScreen = require('./debug_screen')
/** Init the terminal sub-module - called from HTML */ /** Init the terminal sub-module - called from HTML */
window.termInit = function ({ labels, theme, allFn }) { module.exports = function ({ labels, theme, allFn }) {
const screen = new TermScreen() const screen = new TermScreen()
const conn = new Conn(screen) const conn = new TermConnection(screen)
const input = Input(conn, screen) const input = TermInput(conn, screen)
const termUpload = TermUpl(conn, input, screen) const termUpload = TermUpload(conn, input, screen)
screen.input = input screen.input = input
input.termUpload = termUpload input.termUpload = termUpload
@ -39,8 +48,8 @@ window.termInit = function ({ labels, theme, allFn }) {
qs('#screen').appendChild(screen.canvas) qs('#screen').appendChild(screen.canvas)
screen.load(labels, theme) // load labels and theme screen.load(labels, theme) // load labels and theme
window.initSoftKeyboard(screen, input) initSoftKeyboard(screen, input)
if (window.attachDebugScreen) window.attachDebugScreen(screen) if (attachDebugScreen) attachDebugScreen(screen)
let isFullscreen = false let isFullscreen = false
let fitScreen = false let fitScreen = false
@ -75,10 +84,10 @@ window.termInit = function ({ labels, theme, allFn }) {
}) })
// add fullscreen mode & button // add fullscreen mode & button
if (Element.prototype.requestFullscreen || Element.prototype.webkitRequestFullscreen) { if (window.Element.prototype.requestFullscreen || window.Element.prototype.webkitRequestFullscreen) {
let checkForFullscreen = function () { let checkForFullscreen = function () {
// document.fullscreenElement is not really supported yet, so here's a hack // document.fullscreenElement is not really supported yet, so here's a hack
if (isFullscreen && (innerWidth !== window.screen.width || innerHeight !== window.screen.height)) { if (isFullscreen && (window.innerWidth !== window.screen.width || window.innerHeight !== window.screen.height)) {
isFullscreen = false isFullscreen = false
fitScreenIfNeeded() fitScreenIfNeeded()
} }

@ -1,5 +1,9 @@
const EventEmitter = require('events')
const $ = require('./lib/chibi')
const demo = require('./demo')
/** Handle connections */ /** Handle connections */
window.Conn = class TermConnection extends EventEmitter { module.exports = class TermConnection extends EventEmitter {
constructor (screen) { constructor (screen) {
super() super()
@ -94,7 +98,7 @@ window.Conn = class TermConnection extends EventEmitter {
send (message) { send (message) {
if (window._demo) { if (window._demo) {
if (typeof window.demoInterface !== 'undefined') { if (typeof window.demoInterface !== 'undefined') {
demoInterface.input(message) demo.input(message)
} else { } else {
console.log(`TX: ${JSON.stringify(message)}`) console.log(`TX: ${JSON.stringify(message)}`)
} }
@ -130,9 +134,9 @@ window.Conn = class TermConnection extends EventEmitter {
init () { init () {
if (window._demo) { if (window._demo) {
if (typeof window.demoInterface === 'undefined') { if (typeof window.demoInterface === 'undefined') {
alert('Demoing non-demo build!') // this will catch mistakes when deploying to the website window.alert('Demoing non-demo build!') // this will catch mistakes when deploying to the website
} else { } else {
demoInterface.init(this.screen) demo.init(this.screen)
showPage() showPage()
} }
return return
@ -143,7 +147,7 @@ window.Conn = class TermConnection extends EventEmitter {
this.closeSocket() this.closeSocket()
this.ws = new WebSocket('ws://' + _root + '/term/update.ws') this.ws = new window.WebSocket('ws://' + window._root + '/term/update.ws')
this.ws.addEventListener('open', (...args) => this.onWSOpen(...args)) this.ws.addEventListener('open', (...args) => this.onWSOpen(...args))
this.ws.addEventListener('close', (...args) => this.onWSClose(...args)) this.ws.addEventListener('close', (...args) => this.onWSClose(...args))
this.ws.addEventListener('message', (...args) => this.onWSMessage(...args)) this.ws.addEventListener('message', (...args) => this.onWSMessage(...args))
@ -167,7 +171,7 @@ window.Conn = class TermConnection extends EventEmitter {
this.pingInterval = setInterval(() => { this.pingInterval = setInterval(() => {
console.log('> ping') console.log('> ping')
this.emit('ping') this.emit('ping')
$.get('http://' + _root + '/system/ping', (resp, status) => { $.get('http://' + window._root + '/system/ping', (resp, status) => {
if (status === 200) { if (status === 200) {
clearInterval(this.pingInterval) clearInterval(this.pingInterval)
console.info('Server ready, opening socket…') console.info('Server ready, opening socket…')

@ -1,3 +1,6 @@
const $ = require('./lib/chibi')
const { encode2B } = require('./utils')
/** /**
* User input * User input
* *
@ -14,7 +17,7 @@
* r - mb release * r - mb release
* m - mouse move * m - mouse move
*/ */
window.Input = function (conn, screen) { module.exports = function (conn, screen) {
// handle for input object // handle for input object
let input let input

@ -1,3 +1,8 @@
const EventEmitter = require('events')
const $ = require('./lib/chibi')
const { mk, qs, parse2B, parse3B } = require('./utils')
const notify = require('./notif')
// constants for decoding the update blob // constants for decoding the update blob
const SEQ_REPEAT = 2 const SEQ_REPEAT = 2
const SEQ_SET_COLORS = 3 const SEQ_SET_COLORS = 3
@ -8,7 +13,7 @@ const SEQ_SET_BG = 6
const SELECTION_BG = '#b2d7fe' const SELECTION_BG = '#b2d7fe'
const SELECTION_FG = '#333' const SELECTION_FG = '#333'
window.TermScreen = class TermScreen extends EventEmitter { module.exports = class TermScreen extends EventEmitter {
constructor () { constructor () {
super() super()
@ -472,8 +477,6 @@ window.TermScreen = class TermScreen extends EventEmitter {
const { const {
width, width,
height, height,
gridScaleX,
gridScaleY,
fitIntoWidth, fitIntoWidth,
fitIntoHeight fitIntoHeight
} = this.window } = this.window
@ -632,9 +635,9 @@ window.TermScreen = class TermScreen extends EventEmitter {
textarea.value = selectedText textarea.value = selectedText
textarea.select() textarea.select()
if (document.execCommand('copy')) { if (document.execCommand('copy')) {
Notify.show('Copied to clipboard') notify.show('Copied to clipboard')
} else { } else {
Notify.show('Failed to copy') notify.show('Failed to copy')
} }
document.body.removeChild(textarea) document.body.removeChild(textarea)
} }
@ -899,8 +902,6 @@ window.TermScreen = class TermScreen extends EventEmitter {
width, width,
height, height,
devicePixelRatio, devicePixelRatio,
gridScaleX,
gridScaleY,
statusScreen statusScreen
} = this.window } = this.window
@ -913,8 +914,6 @@ window.TermScreen = class TermScreen extends EventEmitter {
const charSize = this.getCharSize() const charSize = this.getCharSize()
const { width: cellWidth, height: cellHeight } = this.getCellSize() const { width: cellWidth, height: cellHeight } = this.getCellSize()
const screenWidth = width * cellWidth
const screenHeight = height * cellHeight
const screenLength = width * height const screenLength = width * height
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0) ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0)
@ -1042,7 +1041,7 @@ window.TermScreen = class TermScreen extends EventEmitter {
// pass 1: backgrounds // pass 1: backgrounds
for (let font of fontGroups.keys()) { for (let font of fontGroups.keys()) {
for (let data of fontGroups.get(font)) { for (let data of fontGroups.get(font)) {
let [cell, x, y, text, fg, bg, attrs, isCursor] = data let [cell, x, y, text, , bg] = data
if (redrawMap.get(cell)) { if (redrawMap.get(cell)) {
this.drawBackground({ x, y, cellWidth, cellHeight, bg }) this.drawBackground({ x, y, cellWidth, cellHeight, bg })
@ -1128,7 +1127,8 @@ window.TermScreen = class TermScreen extends EventEmitter {
const { const {
fontFamily, fontFamily,
width, width,
height height,
devicePixelRatio
} = this.window } = this.window
// reset drawnScreen to force redraw when statusScreen is disabled // reset drawnScreen to force redraw when statusScreen is disabled
@ -1185,7 +1185,7 @@ window.TermScreen = class TermScreen extends EventEmitter {
drawTimerLoop (threadID) { drawTimerLoop (threadID) {
if (!threadID || threadID !== this._drawTimerThread) return if (!threadID || threadID !== this._drawTimerThread) return
requestAnimationFrame(() => this.drawTimerLoop(threadID)) window.requestAnimationFrame(() => this.drawTimerLoop(threadID))
this.draw('draw-loop') this.draw('draw-loop')
} }
@ -1296,7 +1296,7 @@ window.TermScreen = class TermScreen extends EventEmitter {
this.screenAttrs = new Array(screenLength).fill(' ') this.screenAttrs = new Array(screenLength).fill(' ')
} }
let strArray = !undef(Array.from) ? Array.from(str) : str.split('') let strArray = Array.from ? Array.from(str) : str.split('')
const MASK_LINE_ATTR = 0xC8 const MASK_LINE_ATTR = 0xC8
const MASK_BLINK = 1 << 4 const MASK_BLINK = 1 << 4
@ -1392,7 +1392,7 @@ window.TermScreen = class TermScreen extends EventEmitter {
let label = pieces[i + 1].trim() let label = pieces[i + 1].trim()
// if empty string, use the "dim" effect and put nbsp instead to // if empty string, use the "dim" effect and put nbsp instead to
// stretch the button vertically // stretch the button vertically
button.innerHTML = label ? esc(label) : '&nbsp;' button.innerHTML = label ? $.htmlEscape(label) : '&nbsp;'
button.style.opacity = label ? 1 : 0.2 button.style.opacity = label ? 1 : 0.2
}) })
} }
@ -1403,17 +1403,17 @@ window.TermScreen = class TermScreen extends EventEmitter {
*/ */
showNotification (text) { showNotification (text) {
console.info(`Notification: ${text}`) console.info(`Notification: ${text}`)
if (Notification && Notification.permission === 'granted') { if (window.Notification && window.Notification.permission === 'granted') {
let notification = new Notification('ESPTerm', { let notification = new window.Notification('ESPTerm', {
body: text body: text
}) })
notification.addEventListener('click', () => window.focus()) notification.addEventListener('click', () => window.focus())
} else { } else {
if (Notification && Notification.permission !== 'denied') { if (window.Notification && window.Notification.permission !== 'denied') {
Notification.requestPermission() window.Notification.requestPermission()
} else { } else {
// Fallback using the built-in notification balloon // Fallback using the built-in notification balloon
Notify.show(text) notify.show(text)
} }
} }
} }
@ -1500,7 +1500,7 @@ window.TermScreen = class TermScreen extends EventEmitter {
surrOsc.stop(startTime + 0.8) surrOsc.stop(startTime + 0.8)
let loop = function () { let loop = function () {
if (audioCtx.currentTime < startTime + 0.8) requestAnimationFrame(loop) if (audioCtx.currentTime < startTime + 0.8) window.requestAnimationFrame(loop)
mainGain.gain.value *= 0.8 mainGain.gain.value *= 0.8
surrGain.gain.value *= 0.8 surrGain.gain.value *= 0.8
} }

@ -1,5 +1,9 @@
const $ = require('./lib/chibi')
const { qs } = require('./utils')
const modal = require('./modal')
/** File upload utility */ /** File upload utility */
window.TermUpl = function (conn, input, screen) { module.exports = function (conn, input, screen) {
let lines, // array of lines without newlines let lines, // array of lines without newlines
line_i, // current line index line_i, // current line index
fuTout, // timeout handle for line sending fuTout, // timeout handle for line sending
@ -14,7 +18,7 @@ window.TermUpl = function (conn, input, screen) {
function openUploadDialog () { function openUploadDialog () {
updateStatus('Ready...') updateStatus('Ready...')
Modal.show('#fu_modal', onDialogClose) modal.show('#fu_modal', onDialogClose)
$('#fu_form').toggleClass('busy', false) $('#fu_form').toggleClass('busy', false)
input.blockKeys(true) input.blockKeys(true)
} }
@ -125,19 +129,19 @@ window.TermUpl = function (conn, input, screen) {
} }
function fuClose () { function fuClose () {
Modal.hide('#fu_modal') modal.hide('#fu_modal')
} }
return { return {
init: function () { init: function () {
qs('#fu_file').addEventListener('change', function (evt) { qs('#fu_file').addEventListener('change', function (evt) {
let reader = new FileReader() let reader = new window.FileReader()
let file = evt.target.files[0] let file = evt.target.files[0]
let ftype = file.type || 'application/octet-stream' let ftype = file.type || 'application/octet-stream'
console.log('Selected file type: ' + ftype) console.log('Selected file type: ' + ftype)
if (!ftype.match(/text\/.*|application\/(json|csv|.*xml.*|.*script.*|x-php)/)) { if (!ftype.match(/text\/.*|application\/(json|csv|.*xml.*|.*script.*|x-php)/)) {
// Deny load of blobs like img - can crash browser and will get corrupted anyway // Deny load of blobs like img - can crash browser and will get corrupted anyway
if (!confirm(`This does not look like a text file: ${ftype}\nReally load?`)) { if (!window.confirm(`This does not look like a text file: ${ftype}\nReally load?`)) {
qs('#fu_file').value = '' qs('#fu_file').value = ''
return return
} }

@ -1,48 +1,25 @@
/** Make a node */ /** Make a node */
function mk (e) { exports.mk = function mk (e) {
return document.createElement(e) return document.createElement(e)
} }
/** Find one by query */ /** Find one by query */
function qs (s) { exports.qs = function qs (s) {
return document.querySelector(s) return document.querySelector(s)
} }
/** Find all by query */ /** Find all by query */
function qsa (s) { exports.qsa = function qsa (s) {
return document.querySelectorAll(s) return document.querySelectorAll(s)
} }
/** Convert any to bool safely */ /** Convert any to bool safely */
function bool (x) { exports.bool = function bool (x) {
return (x === 1 || x === '1' || x === true || x === 'true') return (x === 1 || x === '1' || x === true || x === 'true')
} }
/**
* Filter 'spacebar' and 'return' from keypress handler,
* and when they're pressed, fire the callback.
* use $(...).on('keypress', cr(handler))
*/
function cr (hdl) {
return function (e) {
if (e.which === 10 || e.which === 13 || e.which === 32) {
hdl()
}
}
}
/** HTML escape */
function esc (str) {
return $.htmlEscape(str)
}
/** Check for undefined */
function undef (x) {
return typeof x == 'undefined'
}
/** Safe json parse */ /** Safe json parse */
function jsp (str) { exports.jsp = function jsp (str) {
try { try {
return JSON.parse(str) return JSON.parse(str)
} catch (e) { } catch (e) {
@ -51,33 +28,28 @@ function jsp (str) {
} }
} }
/** Create a character from ASCII code */
function Chr (n) {
return String.fromCharCode(n)
}
/** Decode number from 2B encoding */ /** Decode number from 2B encoding */
function parse2B (s, i = 0) { exports.parse2B = function parse2B (s, i = 0) {
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127 return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127
} }
/** Decode number from 3B encoding */ /** Decode number from 3B encoding */
function parse3B (s, i = 0) { exports.parse3B = function parse3B (s, i = 0) {
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i + 1) - 1) * 127 + (s.charCodeAt(i + 2) - 1) * 127 * 127 return (s.charCodeAt(i) - 1) + (s.charCodeAt(i + 1) - 1) * 127 + (s.charCodeAt(i + 2) - 1) * 127 * 127
} }
/** Encode using 2B encoding, returns string. */ /** Encode using 2B encoding, returns string. */
function encode2B (n) { exports.encode2B = function encode2B (n) {
let lsb, msb let lsb, msb
lsb = (n % 127) lsb = (n % 127)
n = ((n - lsb) / 127) n = ((n - lsb) / 127)
lsb += 1 lsb += 1
msb = (n + 1) msb = (n + 1)
return Chr(lsb) + Chr(msb) return String.fromCharCode(lsb) + String.fromCharCode(msb)
} }
/** Encode using 3B encoding, returns string. */ /** Encode using 3B encoding, returns string. */
function encode3B (n) { exports.encode3B = function encode3B (n) {
let lsb, msb, xsb let lsb, msb, xsb
lsb = (n % 127) lsb = (n % 127)
n = (n - lsb) / 127 n = (n - lsb) / 127
@ -86,5 +58,5 @@ function encode3B (n) {
n = (n - msb) / 127 n = (n - msb) / 127
msb += 1 msb += 1
xsb = (n + 1) xsb = (n + 1)
return Chr(lsb) + Chr(msb) + Chr(xsb) return String.fromCharCode(lsb) + String.fromCharCode(msb) + String.fromCharCode(xsb)
} }

@ -1,4 +1,8 @@
(function (w) { const $ = require('./lib/chibi')
const { mk, bool } = require('./utils')
const tr = require('./lang')
;(function (w) {
const authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'] const authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2']
let curSSID let curSSID
@ -15,8 +19,8 @@
$('#sta-nw').toggleClass('hidden', name.length === 0) $('#sta-nw').toggleClass('hidden', name.length === 0)
$('#sta-nw-nil').toggleClass('hidden', name.length > 0) $('#sta-nw-nil').toggleClass('hidden', name.length > 0)
$('#sta-nw .essid').html(esc(name)) $('#sta-nw .essid').html($.htmlEscape(name))
const nopw = undef(password) || password.length === 0 const nopw = !password || password.length === 0
$('#sta-nw .passwd').toggleClass('hidden', nopw) $('#sta-nw .passwd').toggleClass('hidden', nopw)
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw) $('#sta-nw .nopasswd').toggleClass('hidden', !nopw)
$('#sta-nw .ip').html(ip.length > 0 ? tr('wifi.connected_ip_is') + ip : tr('wifi.not_conn')) $('#sta-nw .ip').html(ip.length > 0 ? tr('wifi.connected_ip_is') + ip : tr('wifi.not_conn'))
@ -96,7 +100,7 @@
if (+$th.data('pwd')) { if (+$th.data('pwd')) {
// this AP needs a password // this AP needs a password
conn_pass = prompt(tr('wifi.enter_passwd').replace(':ssid:', conn_ssid)) conn_pass = window.prompt(tr('wifi.enter_passwd').replace(':ssid:', conn_ssid))
if (!conn_pass) return if (!conn_pass) return
} }
@ -120,10 +124,10 @@
/** Ask the CGI what APs are visible (async) */ /** Ask the CGI what APs are visible (async) */
function scanAPs () { function scanAPs () {
if (_demo) { if (window._demo) {
onScan(_demo_aps, 200) onScan(window._demo_aps, 200)
} else { } else {
$.get('http://' + _root + '/cfg/wifi/scan', onScan) $.get('http://' + window._root + '/cfg/wifi/scan', onScan)
} }
} }

@ -13,8 +13,7 @@
"webpack": "^3.6.0" "webpack": "^3.6.0"
}, },
"scripts": { "scripts": {
"babel": "babel $@", "webpack": "webpack $@",
"minify": "babel-minify $@",
"sass": "node-sass $@" "sass": "node-sass $@"
} }
} }

@ -2,9 +2,9 @@
<script> <script>
// Workaround for badly loaded page // Workaround for badly loaded page
setTimeout(function() { setTimeout(function() {
if (typeof termInit == 'undefined' || typeof $ == 'undefined') { if (typeof termInit == 'undefined') {
console.error("Page load failed, refreshing…"); console.error("Page load failed, refreshing…")
location.reload(true); location.reload(true)
} }
}, 3000); }, 3000);
</script> </script>

Loading…
Cancel
Save