Initial commit
This commit is contained in:
commit
ad15f11810
52
.eslintrc.js
Normal file
52
.eslintrc.js
Normal file
@ -0,0 +1,52 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"es6": true,
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"impliedStrict": true,
|
||||
},
|
||||
"rules": {
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"linebreak-style": [
|
||||
"off"
|
||||
],
|
||||
"no-console": 0,
|
||||
"no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted" }],
|
||||
"no-unused-vars": ["error", { "vars": "local" }],
|
||||
"quotes": [
|
||||
"error",
|
||||
"backtick",
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never",
|
||||
],
|
||||
"strict": ["error", "global"],
|
||||
},
|
||||
"globals": {
|
||||
"DoseeLoader": true,
|
||||
"Emulator": true,
|
||||
"metaContent": true,
|
||||
"storageAvailable": true,
|
||||
|
||||
"BuildEcma48": true,
|
||||
"buildLinksToCSS": true,
|
||||
"changeTextScanlines": true,
|
||||
"changeTextEffect": true,
|
||||
"checkArg": true,
|
||||
"checkErr": true,
|
||||
"checkRange": true,
|
||||
"displayErr": true,
|
||||
"findControlSequences": true,
|
||||
"findEngine": true,
|
||||
"Font": true,
|
||||
"Guess": true,
|
||||
"humaniseFS": true,
|
||||
"Palette": true,
|
||||
"ParseToChildren": true,
|
||||
"runSpinLoader": true,
|
||||
"Transcode": true,
|
||||
}
|
||||
};
|
BIN
disk_drives/g_drive.zip
Normal file
BIN
disk_drives/g_drive.zip
Normal file
Binary file not shown.
BIN
disk_drives/s_drive.zip
Normal file
BIN
disk_drives/s_drive.zip
Normal file
Binary file not shown.
BIN
disk_drives/u_drive.zip
Normal file
BIN
disk_drives/u_drive.zip
Normal file
Binary file not shown.
160
dosee-functions.js
Normal file
160
dosee-functions.js
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* /javascripts/dosee-functions.js
|
||||
*
|
||||
* DOSee user interface functions
|
||||
*/
|
||||
|
||||
/* global saveAs screenfull */
|
||||
/* eslint-env es6 */
|
||||
/* eslint quotes: ['error', 'backtick'] */
|
||||
/* eslint indent: ["error", 4, { "SwitchCase": 1 } ] */
|
||||
/* eslint no-console: ["error", { allow: ["log", "error", "warn"] }] */
|
||||
|
||||
'use strict'
|
||||
|
||||
// Returns the content data stored in HTML <meta> tags
|
||||
function metaContent(name) {
|
||||
const elm = document.getElementsByName(name)
|
||||
if (elm[0] === undefined) return null
|
||||
else return elm[0].getAttribute(`content`)
|
||||
}
|
||||
|
||||
// Test the local storage availability for the browser
|
||||
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
||||
function storageAvailable(type) {
|
||||
const test = function (t) {
|
||||
try {
|
||||
const storage = window[t],
|
||||
x = `__storage_test__`
|
||||
storage.setItem(x, `test data`)
|
||||
storage.removeItem(x)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case `local`: case `session`:
|
||||
return test(`${type}Storage`)
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
// 'Options' tab interactions
|
||||
|
||||
// Restore existing and save new interactions that are kept after the browser is closed
|
||||
if (storageAvailable(`local`)) {
|
||||
// Reveal Options tab
|
||||
const dto = document.getElementById(`doseeOptionsTab`)
|
||||
dto.classList.remove(`hide-true`)
|
||||
dto.classList.add(`hide-false`)
|
||||
// Automatically start DOS emulation
|
||||
const asb = document.getElementById(`doseeAutoRun`)
|
||||
asb.addEventListener(`click`, function () {
|
||||
const chk = asb.checked
|
||||
localStorage.setItem(`doseeAutostart`, chk) // boolean value
|
||||
})
|
||||
const d = localStorage.getItem(`doseeAutostart`)
|
||||
if (d === `true`) asb.checked = true
|
||||
// For sharper DOS ASCII/ANSI text
|
||||
const nab = document.getElementById(`doseeAspect`)
|
||||
const e = localStorage.getItem(`doseeAspect`)
|
||||
nab.addEventListener(`click`, function () {
|
||||
var dosaspect = nab.checked
|
||||
localStorage.setItem(`doseeAspect`, !dosaspect) // boolean value
|
||||
})
|
||||
if (e === `false`) nab.checked = true
|
||||
// Scaler engine
|
||||
const sOpt = document.querySelectorAll(`input[name=dosscale]`)
|
||||
let i = sOpt.length
|
||||
while (i--)
|
||||
sOpt[i].addEventListener(`change`, function () {
|
||||
localStorage.setItem(`doseeScaler`, this.value)
|
||||
}, 0)
|
||||
}
|
||||
// Session storage interactions that get deleted when the browser tab is closed
|
||||
if (storageAvailable(`session`)) {
|
||||
const setTab = function (ac) {
|
||||
if (ac === null) return
|
||||
sessionStorage.setItem(`doseeTab`, `${ac}`)
|
||||
}
|
||||
const tab = document.getElementById(`doseeTabs`)
|
||||
const tabs = tab.getElementsByClassName(`tabtoggle`)
|
||||
// tab event listeners
|
||||
if (tabs != null && tabs.length >= 0) {
|
||||
let i = tabs.length
|
||||
while (i--) {
|
||||
tabs[i].addEventListener(`click`, function () {
|
||||
setTab(this.firstChild.getAttribute(`aria-controls`))
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
// restore most recently used tab
|
||||
const savedTab = sessionStorage.getItem(`doseeTab`)
|
||||
if (savedTab !== null) {
|
||||
const dt = document.getElementById(`doseeTabs`)
|
||||
const dtc = document.getElementById(`doseeTabContent`)
|
||||
const ts = document.getElementById(`${savedTab}Tab`)
|
||||
const tb = document.getElementById(savedTab)
|
||||
if (dt !== null && dtc !== null && ts !== null && tb !== null) {
|
||||
dt.getElementsByClassName(`active`)[0].classList.remove(`active`)
|
||||
dtc.getElementsByClassName(`active`)[0].classList.remove(`active`)
|
||||
ts.classList.add(`active`)
|
||||
tb.classList.add(`active`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Full screen button
|
||||
{
|
||||
let elem = document.getElementById(`doseeCanvas`)
|
||||
if (screenfull.enabled) {
|
||||
const fsb = document.getElementById(`doseeFullScreen`)
|
||||
fsb.classList.remove(`hide-true`)
|
||||
fsb.classList.add(`hide-false-inline`)
|
||||
const chrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)
|
||||
if (chrome) elem = document.getElementById(`doseeContainer`)
|
||||
}
|
||||
document.getElementById(`doseeFullScreen`).addEventListener(`click`, function () {
|
||||
if (screenfull.enabled) screenfull.request(elem)
|
||||
})
|
||||
}
|
||||
|
||||
// Screenshot button & screenshot + upload button
|
||||
try {
|
||||
const fss = !!new Blob()
|
||||
if (typeof fss !== `undefined`) {
|
||||
const ssb = document.getElementById(`doseeCaptureScreen`)
|
||||
// dosee screenshot button
|
||||
ssb.classList.remove(`hide-true`)
|
||||
ssb.classList.add(`hide-false-inline`)
|
||||
ssb.addEventListener(`click`, function () {
|
||||
const canvas = document.getElementById(`doseeCanvas`)
|
||||
const filename = metaContent(`dosee:capname`)
|
||||
canvas.toBlob(function (blob) {
|
||||
saveAs(blob, filename)
|
||||
const click = ssb.childNodes[0].childNodes[0].classList
|
||||
click.add(`brand-success`)
|
||||
setTimeout(function () { click.remove(`brand-success`) }, 750)
|
||||
})
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
// Keyboard navigation is disabled
|
||||
{
|
||||
const tki = document.getElementsByClassName(`touchKeyboardIcons`)
|
||||
if (typeof tki[0] !== `undefined`) {
|
||||
tki[0].style.cssText = `display: none`
|
||||
}
|
||||
}
|
||||
|
||||
// Reboot button
|
||||
document.getElementById(`doseereboot`).addEventListener(`click`, function () {
|
||||
location.reload(true)
|
||||
})
|
||||
|
||||
})()
|
74
dosee-init.js
Normal file
74
dosee-init.js
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* dosee-loader.js
|
||||
* DOSee initialisator
|
||||
*/
|
||||
{
|
||||
`use strict`
|
||||
|
||||
const paths = new Map()
|
||||
.set(`ddg`, `disk_drives/g_drive.zip`)
|
||||
.set(`dds`, `disk_drives/s_drive.zip`)
|
||||
.set(`ddu`, `disk_drives/u_drive.zip`)
|
||||
.set(`core`, `emulator/dosee-core.js`)
|
||||
.set(`mem`, `emulator/dosee-core.mem`)
|
||||
|
||||
// Load GUS (Gravis UltraSound) drivers
|
||||
const gusDriver = function (q) {
|
||||
if (q !== `true`) return null
|
||||
return DoseeLoader.mountZip(`g`, DoseeLoader.fetchFile(`gravis ultrasound drivers`, `${paths.get(`ddg`)}`))
|
||||
}
|
||||
|
||||
// Load the Emscripten static memory initialization code external file
|
||||
// https://kripken.github.io/emscripten-site/docs/optimizing/Optimizing-Code.html#code-size
|
||||
const locateFiles = function (filename) {
|
||||
console.log(`filename: ${filename}`)
|
||||
if (filename === `dosbox.html.mem`) return `${paths.get(`mem`)}`
|
||||
return `libs/${filename}`
|
||||
}
|
||||
|
||||
// Initialise the resolution of the DOS program - width, height
|
||||
const nr = function () {
|
||||
const arr = cfg.res.split(`,`)
|
||||
if (arr.length < 1) return [640, 480]
|
||||
return [parseInt(arr[0]), parseInt(arr[1])]
|
||||
}
|
||||
|
||||
// Load additional DOS tools and utilities
|
||||
const utils = function (q) {
|
||||
if (q !== `true`) return null
|
||||
return DoseeLoader.mountZip(`u`, DoseeLoader.fetchFile(`dos utilities`, `${paths.get(`ddu`)}`))
|
||||
}
|
||||
|
||||
// Load configurations obtained from <meta name="dosee:"> HTML tags
|
||||
const cfg = {
|
||||
start: false,
|
||||
exe: metaContent(`dosee:startexe`),
|
||||
filename: metaContent(`dosee:filename`),
|
||||
gus: metaContent(`dosee:gusaudio`),
|
||||
path: metaContent(`dosee:gamefilepath`),
|
||||
res: metaContent(`dosee:resolution`),
|
||||
utils: metaContent(`dosee:utils`),
|
||||
}
|
||||
|
||||
// Start DOSee automatically?
|
||||
if (storageAvailable(`local`) && localStorage.getItem(`doseeAutostart`) === `true`) { cfg.start = true }
|
||||
if (cfg.start === true) console.log(`DOSee will launch automatically`)
|
||||
|
||||
// Initialise DOSee
|
||||
// Note order of these DoseeLoader values are important and swapping them could cause failures
|
||||
// dosee-core.js is the compiled Emscripten edition of DOSBox and should not be minified
|
||||
const init = new DoseeLoader(
|
||||
DoseeLoader.emulatorJS(`${paths.get(`core`)}`),
|
||||
DoseeLoader.locateAdditionalEmulatorJS(locateFiles),
|
||||
DoseeLoader.nativeResolution(nr()[0], nr()[1]),
|
||||
DoseeLoader.mountZip(`c`, DoseeLoader.fetchFile(`\'${cfg.filename}\'`, `${cfg.path}`)),
|
||||
DoseeLoader.mountZip(`s`, DoseeLoader.fetchFile(`DOSee configurations`, `${paths.get(`dds`)}`)),
|
||||
gusDriver(cfg.gus),
|
||||
utils(cfg.utils),
|
||||
DoseeLoader.startExe(cfg.exe)
|
||||
)
|
||||
|
||||
// Start DOSee!
|
||||
const emulator = new Emulator(document.querySelector(`#doseeCanvas`), null, init)
|
||||
emulator.start({ waitAfterDownloading: !cfg.start })
|
||||
}
|
979
dosee-loader.js
Normal file
979
dosee-loader.js
Normal file
@ -0,0 +1,979 @@
|
||||
/*
|
||||
* /javascripts/dosee-loader.js
|
||||
*
|
||||
* DOSee emulator
|
||||
*
|
||||
* Fork of https://github.com/db48x/emularity/blob/master/loader.js
|
||||
* Last commit synced to: Feb 15, 2018
|
||||
*
|
||||
* Major differences:
|
||||
* Requires ES6 compatible browser
|
||||
* Only DOS emulation
|
||||
* No local save game states
|
||||
* No Web Assembly builds [todo]
|
||||
*/
|
||||
|
||||
/* global storageAvailable Module BrowserFS ES6Promise FS Promise */
|
||||
/* eslint-env es6 */
|
||||
/* eslint quotes: ['error', 'backtick'] */
|
||||
/* eslint indent: ["error", 4, { "SwitchCase": 1 } ] */
|
||||
/* eslint no-console: ["error", { allow: ["log","warn", "error"] }] */
|
||||
/* eslint no-global-assign: ["error", {"exceptions": ["Module"]}] */
|
||||
|
||||
Module = null;
|
||||
|
||||
(function (Promise) {
|
||||
`use strict`
|
||||
|
||||
const doseeVersion = `1.8p`
|
||||
|
||||
// DOSBox requires a valid IndexedDB
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
|
||||
if (window.indexedDB) {
|
||||
document.getElementById(`doseeCrashed`).style.display = `none`
|
||||
}
|
||||
|
||||
|
||||
// Common API functions
|
||||
// DOSee is based off The Emularity which supports multiple emulators.
|
||||
// The BaseLoader naming convention was used to highlight the shared functions.
|
||||
function DoseeAPI() {
|
||||
return Array.prototype.reduce.call(arguments, extend)
|
||||
}
|
||||
|
||||
// HTML <canvas> element used to display the emulation
|
||||
DoseeAPI.canvas = function (id) {
|
||||
const elem = id instanceof Element ? id : document.getElementById(id)
|
||||
return { canvas: elem }
|
||||
}
|
||||
|
||||
DoseeAPI.emulatorJS = function (url) {
|
||||
return { emulatorJS: url }
|
||||
}
|
||||
|
||||
DoseeAPI.locateAdditionalEmulatorJS = function (func) {
|
||||
return { locateAdditionalJS: func }
|
||||
}
|
||||
|
||||
DoseeAPI.nativeResolution = function (width, height) {
|
||||
if (typeof width !== `number` || typeof height !== `number`)
|
||||
throw new Error(`Width and height must be numbers`)
|
||||
return {
|
||||
nativeResolution: {
|
||||
width: Math.floor(width),
|
||||
height: Math.floor(height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoseeAPI.aspectRatio = function (ratio) {
|
||||
if (typeof ratio !== `number`)
|
||||
throw new Error(`Aspect ratio must be a number`)
|
||||
return { aspectRatio: ratio }
|
||||
}
|
||||
|
||||
DoseeAPI.mountZip = function (drive, file) {
|
||||
return {
|
||||
files: [{
|
||||
drive: drive,
|
||||
mountpoint: `/${drive}`,
|
||||
file: file,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
DoseeAPI.mountFile = function (filename, file) {
|
||||
return {
|
||||
files: [{
|
||||
mountpoint: filename,
|
||||
file: file,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
DoseeAPI.fetchFile = function (title, url) {
|
||||
return {
|
||||
title: title,
|
||||
url: url,
|
||||
}
|
||||
}
|
||||
|
||||
DoseeAPI.fetchOptionalFile = function (title, url) {
|
||||
return {
|
||||
title: title,
|
||||
url: url,
|
||||
optional: true,
|
||||
}
|
||||
}
|
||||
|
||||
DoseeAPI.localFile = function (title, data) {
|
||||
return {
|
||||
title: title,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
function DoseeLoader() {
|
||||
const config = Array.prototype.reduce.call(arguments, extend)
|
||||
config.emulator_arguments = build_dosbox_arguments(config.emulatorStart, config.files, config.emulatorCPU)
|
||||
return config
|
||||
}
|
||||
Object.setPrototypeOf(DoseeLoader, DoseeAPI)
|
||||
|
||||
DoseeLoader.startExe = function (path) {
|
||||
return { emulatorStart: path }
|
||||
}
|
||||
|
||||
const build_dosbox_arguments = function (emulator_start, files) {
|
||||
console.log(`Initialisation of DOSee ` + doseeVersion)
|
||||
let verbose = `with the following configuration:`
|
||||
|
||||
// get guest program path
|
||||
const path = emulator_start.split(/\\|\//) // I have LTS already
|
||||
// get guest program file name
|
||||
let prog = path.pop()
|
||||
|
||||
// dosbox command line arguments
|
||||
const args = []
|
||||
|
||||
// parse URL query string
|
||||
if (`URLSearchParams` in window == false) {
|
||||
console.log(`DOSee needs the URLSearchParams interface to read URL query string values`)
|
||||
return args
|
||||
}
|
||||
const urlParams = new URLSearchParams(window.location.href)
|
||||
|
||||
// graphic engine scalers (https://www.dosbox.com/wiki/Scaler)
|
||||
let scaler = null
|
||||
if (storageAvailable(`local`)) scaler = localStorage.getItem(`doseeScaler`) // look for saved option
|
||||
if (scaler === null) scaler = `none`
|
||||
switch (scaler) {
|
||||
case `advinterp3x`:
|
||||
verbose += ` Advanced interpolation engine (advinterp3x).`
|
||||
args.push(`-conf`, `/dos/s/engine-advinterp3x.con`)
|
||||
document.getElementById(`dosscale5`).checked = true
|
||||
break
|
||||
case `hq3x`:
|
||||
verbose += ` High Quality 3 magnification (hq3x).`
|
||||
args.push(`-conf`, `/dos/s/engine-hq3x.con`)
|
||||
document.getElementById(`dosscale4`).checked = true
|
||||
break
|
||||
case `rgb3x`:
|
||||
verbose += ` RGB engine (rgb3x).`
|
||||
args.push(`-conf`, `/dos/s/engine-rgb3x.con`)
|
||||
document.getElementById(`dosscale3`).checked = true
|
||||
break
|
||||
case `super2xsai`:
|
||||
verbose += ` Super scale and interpolation engine (super2xsai).`
|
||||
args.push(`-conf`, `/dos/s/engine-super2xsai.con`)
|
||||
document.getElementById(`dosscale1`).checked = true
|
||||
break
|
||||
case `tv3x`:
|
||||
verbose += ` TV 3x scale engine (tv3x).`
|
||||
args.push(`-conf`, `/dos/s/engine-tv3x.con`)
|
||||
document.getElementById(`dosscale2`).checked = true
|
||||
break
|
||||
default: document.getElementById(`dosscale0`).checked = true
|
||||
break
|
||||
}
|
||||
|
||||
// impose aspect ratio correction
|
||||
let aspect = null
|
||||
if (storageAvailable(`local`)) aspect = localStorage.getItem(`doseeAspect`) // look for saved option
|
||||
if (aspect === null) aspect = `true`
|
||||
if (aspect !== `false`) {
|
||||
verbose += ` With aspect correction.`
|
||||
args.push(`-conf`, `/dos/s/render.con`) // aspect=true
|
||||
} else {
|
||||
document.getElementById(`doseeAspect`).checked = true
|
||||
verbose += ` No aspect correction.`
|
||||
}
|
||||
// emulation cpu speed
|
||||
const cpuspeed = urlParams.get(`dosspeed`)
|
||||
switch (cpuspeed) {
|
||||
case `8086`:
|
||||
verbose += ` 8086 real mode CPU.`
|
||||
args.push(`-conf`, `/dos/s/cpu-8086.con`)
|
||||
document.getElementById(`dosspeed4`).checked = true
|
||||
break
|
||||
case `386`:
|
||||
verbose += ` 386 protect mode CPU.`
|
||||
args.push(`-conf`, `/dos/s/cpu-386.con`)
|
||||
document.getElementById(`dosspeed3`).checked = true
|
||||
break
|
||||
case `486`: case `max`:
|
||||
verbose += ` Unlocked CPU speed.`
|
||||
args.push(`-conf`, `/dos/s/cpu-max.con`)
|
||||
document.getElementById(`dosspeed2`).checked = true
|
||||
break
|
||||
default:
|
||||
verbose += ` Automatic CPU speed.`
|
||||
args.push(`-conf`, `/dos/s/cpu-auto.con`)
|
||||
document.getElementById(`dosspeed1`).checked = true
|
||||
break
|
||||
}
|
||||
|
||||
// emulation sound cards
|
||||
const sound = urlParams.get(`dosaudio`)
|
||||
switch (sound) {
|
||||
case `none`:
|
||||
verbose += ` No audio.`
|
||||
args.push(`-conf`, `/dos/s/noaudio.con`)
|
||||
document.getElementById(`dosaudio5`).checked = true
|
||||
break
|
||||
case `sb1`:
|
||||
verbose += ` Sound Blaster 1.0 audio.`
|
||||
args.push(`-conf`, `/dos/s/sb1.con`)
|
||||
document.getElementById(`dosaudio3`).checked = true
|
||||
break
|
||||
case `gus`:
|
||||
verbose += ` Gravis Ultrasound audio.`
|
||||
args.push(`-conf`, `/dos/g/gus.con`)
|
||||
document.getElementById(`dosaudio1`).checked = true
|
||||
break
|
||||
case `covox`:
|
||||
verbose += ` Covox Speech Accelerator audio.`
|
||||
args.push(`-conf`, `/dos/s/covox.con`)
|
||||
document.getElementById(`dosaudio4`).checked = true
|
||||
break
|
||||
default:
|
||||
verbose += ` Sound Blaster 16 audio.`
|
||||
args.push(`-conf`, `/dos/s/sb16.con`)
|
||||
document.getElementById(`dosaudio2`).checked = true
|
||||
break
|
||||
}
|
||||
|
||||
// emulation graphics or machine type
|
||||
const machine = urlParams.get(`dosmachine`)
|
||||
switch (machine) {
|
||||
case `svga`:
|
||||
verbose += ` SVGA s3 graphics.`
|
||||
args.push(`-conf`, `/dos/s/svga.con`)
|
||||
document.getElementById(`dosmachine1`).checked = true
|
||||
document.getElementById(`svgaEffectsMsg`).style.display = `none`
|
||||
break
|
||||
case `cga`:
|
||||
verbose += ` CGA graphics.`
|
||||
args.push(`-conf`, `/dos/s/cga.con`)
|
||||
document.getElementById(`dosmachine5`).checked = true
|
||||
break
|
||||
case `ega`:
|
||||
verbose += ` EGA graphics.`
|
||||
args.push(`-conf`, `/dos/s/ega.con`)
|
||||
document.getElementById(`dosmachine3`).checked = true
|
||||
break
|
||||
case `herc`:
|
||||
verbose += ` Hercules graphics.`
|
||||
args.push(`-conf`, `/dos/s/herc.con`)
|
||||
document.getElementById(`dosmachine6`).checked = true
|
||||
break
|
||||
case `tandy`:
|
||||
verbose += ` Tandy graphics.`
|
||||
args.push(`-conf`, `/dos/s/tandy.con`)
|
||||
document.getElementById(`dosmachine4`).checked = true
|
||||
break
|
||||
case `et3000`:
|
||||
verbose += ` SVGA ET3000 graphics.`
|
||||
args.push(`-conf`, `/dos/s/et3000.con`)
|
||||
document.getElementById(`dosmachine1`).checked = true
|
||||
break
|
||||
case `et4000`:
|
||||
verbose += ` SVGA ET4000 graphics.`
|
||||
args.push(`-conf`, `/dos/s/et4000.con`)
|
||||
break
|
||||
case `paradise`:
|
||||
verbose += ` Paradise PVGA1A graphics.`
|
||||
args.push(`-conf`, `/dos/s/paradise.con`)
|
||||
break
|
||||
case `nolfb`:
|
||||
verbose += ` SVGA s3 graphics with no-line frame buffer hack.`
|
||||
args.push(`-conf`, `/dos/s/nolfb.con`)
|
||||
break
|
||||
case `oldvbe`:
|
||||
verbose += ` VESA 1.3 graphics.`
|
||||
args.push(`-conf`, `/dos/s/oldvbe.con`)
|
||||
break
|
||||
default:
|
||||
verbose += ` VGA graphics.`
|
||||
args.push(`-conf`, `/dos/s/vgaonly.con`)
|
||||
document.getElementById(`dosmachine2`).checked = true
|
||||
break
|
||||
}
|
||||
|
||||
// dosbox memory managers
|
||||
const ems = urlParams.get(`dosems`)
|
||||
if (ems === `false`) {
|
||||
verbose += ` ✗ Expanded (EMS) memory.`
|
||||
args.push(`-conf`, `/dos/s/noems.con`)
|
||||
}
|
||||
const umb = urlParams.get(`dosumb`)
|
||||
if (umb === `false`) {
|
||||
verbose += ` ✗ Upper Memory Block (UMB) access.`
|
||||
args.push(`-conf`, `/dos/s/noumb.con`)
|
||||
}
|
||||
const xms = urlParams.get(`dosxms`)
|
||||
if (xms === `false`) {
|
||||
verbose += ` ✗ Extended (XMS) memory.`
|
||||
args.push(`-conf`, `/dos/s/noxms.con`)
|
||||
}
|
||||
|
||||
// dosbox mount points (dos drive letters)
|
||||
for (const i in files) {
|
||||
if (`drive` in files[i]) {
|
||||
args.push(`-c`, `mount ${files[i].drive} /dos${files[i].mountpoint}`)
|
||||
}
|
||||
}
|
||||
|
||||
// dosbox default drive letter
|
||||
args.push(`-c`, /^[a-zA-Z]:$/.test(path[0]) ? path.shift() : `C:`)
|
||||
|
||||
// load drivers not natively supplied by DOSBox and after emulated drives are mounted
|
||||
// paths to drivers has been set in the sb1.con under [autoexec]
|
||||
if (sound === `sb1`) {
|
||||
args.push(`-c`, `SBFMDRV.COM`)
|
||||
args.push(`-c`, `SOUND.COM`)
|
||||
}
|
||||
|
||||
// dos utilities with PATH setup
|
||||
const dosutilities = urlParams.get(`dosutils`)
|
||||
if (dosutilities === `true`) {
|
||||
args.push(`-conf`, `/dos/s/utils.con`)
|
||||
}
|
||||
|
||||
// some programs don't run correctly unless their root directory is active
|
||||
if (path && path.length) {
|
||||
let pathStr = path.toString()
|
||||
pathStr = pathStr.replace(`,`, `\\`)
|
||||
console.log(`Execute path ${pathStr}`)
|
||||
args.push(`-c`, `CD ${pathStr}`)
|
||||
}
|
||||
|
||||
// automatically run the guest program
|
||||
const skiprun = urlParams.get(`dosautorun`)
|
||||
if (skiprun !== `false`) {
|
||||
prog = prog.replace(` :`, ` /`) // hack to implement program options
|
||||
verbose = `Will execute \`${prog}\` ${verbose}`
|
||||
args.push(`-c`, prog)
|
||||
|
||||
// partial ansi escape code generator for coloured text
|
||||
const ansi = function (code, str) {
|
||||
let c = String.fromCharCode(27, 91, 48, 109) // esc[0m
|
||||
switch (code) {
|
||||
case `bold`: c = String.fromCharCode(27, 91, 49, 109); break // esc[1m
|
||||
case `blue`: c = String.fromCharCode(27, 91, 51, 52, 109); break // esc[34m
|
||||
case `white`: c = String.fromCharCode(27, 91, 51, 55, 109); break // esc[37m
|
||||
}
|
||||
if (str === undefined) return c
|
||||
return `${c}${str}`
|
||||
}
|
||||
|
||||
// test to display after guest program is complete
|
||||
if (urlParams.get(`name`) !== `waitingapproval`) {
|
||||
const finCmd = `@echo ${prog} has finished. -${ansi(`bold`)}${ansi(`blue`, `d`)}${ansi(`white`, `e`)}\
|
||||
${ansi(`blue`, `f`)}${ansi(`white`, `acto`)}${ansi(`blue`, `2`)}${ansi(`white`, `.net`)}${ansi()}-`
|
||||
args.push(`-c`, finCmd)
|
||||
}
|
||||
}
|
||||
console.log(verbose)
|
||||
return args
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulator
|
||||
*/
|
||||
function Emulator(canvas, callbacks, loadFiles) {
|
||||
if (typeof callbacks !== `object`) {
|
||||
callbacks = {
|
||||
before_emulator: null,
|
||||
before_run: callbacks,
|
||||
}
|
||||
}
|
||||
let has_started = false
|
||||
const defaultSplashColors = {
|
||||
foreground: `white`,
|
||||
background: `black`,
|
||||
failure: `red`,
|
||||
success: `green`,
|
||||
}
|
||||
const splash = {
|
||||
loading_text: ``,
|
||||
spinning: true,
|
||||
finished_loading: false,
|
||||
colors: defaultSplashColors,
|
||||
table: null,
|
||||
splashimg: new Image(),
|
||||
}
|
||||
|
||||
let cssResolution, scale, aspectRatio
|
||||
// right off the bat we set the canvas's inner dimensions to
|
||||
// whatever it's current css dimensions are this isn't likely to be
|
||||
// the same size that dosbox will set it to, but it avoids
|
||||
// the case where the size was left at the default 300x150
|
||||
if (!canvas.hasAttribute(`width`)) {
|
||||
const style = getComputedStyle(canvas)
|
||||
canvas.width = parseInt(style.width, 10)
|
||||
canvas.height = parseInt(style.height, 10)
|
||||
}
|
||||
|
||||
this.setSplashImage = function (_splashimg) {
|
||||
if (_splashimg) {
|
||||
if (_splashimg instanceof Image) {
|
||||
if (splash.splashimg.parentNode) {
|
||||
splash.splashimg.src = _splashimg.src
|
||||
} else {
|
||||
splash.splashimg = _splashimg
|
||||
}
|
||||
} else {
|
||||
splash.splashimg.src = _splashimg
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
this.setCallbacks = function (_callbacks) {
|
||||
if (typeof _callbacks !== `object`) {
|
||||
callbacks = {
|
||||
before_emulator: null,
|
||||
before_run: _callbacks,
|
||||
}
|
||||
} else {
|
||||
callbacks = _callbacks
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
this.setSplashColors = function (colors) {
|
||||
splash.colors = colors
|
||||
return this
|
||||
}
|
||||
|
||||
this.setLoad = function (loadFunc) {
|
||||
loadFiles = loadFunc
|
||||
return this
|
||||
}
|
||||
|
||||
const start = function (options) {
|
||||
if (has_started) return false
|
||||
has_started = true
|
||||
if (typeof options !== `object`) {
|
||||
options = { waitAfterDownloading: false }
|
||||
}
|
||||
let k, c, game_data
|
||||
setupSplash(canvas, splash)
|
||||
drawsplash()
|
||||
|
||||
let loading
|
||||
if (typeof loadFiles === `function`) {
|
||||
loading = loadFiles(fetch_file, splash)
|
||||
} else {
|
||||
loading = Promise.resolve(loadFiles)
|
||||
}
|
||||
|
||||
loading.then(loadHardDrive)
|
||||
.then(loadBranding, errBranding)
|
||||
.then(loadDosWarp, errDosWarp)
|
||||
|
||||
// hide long load time messages once emulator has loaded
|
||||
{
|
||||
const sdlm = document.getElementById(`doseeSlowLoad`)
|
||||
if (sdlm !== `undefined`) {
|
||||
sdlm.style.display = `none`
|
||||
}
|
||||
}
|
||||
return this
|
||||
|
||||
function loadHardDrive(_game_data) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const deltaFS = new BrowserFS.FileSystem.InMemory()
|
||||
finish()
|
||||
|
||||
function finish() {
|
||||
game_data = _game_data
|
||||
|
||||
// Any file system writes to MountableFileSystem will be written to the
|
||||
// deltaFS, letting us mount read-only zip files into the MountableFileSystem
|
||||
// while being able to 'write' to them.
|
||||
game_data.fs = new BrowserFS.FileSystem.OverlayFS(deltaFS,
|
||||
new BrowserFS.FileSystem.MountableFileSystem())
|
||||
game_data.fs.initialize(function () {
|
||||
const Buffer = BrowserFS.BFSRequire(`buffer`).Buffer
|
||||
|
||||
function fetch(file) {
|
||||
if (`data` in file && file.data !== null && typeof file.data !== `undefined`) {
|
||||
return Promise.resolve(file.data)
|
||||
}
|
||||
return fetch_file(file.title, file.url, `arraybuffer`, file.optional)
|
||||
}
|
||||
|
||||
function mountAt(drive) {
|
||||
return function (data) {
|
||||
if (data !== null) {
|
||||
drive = drive.toLowerCase()
|
||||
const mountpoint = `/${drive}`
|
||||
// Mount into RO MFS.
|
||||
game_data.fs.getOverlayedFileSystems().readable.mount(mountpoint, BFSOpenZip(new Buffer(data)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(game_data.files
|
||||
.map(function (f) {
|
||||
if (f && f.file) {
|
||||
if (f.drive) return fetch(f.file).then(mountAt(f.drive))
|
||||
}
|
||||
return null
|
||||
})).then(resolve, reject)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function loadBranding() {
|
||||
if (!game_data || splash.failed_loading) return null
|
||||
if (options.waitAfterDownloading) {
|
||||
return new Promise(function (resolve) {
|
||||
splash.setTitle(`✔ Click here to run`)
|
||||
splash.spinning = false
|
||||
// stashes these event listeners so that we can remove them after
|
||||
window.addEventListener(`keydown`, k = keyevent(resolve))
|
||||
canvas.addEventListener(`click`, c = resolve)
|
||||
splash.splashElt.addEventListener(`click`, c)
|
||||
})
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function loadDosWarp() {
|
||||
if (!game_data || splash.failed_loading) return
|
||||
splash.spinning = true
|
||||
window.removeEventListener(`keypress`, k)
|
||||
canvas.removeEventListener(`click`, c)
|
||||
splash.splashElt.removeEventListener(`click`, c)
|
||||
|
||||
// Don't let arrow, pg up/down, home, end affect page position
|
||||
blockSomeKeys()
|
||||
setupFullScreen()
|
||||
disableRightClickContextMenu(canvas)
|
||||
|
||||
// Emscripten doesn't use the proper prefixed functions for fullscreen requests,
|
||||
// so let's map the prefixed versions to the correct function.
|
||||
canvas.requestPointerLock = getpointerlockenabler()
|
||||
|
||||
moveConfigToRoot(game_data.fs)
|
||||
Module = init_module(
|
||||
game_data.emulator_arguments,
|
||||
game_data.fs,
|
||||
game_data.locateAdditionalJS,
|
||||
game_data.nativeResolution,
|
||||
game_data.aspectRatio)
|
||||
|
||||
if (callbacks && callbacks.before_emulator) {
|
||||
try {
|
||||
callbacks.before_emulator()
|
||||
} catch (x) {
|
||||
console.log(x)
|
||||
}
|
||||
}
|
||||
if (game_data.emulatorJS) {
|
||||
// enable the operator screenshot and upload button plus jump to the emulation canvas
|
||||
{
|
||||
const oscb = document.getElementById(`doseeCaptureUpload`)
|
||||
if (oscb !== null) {
|
||||
oscb.disabled = false
|
||||
}
|
||||
window.location.href = `#emulator`
|
||||
}
|
||||
splash.setTitle(`Warping to DOS`)
|
||||
attach_script(game_data.emulatorJS)
|
||||
} else {
|
||||
splash.setTitle(`Non-system disk or disk error`)
|
||||
}
|
||||
}
|
||||
|
||||
function errBranding() {
|
||||
if (splash.failed_loading) return null
|
||||
splash.setTitle(`The emulator broke ${String.fromCharCode(9785)}`) // frown face
|
||||
splash.failed_loading = true
|
||||
}
|
||||
|
||||
function errDosWarp() {
|
||||
if (splash.failed_loading) return
|
||||
splash.setTitle(`Invalid media, track 0 bad or unusable`)
|
||||
splash.failed_loading = true
|
||||
}
|
||||
}
|
||||
this.start = start
|
||||
|
||||
const init_module = function (args, fs, locateAdditionalJS, nativeResolution, aspectRatio) {
|
||||
return {
|
||||
arguments: args,
|
||||
screenIsReadOnly: true,
|
||||
print: function (text) {
|
||||
// feedback from DOSBox
|
||||
console.log(text)
|
||||
},
|
||||
canvas: canvas,
|
||||
noInitialRun: false,
|
||||
locateFile: locateAdditionalJS,
|
||||
preInit: function () {
|
||||
splash.setTitle(`Loading program into the file system`)
|
||||
// Re-initialize BFS to just use the writeable in-memory storage.
|
||||
BrowserFS.initialize(fs)
|
||||
const BFS = new BrowserFS.EmscriptenFS()
|
||||
// Mount the file system into Emscripten.
|
||||
FS.mkdir(`/dos`)
|
||||
FS.mount(BFS, { root: `/` }, `/dos`)
|
||||
splash.finished_loading = true
|
||||
splash.hide()
|
||||
setTimeout(function () {
|
||||
resizeCanvas(canvas,
|
||||
scale = scale || scale,
|
||||
cssResolution = nativeResolution || cssResolution,
|
||||
aspectRatio = aspectRatio || aspectRatio)
|
||||
})
|
||||
if (callbacks && callbacks.before_run) {
|
||||
window.setTimeout(function () {
|
||||
callbacks.before_run()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const formatSize = function (event) {
|
||||
if (event.lengthComputable)
|
||||
return `(${(event.total ? (event.loaded / event.total * 100).toFixed(0) : `100`)}% \
|
||||
${formatBytes(event.loaded)} of ${formatBytes(event.total)})`
|
||||
return `(${formatBytes(event.loaded)})`
|
||||
}
|
||||
|
||||
const formatBytes = function (bytes, base10) {
|
||||
if (bytes === 0) return `0 B`
|
||||
const unit = base10 ? 1000 : 1024,
|
||||
units = base10 ? [`B`, `kB`, `MB`, `GB`, `TB`, `PB`, `EB`, `ZB`, `YB`] : [`B`, `KiB`, `MiB`, `GiB`, `TiB`, `PiB`, `EiB`, `ZiB`, `YiB`],
|
||||
exp = parseInt((Math.log(bytes) / Math.log(unit))),
|
||||
size = bytes / Math.pow(unit, exp)
|
||||
return `${size.toFixed(1)} ${units[exp]}`
|
||||
}
|
||||
|
||||
const fetch_file = function (title, url, rt, optional) {
|
||||
const row = addRow(splash.table)
|
||||
const titleCell = row[0],
|
||||
statusCell = row[1]
|
||||
titleCell.textContent = title
|
||||
return new Promise(function (resolve, reject) {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.open(`GET`, url, true)
|
||||
xhr.responseType = rt || `arraybuffer`
|
||||
xhr.onprogress = function (e) {
|
||||
titleCell.textContent = `${title} ${formatSize(e)}`
|
||||
}
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200 || xhr.status === 0) {
|
||||
success()
|
||||
resolve(xhr.response)
|
||||
} else if (optional) {
|
||||
success()
|
||||
resolve(null)
|
||||
} else {
|
||||
failure()
|
||||
reject()
|
||||
}
|
||||
}
|
||||
xhr.onerror = function () {
|
||||
if (optional) {
|
||||
success()
|
||||
resolve(null)
|
||||
} else {
|
||||
failure()
|
||||
reject()
|
||||
}
|
||||
}
|
||||
|
||||
function success() {
|
||||
statusCell.textContent = `✔`
|
||||
statusCell.style.color = splash.getColor(`success`)
|
||||
titleCell.textContent = title
|
||||
titleCell.style.fontWeight = `bold`
|
||||
titleCell.parentNode.style.backgroundColor = splash.getColor(`foreground`)
|
||||
titleCell.parentNode.style.color = splash.getColor(`background`)
|
||||
}
|
||||
|
||||
function failure() {
|
||||
statusCell.textContent = `✘`
|
||||
statusCell.style.color = splash.getColor(`failure`)
|
||||
titleCell.textContent = title
|
||||
titleCell.style.fontWeight = `bold`
|
||||
titleCell.parentNode.style.backgroundColor = splash.getColor(`foreground`)
|
||||
titleCell.parentNode.style.color = splash.getColor(`failure`)
|
||||
}
|
||||
xhr.send()
|
||||
})
|
||||
}
|
||||
|
||||
function keyevent(resolve) {
|
||||
return function (e) {
|
||||
if ((e.keycode || e.which) == 32) {
|
||||
e.preventDefault()
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const resizeCanvas = function (canvas, scale, resolution) {
|
||||
if (scale && resolution) {
|
||||
// optimizeSpeed is the standardized value. different
|
||||
// browsers support different values they will all ignore
|
||||
// values that they don't understand.
|
||||
canvas.style.imageRendering = `-moz-crisp-edges`
|
||||
canvas.style.imageRendering = `-o-crisp-edges`
|
||||
canvas.style.imageRendering = `-webkit-optimize-contrast`
|
||||
canvas.style.imageRendering = `optimize-contrast`
|
||||
canvas.style.imageRendering = `crisp-edges`
|
||||
canvas.style.imageRendering = `pixelated`
|
||||
canvas.style.imageRendering = `optimizeSpeed`
|
||||
|
||||
canvas.style.width = `${resolution.width * scale}px`
|
||||
canvas.style.height = `${resolution.height * scale}px`
|
||||
canvas.width = resolution.width
|
||||
canvas.height = resolution.height
|
||||
}
|
||||
}
|
||||
|
||||
function setupSplash(canvas, splash) {
|
||||
splash.splashElt = document.getElementById(`doseeSplashScreen`)
|
||||
if (!splash.splashElt) {
|
||||
splash.splashElt = document.createElement(`div`)
|
||||
splash.splashElt.setAttribute(`id`, `doseeSplashScreen`)
|
||||
splash.splashElt.style.position = `absolute`
|
||||
splash.splashElt.style.top = `${canvas.offsetTop}px`
|
||||
splash.splashElt.style.left = `${canvas.offsetLeft}px`
|
||||
splash.splashElt.style.width = `${canvas.offsetWidth}px`
|
||||
splash.splashElt.style.color = splash.getColor(`foreground`)
|
||||
splash.splashElt.style.backgroundColor = splash.getColor(`background`)
|
||||
canvas.parentElement.appendChild(splash.splashElt)
|
||||
}
|
||||
|
||||
splash.splashimg.setAttribute(`id`, `doseeSplashImg`)
|
||||
splash.splashimg.style.display = `block`
|
||||
splash.splashimg.style.marginLeft = `auto`
|
||||
splash.splashimg.style.marginRight = `auto`
|
||||
splash.splashElt.appendChild(splash.splashimg)
|
||||
|
||||
splash.titleElt = document.createElement(`span`)
|
||||
splash.titleElt.setAttribute(`id`, `doseeSplashTitle`)
|
||||
splash.titleElt.style.display = `block`
|
||||
splash.titleElt.style.width = `100%`
|
||||
splash.titleElt.style.marginTop = `1em`
|
||||
splash.titleElt.style.marginBottom = `1em`
|
||||
splash.titleElt.style.textAlign = `center`
|
||||
splash.titleElt.style.font = `24px sans-serif`
|
||||
splash.titleElt.textContent = ``
|
||||
splash.splashElt.appendChild(splash.titleElt)
|
||||
|
||||
let table = document.getElementById(`doseeProgressIndicator`)
|
||||
if (!table) {
|
||||
table = document.createElement(`table`)
|
||||
table.setAttribute(`id`, `doseeProgressIndicator`)
|
||||
table.style.width = `50%`
|
||||
table.style.color = splash.getColor(`foreground`)
|
||||
table.style.backgroundColor = splash.getColor(`background`)
|
||||
table.style.marginLeft = `auto`
|
||||
table.style.marginRight = `auto`
|
||||
table.style.borderCollapse = `separate`
|
||||
table.style.borderSpacing = `2px`
|
||||
splash.splashElt.appendChild(table)
|
||||
}
|
||||
splash.table = table
|
||||
}
|
||||
|
||||
splash.setTitle = function (title) {
|
||||
splash.titleElt.textContent = title
|
||||
}
|
||||
|
||||
splash.hide = function () {
|
||||
splash.splashElt.style.display = `none`
|
||||
}
|
||||
|
||||
splash.getColor = function (name) {
|
||||
return name in splash.colors ? splash.colors[name] : defaultSplashColors[name]
|
||||
}
|
||||
|
||||
const addRow = function (table) {
|
||||
const row = table.insertRow(-1)
|
||||
row.style.textAlign = `center`
|
||||
const cell = row.insertCell(-1)
|
||||
cell.style.position = `relative`
|
||||
const titleCell = document.createElement(`span`)
|
||||
titleCell.textContent = `—`
|
||||
titleCell.style.verticalAlign = `center`
|
||||
titleCell.style.minHeight = `24px`
|
||||
cell.appendChild(titleCell)
|
||||
const statusCell = document.createElement(`span`)
|
||||
statusCell.style.position = `absolute`
|
||||
statusCell.style.left = `0`
|
||||
statusCell.style.paddingLeft = `0.5em`
|
||||
cell.appendChild(statusCell)
|
||||
return [titleCell, statusCell]
|
||||
}
|
||||
|
||||
const drawsplash = function () {
|
||||
canvas.setAttribute(`moz-opaque`, ``)
|
||||
if (!splash.splashimg.src) {
|
||||
splash.splashimg.src = `images/floppy_disk_icon-180x180.png`
|
||||
}
|
||||
}
|
||||
|
||||
function attach_script(js_url) {
|
||||
if (js_url) {
|
||||
const head = document.getElementsByTagName(`head`)[0]
|
||||
const newScript = document.createElement(`script`)
|
||||
newScript.type = `text/javascript`
|
||||
newScript.src = js_url
|
||||
head.appendChild(newScript)
|
||||
}
|
||||
}
|
||||
|
||||
function getpointerlockenabler() {
|
||||
return canvas.requestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock
|
||||
}
|
||||
|
||||
function getfullscreenenabler() {
|
||||
return canvas.webkitRequestFullScreen || canvas.mozRequestFullScreen || canvas.requestFullScreen
|
||||
}
|
||||
|
||||
this.isfullscreensupported = function () {
|
||||
return !!(getfullscreenenabler())
|
||||
}
|
||||
|
||||
function setupFullScreen() {
|
||||
const fullScreenChangeHandler = function () {
|
||||
if (!(document.mozFullScreenElement || document.fullScreenElement)) {
|
||||
resizeCanvas(canvas, scale, cssResolution, aspectRatio)
|
||||
}
|
||||
}
|
||||
if (`onfullscreenchange` in document) {
|
||||
document.addEventListener(`fullscreenchange`, fullScreenChangeHandler)
|
||||
} else if (`onmozfullscreenchange` in document) {
|
||||
document.addEventListener(`mozfullscreenchange`, fullScreenChangeHandler)
|
||||
} else if (`onwebkitfullscreenchange` in document) {
|
||||
document.addEventListener(`webkitfullscreenchange`, fullScreenChangeHandler)
|
||||
}
|
||||
}
|
||||
|
||||
this.requestFullScreen = function () {
|
||||
Module.requestFullScreen(1, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents page navigation keys such as page up/page down from
|
||||
* moving the page while the user is playing.
|
||||
*/
|
||||
function blockSomeKeys() {
|
||||
function keypress(e) {
|
||||
if (e.which >= 33 && e.which <= 40) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
window.onkeydown = keypress
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the right click menu for the given element.
|
||||
*/
|
||||
function disableRightClickContextMenu(element) {
|
||||
element.addEventListener(`contextmenu`,
|
||||
function (e) {
|
||||
if (e.button == 2) {
|
||||
// Block right-click menu thru preventing default action.
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* misc
|
||||
*/
|
||||
function BFSOpenZip(loadedData) {
|
||||
return new BrowserFS.FileSystem.ZipFS(loadedData)
|
||||
}
|
||||
|
||||
// This is such a hack. We're not calling the BrowserFS api
|
||||
// 'correctly', so we have to synthesize these flags ourselves
|
||||
const flag_r = {
|
||||
isReadable: function () { return true },
|
||||
isWriteable: function () { return false },
|
||||
isTruncating: function () { return false },
|
||||
isAppendable: function () { return false },
|
||||
isSynchronous: function () { return false },
|
||||
isExclusive: function () { return false },
|
||||
pathExistsAction: function () { return 0 },
|
||||
pathNotExistsAction: function () { return 1 },
|
||||
}
|
||||
const flag_w = {
|
||||
isReadable: function () { return false },
|
||||
isWriteable: function () { return true },
|
||||
isTruncating: function () { return false },
|
||||
isAppendable: function () { return false },
|
||||
isSynchronous: function () { return false },
|
||||
isExclusive: function () { return false },
|
||||
pathExistsAction: function () { return 0 },
|
||||
pathNotExistsAction: function () { return 3 },
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for dosbox.conf, and moves it to '/dosbox.conf'
|
||||
so dosbox uses it.
|
||||
*/
|
||||
function moveConfigToRoot(fs) {
|
||||
let dosboxConfPath = null
|
||||
// Recursively search for dosbox.conf.
|
||||
function searchDirectory(dirPath) {
|
||||
fs.readdirSync(dirPath).forEach(function (item) {
|
||||
if (dosboxConfPath) return
|
||||
// Avoid infinite recursion by ignoring these entries, which exist at
|
||||
// the root.
|
||||
if (item === `.` || item === `..`) return
|
||||
// Append `/` between dirPath and the item's name... unless dirPath
|
||||
// already ends in it (which always occurs if dirPath is the root, `/`).
|
||||
const itemPath = dirPath + (dirPath[dirPath.length - 1] !== `/` ? `/` : ``) + item, itemStat = fs.statSync(itemPath)
|
||||
if (itemStat.isDirectory(itemStat.mode)) {
|
||||
searchDirectory(itemPath)
|
||||
} else if (item === `dosbox.conf`) {
|
||||
dosboxConfPath = itemPath
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
searchDirectory(`/`)
|
||||
|
||||
if (dosboxConfPath !== null) {
|
||||
fs.writeFileSync(`/dosbox.conf`, fs.readFileSync(dosboxConfPath, null, flag_r), null, flag_w, 0x1a4)
|
||||
}
|
||||
}
|
||||
|
||||
function extend(a, b) {
|
||||
if (a === null) return b
|
||||
if (b === null) return a
|
||||
const ta = typeof a,
|
||||
tb = typeof b
|
||||
if (ta !== tb) {
|
||||
if (ta === `undefined`) return b
|
||||
if (tb === `undefined`) return a
|
||||
throw new Error(`Cannot extend an ${ta} with an ${tb}`)
|
||||
}
|
||||
if (Array.isArray(a)) return a.concat(b)
|
||||
if (ta === `object`) {
|
||||
Object.keys(b).forEach(function (k) { a[k] = extend(a[k], b[k]) })
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
document.getElementById(`doseeVersion`).innerHTML = ` version ${doseeVersion}`
|
||||
window.DoseeLoader = DoseeLoader
|
||||
window.Emulator = Emulator
|
||||
})(typeof Promise === `undefined` ? ES6Promise.Promise : Promise)
|
BIN
dragglex_loader.zip
Normal file
BIN
dragglex_loader.zip
Normal file
Binary file not shown.
28
emulator/dosee-core.js
Normal file
28
emulator/dosee-core.js
Normal file
File diff suppressed because one or more lines are too long
BIN
emulator/dosee-core.mem
Normal file
BIN
emulator/dosee-core.mem
Normal file
Binary file not shown.
24
fetch-libs.sh
Normal file
24
fetch-libs.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BFS="https://github.com/jvilk/BrowserFS/releases/download/v1.4.3/browserfs.min.js"
|
||||
ZFS="https://github.com/jvilk/browserfs-zipfs-extras/releases/download/v1.0.1/browserfs-zipfs-extras.js"
|
||||
MAP="https://github.com/jvilk/browserfs-zipfs-extras/releases/download/v1.0.1/browserfs-zipfs-extras.js.map"
|
||||
FULL="https://raw.githubusercontent.com/sindresorhus/screenfull.js/gh-pages/dist/screenfull.min.js"
|
||||
SAVE="https://raw.githubusercontent.com/eligrey/FileSaver.js/master/src/FileSaver.js"
|
||||
CTB="https://raw.githubusercontent.com/eligrey/canvas-toBlob.js/master/canvas-toBlob.js"
|
||||
|
||||
echo "Fetching BrowserFS"
|
||||
wget -nc -O libs/browserfs.min.js $BFS
|
||||
|
||||
echo "Fetching BrowserFS ZipFS"
|
||||
wget -nc -O libs/browserfs-zipfs-extras.js $ZFS
|
||||
wget -nc -O libs/browserfs-zipfs-extras.js.map $MAP
|
||||
|
||||
echo "Fetching screenfull"
|
||||
wget -nc -O libs/screenfull.min.js $FULL
|
||||
|
||||
echo "Fetching FileSaver (needs to be minified)"
|
||||
wget -nc -O libs/FileSaver.min.js $SAVE
|
||||
|
||||
echo "Fetching canvas-toBlob (needs to be minified)"
|
||||
wget -nc -O libs/canvas-toBlob.min.js $CTB
|
BIN
images/floppy_disk_icon-180x180.png
Normal file
BIN
images/floppy_disk_icon-180x180.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 329 B |
231
index.html
Normal file
231
index.html
Normal file
@ -0,0 +1,231 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<!--
|
||||
Archive.org
|
||||
DOSBox version 8fedaa0 built with Emscripten 1.37.3 04255b4
|
||||
Copyright 2002-2015 DOSBox Team, published under GNU GPL.
|
||||
CONFIG: Generating default configuration.
|
||||
Writing it to /home/web_user/.dosbox/dosbox-SVN.conf
|
||||
CONFIG:Loading primary settings from config file /home/web_user/.dosbox/dosbox-SVN.conf
|
||||
SDL:Current window pixel format: SDL_PIXELFORMAT_RGB888
|
||||
MIDI: Opened device:none
|
||||
DOSBox has switched to max cycles, because of the setting: cycles=auto.
|
||||
If the game runs too fast, try a fixed cycles amount in DOSBox's options.
|
||||
Emulation aborted due to nested emulation timeout.
|
||||
|
||||
Local:
|
||||
DOSBox version 30e85e1M built with Emscripten 1.37.0 6dc4ac5
|
||||
Copyright 2002-2015 DOSBox Team, published under GNU GPL.
|
||||
CONFIG:Loading primary settings from config file /dos/s/render.con
|
||||
CONFIG:Loading additional settings from config file /dos/s/cpu-auto.con
|
||||
CONFIG:Loading additional settings from config file /dos/s/sb16.con
|
||||
CONFIG:Loading additional settings from config file /dos/s/vgaonly.con
|
||||
SDL:Current window pixel format: SDL_PIXELFORMAT_RGB888
|
||||
MIDI: Opened device:none
|
||||
-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DOSee running example</title>
|
||||
<meta name="description" content="DOSee">
|
||||
<meta name="author" content="Ben Garrett">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- DOSee initialisation options -->
|
||||
<meta name="dosee:capname" content="b12599c.png">
|
||||
<meta name="dosee:filename" content="dragglex_loader.zip">
|
||||
<meta name="dosee:gamefilepath" content="dragglex_loader.zip">
|
||||
<meta name="dosee:gusaudio" content="false">
|
||||
<meta name="dosee:resolution" content="640, 480">
|
||||
<meta name="dosee:startexe" content="DRAGGLEX.EXE">
|
||||
<meta name="dosee:utils" content="false">
|
||||
|
||||
<!--- Preloading content to for page load performance improvement -->
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content -->
|
||||
<!-- <link rel="preload" href="emulator/dosee-core.js" as="script">
|
||||
<link rel="preload" href="emulator/dosee-core.mem" as="fetch" type="application/octet-stream">
|
||||
<link rel="preload" href="disk_drives/s_drive.zip" as="fetch" type="application/zip">
|
||||
<link rel="preload" href="libs/browserfs.min.js" as="script">
|
||||
<link rel="preload" href="libs/browserfs-zipfs-extras.js" as="script">
|
||||
<link rel="preload" href="dosee-loader.js" as="script">
|
||||
<link rel="preload" href="libs/screenfull.min.js" as="script">
|
||||
<link rel="preload" href="dosee-functions.js" as="script">
|
||||
<link rel="preload" href="libs/FileSaver.min.js" as="script">
|
||||
<link rel="preload" href="libs/canvas-toBlob.min.js" as="script"> -->
|
||||
|
||||
<!-- Javascripts -->
|
||||
<script src="index.js" defer></script>
|
||||
|
||||
<!-- "minimal, responsive, style-agnostic CSS framework" https://minicss.org/docs -->
|
||||
<link rel="stylesheet" href="libs/mini.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>DOSee</p>
|
||||
<hr>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm">Emulating ... Something</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="doseeCrashed">Oops!</div>
|
||||
<p id="doseeSlowLoad">
|
||||
<small class="text-warning"> If the emulation is taking too long to load,
|
||||
<a href="#XMLFormat(dosee.url)#">you can turn it off</a>.</small>
|
||||
</p>
|
||||
<canvas id="doseeCanvas" class="rounded" style="width:640px; height: 480px; background-color: #000"></canvas>
|
||||
<header id="doseeTabs">
|
||||
<a href="#" id="hwButton" class="button tabtoggle">Hardware</a>
|
||||
<a href="#" id="optButton" class="button tabtoggle">Options</a>
|
||||
<a href="#" class="button tabtoggle">Help</a>
|
||||
<a href="#" class="button" id="doseeFullScreen">Fullscreen</a>
|
||||
<a href="#" class="button" id="doseeCaptureScreen">Capture</a>
|
||||
<a href="#" class="button" id="doseereboot">Reboot</a>
|
||||
</header>
|
||||
<form id="hardware">
|
||||
<fieldset>
|
||||
<legend>CPU Speed</legend>
|
||||
<label for="dosspeed2">
|
||||
<input type="radio" name="dosspeed" id="dosspeed2" value="max"> maximum (80486)
|
||||
</label>
|
||||
<label for="dosspeed3">
|
||||
<input type="radio" name="dosspeed" id="dosspeed3" value="386"> medium (80386)
|
||||
</label>
|
||||
<label for="dosspeed4">
|
||||
<input type="radio" name="dosspeed" id="dosspeed4" value="8086"> slow (8086)
|
||||
</label>
|
||||
<label for="dosspeed1">
|
||||
<input type="radio" name="dosspeed" id="dosspeed1" value="auto">
|
||||
<u>automatic</u>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Graphics</legend>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosmachine" id="dosmachine1" value="svga"> SuperVGA
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosmachine" id="dosmachine2" value="vga">
|
||||
<u>VGA</u>
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosmachine" id="dosmachine3" value="ega"> EGA
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosmachine" id="dosmachine4" value="tandy"> Tandy
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosmachine" id="dosmachine5" value="cga"> CGA
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosmachine" id="dosmachine6" value="herc"> Hercules
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Audio</legend>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosaudio" id="dosaudio1" value="gus"> Gravis Ultrasound
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosaudio" id="dosaudio4" value="covox"> Covox
|
||||
<small>also Disney Sound Source/DA Converter</small>
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosaudio" id="dosaudio2" value="sb16">
|
||||
<u>Sound Blaster 16</u>
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosaudio" id="dosaudio3" value="sb1"> Sound Blaster 1.0
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosaudio" id="dosaudio5" value="none"> none
|
||||
</label>
|
||||
</fieldset>
|
||||
<input type="submit" class="small primary" value="Apply changes">
|
||||
</form>
|
||||
<form id="options">
|
||||
<p>Changes will not be applied until this browser tab has been refreshed</p>
|
||||
<hr>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="doseeAutoRun"> Automatically start DOS emulation
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="doseeAspect"> Disable aspect correction
|
||||
<small class="text-muted">For sharper DOS ASCII/ANSI text</small>
|
||||
</label>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail3" class="col-sm-12 control-label">SuperVGA real-time graphic effect
|
||||
<br>
|
||||
<small class="text-danger" id="svgaEffectsMsg">Only works when Hardware 🡒 Graphics = SuperVGA</small>
|
||||
</label>
|
||||
<div class="col-sm-12">
|
||||
<div class="radio">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosscale" id="dosscale0" value="none"> None
|
||||
</label>
|
||||
<br>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosscale" id="dosscale5" value="advinterp3x"> Advanced interpolation
|
||||
<small class="text-muted">advinterp3x</small>
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosscale" id="dosscale4" value="hq3x"> High Quality 3 magnification
|
||||
<small class="text-muted">hq3x</small>
|
||||
</label>
|
||||
<br>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosscale" id="dosscale3" value="rgb3x"> RGB
|
||||
<small class="text-muted">rgb3x</small>
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosscale" id="dosscale1" value="super2xsai"> Super scale and interpolation
|
||||
<small class="text-muted">super2xsai</small>
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="dosscale" id="dosscale2" value="tv3x"> Television
|
||||
<small class="text-muted">tv3x</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="helpBlock" class="help-block">
|
||||
<p>
|
||||
<i class="fal fa-info-circle"></i>
|
||||
<a href="https://www.dosbox.com/wiki/Scaler">Graphic effect comparisons</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<footer>© 2018 - Ben Garrett |
|
||||
<a href="">GitHub Repo</a>
|
||||
<p>
|
||||
<small>DOSee
|
||||
<span id="doseeVersion"></span>, built on The Emularity, EM-DOSBox and DOSBox. Capture screenshot and save function built on
|
||||
<a href="https://github.com/eligrey/canvas-toBlob.js">canvas-toBlob.js</a>.</small>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<!-- these javascripts are placed at end of the page for a better user load page experience -->
|
||||
<!-- they need to be loaded in this sequence and will not work with async or deferred loading -->
|
||||
|
||||
<!-- dosee loader -->
|
||||
<script src="dosee-loader.js"></script>
|
||||
<!-- browserfs -->
|
||||
<script src="libs/browserfs.min.js"></script>
|
||||
<!-- browserfs zipfs -->
|
||||
<script src="libs/browserfs-zipfs-extras.js"></script>
|
||||
<!-- screenfull -->
|
||||
<script src="libs/screenfull.min.js"></script>
|
||||
<!-- dosee functions -->
|
||||
<script src="dosee-functions.js"></script>
|
||||
<!-- dosee initialisation -->
|
||||
<script src="dosee-init.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
26
index.js
Normal file
26
index.js
Normal file
@ -0,0 +1,26 @@
|
||||
`use strict`
|
||||
{
|
||||
|
||||
(function () {
|
||||
const hw = document.getElementById(`hardware`)
|
||||
const opt = document.getElementById(`options`)
|
||||
const hwBut = document.getElementById(`hwButton`)
|
||||
const optBut = document.getElementById(`optButton`)
|
||||
|
||||
console.info(`index.js v1.0 loaded`)
|
||||
// document.getElementById(`hardware`)
|
||||
hwBut.onclick = function () {
|
||||
resetMenu()
|
||||
hw.style.display = "block"
|
||||
}
|
||||
optBut.onclick = function () {
|
||||
resetMenu()
|
||||
opt.style.display = "block"
|
||||
}
|
||||
|
||||
resetMenu = function () {
|
||||
hw.style.display = "none"
|
||||
opt.style.display = "none"
|
||||
}
|
||||
})()
|
||||
}
|
182
libs/FileSaver.min.js
vendored
Normal file
182
libs/FileSaver.min.js
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
/* FileSaver.js
|
||||
* A saveAs() FileSaver implementation.
|
||||
* 1.3.8
|
||||
* 2018-03-22 14:03:47
|
||||
*
|
||||
* By Eli Grey, https://eligrey.com
|
||||
* License: MIT
|
||||
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self */
|
||||
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/src/FileSaver.js */
|
||||
|
||||
export var saveAs = saveAs || (function(view) {
|
||||
"use strict";
|
||||
// IE <10 is explicitly unsupported
|
||||
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
|
||||
return;
|
||||
}
|
||||
var
|
||||
doc = view.document
|
||||
// only get URL when necessary in case Blob.js hasn't overridden it yet
|
||||
, get_URL = function() {
|
||||
return view.URL || view.webkitURL || view;
|
||||
}
|
||||
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
||||
, can_use_save_link = "download" in save_link
|
||||
, click = function(node) {
|
||||
var event = new MouseEvent("click");
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
|
||||
, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
|
||||
, setImmediate = view.setImmediate || view.setTimeout
|
||||
, throw_outside = function(ex) {
|
||||
setImmediate(function() {
|
||||
throw ex;
|
||||
}, 0);
|
||||
}
|
||||
, force_saveable_type = "application/octet-stream"
|
||||
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
|
||||
, arbitrary_revoke_timeout = 1000 * 40 // in ms
|
||||
, revoke = function(file) {
|
||||
var revoker = function() {
|
||||
if (typeof file === "string") { // file is an object URL
|
||||
get_URL().revokeObjectURL(file);
|
||||
} else { // file is a File
|
||||
file.remove();
|
||||
}
|
||||
};
|
||||
setTimeout(revoker, arbitrary_revoke_timeout);
|
||||
}
|
||||
, dispatch = function(filesaver, event_types, event) {
|
||||
event_types = [].concat(event_types);
|
||||
var i = event_types.length;
|
||||
while (i--) {
|
||||
var listener = filesaver["on" + event_types[i]];
|
||||
if (typeof listener === "function") {
|
||||
try {
|
||||
listener.call(filesaver, event || filesaver);
|
||||
} catch (ex) {
|
||||
throw_outside(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
, auto_bom = function(blob) {
|
||||
// prepend BOM for UTF-8 XML and text/* types (including HTML)
|
||||
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
|
||||
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
|
||||
return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
, FileSaver = function(blob, name, no_auto_bom) {
|
||||
if (!no_auto_bom) {
|
||||
blob = auto_bom(blob);
|
||||
}
|
||||
// First try a.download, then web filesystem, then object URLs
|
||||
var
|
||||
filesaver = this
|
||||
, type = blob.type
|
||||
, force = type === force_saveable_type
|
||||
, object_url
|
||||
, dispatch_all = function() {
|
||||
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
||||
}
|
||||
// on any filesys errors revert to saving with object URLs
|
||||
, fs_error = function() {
|
||||
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
|
||||
// Safari doesn't allow downloading of blob urls
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function() {
|
||||
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
|
||||
var popup = view.open(url, '_blank');
|
||||
if(!popup) view.location.href = url;
|
||||
url=undefined; // release reference before dispatching
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
filesaver.readyState = filesaver.INIT;
|
||||
return;
|
||||
}
|
||||
// don't create more object URLs than needed
|
||||
if (!object_url) {
|
||||
object_url = get_URL().createObjectURL(blob);
|
||||
}
|
||||
if (force) {
|
||||
view.location.href = object_url;
|
||||
} else {
|
||||
var opened = view.open(object_url, "_blank");
|
||||
if (!opened) {
|
||||
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
|
||||
view.location.href = object_url;
|
||||
}
|
||||
}
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
revoke(object_url);
|
||||
}
|
||||
;
|
||||
filesaver.readyState = filesaver.INIT;
|
||||
|
||||
if (can_use_save_link) {
|
||||
object_url = get_URL().createObjectURL(blob);
|
||||
setImmediate(function() {
|
||||
save_link.href = object_url;
|
||||
save_link.download = name;
|
||||
click(save_link);
|
||||
dispatch_all();
|
||||
revoke(object_url);
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
fs_error();
|
||||
}
|
||||
, FS_proto = FileSaver.prototype
|
||||
, saveAs = function(blob, name, no_auto_bom) {
|
||||
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
|
||||
}
|
||||
;
|
||||
|
||||
// IE 10+ (native saveAs)
|
||||
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
|
||||
return function(blob, name, no_auto_bom) {
|
||||
name = name || blob.name || "download";
|
||||
|
||||
if (!no_auto_bom) {
|
||||
blob = auto_bom(blob);
|
||||
}
|
||||
return navigator.msSaveOrOpenBlob(blob, name);
|
||||
};
|
||||
}
|
||||
|
||||
// todo: detect chrome extensions & packaged apps
|
||||
//save_link.target = "_blank";
|
||||
|
||||
FS_proto.abort = function(){};
|
||||
FS_proto.readyState = FS_proto.INIT = 0;
|
||||
FS_proto.WRITING = 1;
|
||||
FS_proto.DONE = 2;
|
||||
|
||||
FS_proto.error =
|
||||
FS_proto.onwritestart =
|
||||
FS_proto.onprogress =
|
||||
FS_proto.onwrite =
|
||||
FS_proto.onabort =
|
||||
FS_proto.onerror =
|
||||
FS_proto.onwriteend =
|
||||
null;
|
||||
|
||||
return saveAs;
|
||||
}(
|
||||
typeof self !== "undefined" && self
|
||||
|| typeof window !== "undefined" && window
|
||||
|| this
|
||||
));
|
1932
libs/browserfs-zipfs-extras.js
Normal file
1932
libs/browserfs-zipfs-extras.js
Normal file
File diff suppressed because it is too large
Load Diff
14
libs/browserfs.min.js
vendored
Normal file
14
libs/browserfs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
125
libs/canvas-toBlob.min.js
vendored
Normal file
125
libs/canvas-toBlob.min.js
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/* canvas-toBlob.js
|
||||
* A canvas.toBlob() implementation.
|
||||
* 2016-05-26
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
|
||||
* License: MIT
|
||||
* See https://github.com/eligrey/canvas-toBlob.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self */
|
||||
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
||||
plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */
|
||||
|
||||
(function(view) {
|
||||
"use strict";
|
||||
var
|
||||
Uint8Array = view.Uint8Array
|
||||
, HTMLCanvasElement = view.HTMLCanvasElement
|
||||
, canvas_proto = HTMLCanvasElement && HTMLCanvasElement.prototype
|
||||
, is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i
|
||||
, to_data_url = "toDataURL"
|
||||
, base64_ranks
|
||||
, decode_base64 = function(base64) {
|
||||
var
|
||||
len = base64.length
|
||||
, buffer = new Uint8Array(len / 4 * 3 | 0)
|
||||
, i = 0
|
||||
, outptr = 0
|
||||
, last = [0, 0]
|
||||
, state = 0
|
||||
, save = 0
|
||||
, rank
|
||||
, code
|
||||
, undef
|
||||
;
|
||||
while (len--) {
|
||||
code = base64.charCodeAt(i++);
|
||||
rank = base64_ranks[code-43];
|
||||
if (rank !== 255 && rank !== undef) {
|
||||
last[1] = last[0];
|
||||
last[0] = code;
|
||||
save = (save << 6) | rank;
|
||||
state++;
|
||||
if (state === 4) {
|
||||
buffer[outptr++] = save >>> 16;
|
||||
if (last[1] !== 61 /* padding character */) {
|
||||
buffer[outptr++] = save >>> 8;
|
||||
}
|
||||
if (last[0] !== 61 /* padding character */) {
|
||||
buffer[outptr++] = save;
|
||||
}
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2/3 chance there's going to be some null bytes at the end, but that
|
||||
// doesn't really matter with most image formats.
|
||||
// If it somehow matters for you, truncate the buffer up outptr.
|
||||
return buffer;
|
||||
}
|
||||
;
|
||||
if (Uint8Array) {
|
||||
base64_ranks = new Uint8Array([
|
||||
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
|
||||
, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
|
||||
, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
|
||||
, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||
]);
|
||||
}
|
||||
if (HTMLCanvasElement && (!canvas_proto.toBlob || !canvas_proto.toBlobHD)) {
|
||||
if (!canvas_proto.toBlob)
|
||||
canvas_proto.toBlob = function(callback, type /*, ...args*/) {
|
||||
if (!type) {
|
||||
type = "image/png";
|
||||
} if (this.mozGetAsFile) {
|
||||
callback(this.mozGetAsFile("canvas", type));
|
||||
return;
|
||||
} if (this.msToBlob && /^\s*image\/png\s*(?:$|;)/i.test(type)) {
|
||||
callback(this.msToBlob());
|
||||
return;
|
||||
}
|
||||
|
||||
var
|
||||
args = Array.prototype.slice.call(arguments, 1)
|
||||
, dataURI = this[to_data_url].apply(this, args)
|
||||
, header_end = dataURI.indexOf(",")
|
||||
, data = dataURI.substring(header_end + 1)
|
||||
, is_base64 = is_base64_regex.test(dataURI.substring(0, header_end))
|
||||
, blob
|
||||
;
|
||||
if (Blob.fake) {
|
||||
// no reason to decode a data: URI that's just going to become a data URI again
|
||||
blob = new Blob
|
||||
if (is_base64) {
|
||||
blob.encoding = "base64";
|
||||
} else {
|
||||
blob.encoding = "URI";
|
||||
}
|
||||
blob.data = data;
|
||||
blob.size = data.length;
|
||||
} else if (Uint8Array) {
|
||||
if (is_base64) {
|
||||
blob = new Blob([decode_base64(data)], {type: type});
|
||||
} else {
|
||||
blob = new Blob([decodeURIComponent(data)], {type: type});
|
||||
}
|
||||
}
|
||||
callback(blob);
|
||||
};
|
||||
|
||||
if (!canvas_proto.toBlobHD && canvas_proto.toDataURLHD) {
|
||||
canvas_proto.toBlobHD = function() {
|
||||
to_data_url = "toDataURLHD";
|
||||
var blob = this.toBlob();
|
||||
to_data_url = "toDataURL";
|
||||
return blob;
|
||||
}
|
||||
} else {
|
||||
canvas_proto.toBlobHD = canvas_proto.toBlob;
|
||||
}
|
||||
}
|
||||
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
|
1
libs/mini.min.css
vendored
Normal file
1
libs/mini.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
libs/screenfull.min.js
vendored
Normal file
7
libs/screenfull.min.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* screenfull
|
||||
* v3.3.2 - 2017-10-27
|
||||
* (c) Sindre Sorhus; MIT License
|
||||
*/
|
||||
|
||||
!function(){"use strict";var a="undefined"!=typeof window&&void 0!==window.document?window.document:{},b="undefined"!=typeof module&&module.exports,c="undefined"!=typeof Element&&"ALLOW_KEYBOARD_INPUT"in Element,d=function(){for(var b,c=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],d=0,e=c.length,f={};d<e;d++)if((b=c[d])&&b[1]in a){for(d=0;d<b.length;d++)f[c[0][d]]=b[d];return f}return!1}(),e={change:d.fullscreenchange,error:d.fullscreenerror},f={request:function(b){var e=d.requestFullscreen;b=b||a.documentElement,/ Version\/5\.1(?:\.\d+)? Safari\//.test(navigator.userAgent)?b[e]():b[e](c&&Element.ALLOW_KEYBOARD_INPUT)},exit:function(){a[d.exitFullscreen]()},toggle:function(a){this.isFullscreen?this.exit():this.request(a)},onchange:function(a){this.on("change",a)},onerror:function(a){this.on("error",a)},on:function(b,c){var d=e[b];d&&a.addEventListener(d,c,!1)},off:function(b,c){var d=e[b];d&&a.removeEventListener(d,c,!1)},raw:d};if(!d)return void(b?module.exports=!1:window.screenfull=!1);Object.defineProperties(f,{isFullscreen:{get:function(){return Boolean(a[d.fullscreenElement])}},element:{enumerable:!0,get:function(){return a[d.fullscreenElement]}},enabled:{enumerable:!0,get:function(){return Boolean(a[d.fullscreenEnabled])}}}),b?module.exports=f:window.screenfull=f}();
|
Loading…
x
Reference in New Issue
Block a user