update
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>My wonderful page</title>
|
||||
<script src="jdataview.js"></script>
|
||||
<script src="jsspeccy-core.min.js"></script>
|
||||
<script src="jquery-1.7.2.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
var jsspeccy = JSSpeccy('speccy', {
|
||||
'autostart': false,
|
||||
'autoload': true,
|
||||
'scaleFactor': 2,
|
||||
'loadFile': 'scroller1.tap'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="speccy"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,107 @@
|
|||
.jsspeccy {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 11pt;
|
||||
position: relative;
|
||||
width: 640px;
|
||||
}
|
||||
.jsspeccy canvas {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.jsspeccy .toolbar {
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #445;
|
||||
list-style-type: none;
|
||||
}
|
||||
.jsspeccy .toolbar li {
|
||||
float: left;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.jsspeccy .toolbar button {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
text-indent: -5000px;
|
||||
direction: ltr;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.jsspeccy .toolbar label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.jsspeccy .toolbar .stop {
|
||||
background-image: url(images/48x48_player_pause.png);
|
||||
}
|
||||
.jsspeccy .toolbar .start {
|
||||
background-image: url(images/48x48_player_play.png);
|
||||
}
|
||||
.jsspeccy .toolbar .reset {
|
||||
background-image: url(images/48x48_kaboodleloop.png);
|
||||
}
|
||||
.jsspeccy .toolbar .audio {
|
||||
background-image: url(images/48x48_sound_off.png);
|
||||
}
|
||||
.jsspeccy .toolbar .audio.enabled {
|
||||
background-image: url(images/48x48_sound_on.png);
|
||||
}
|
||||
.jsspeccy .toolbar .open {
|
||||
background-image: url(images/48x48_folder_blue_open.png);
|
||||
}
|
||||
.jsspeccy .toolbar .about {
|
||||
background-image: url(images/48x48_messagebox_info.png);
|
||||
}
|
||||
|
||||
.jsspeccy .panel {
|
||||
top: 30px;
|
||||
left: 50%;
|
||||
margin-left: -300px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
width: 580px;
|
||||
height: 400px;
|
||||
background-color: white;
|
||||
color: black;
|
||||
display: none;
|
||||
position: absolute;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.jsspeccy .panel .close {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.jsspeccy .panel h1, .jsspeccy .panel h2 {
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
.jsspeccy .panel .note {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.jsspeccy .about {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.jsspeccy .open-file .search-wos input[type=search] {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.jsspeccy .about h1 {
|
||||
font-size: 18pt;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.jsspeccy .about h2 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
.jsspeccy .about .licence {
|
||||
font-size: 9pt;
|
||||
}
|
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 21 KiB |
173
index.html
|
@ -1,23 +1,180 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>My wonderful page</title>
|
||||
<script src="jdataview.js"></script>
|
||||
<script src="jsspeccy-core.min.js"></script>
|
||||
<script src="jquery-1.7.2.min.js"></script>
|
||||
<title>jsspeccy</title>
|
||||
<link rel="stylesheet" href="css/jsspeccy.css">
|
||||
<script src="js/jdataview.js"></script>
|
||||
<script src="js/jsspeccy-core.min.js"></script>
|
||||
|
||||
<script src="js/jquery-1.7.2.min.js"></script>
|
||||
<script src="js/ui.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background-color: #222;
|
||||
color: #ddd;
|
||||
}
|
||||
h1, h2 {
|
||||
padding: 0; margin: 0;
|
||||
}
|
||||
a:link {
|
||||
color: #77f;
|
||||
}
|
||||
a:visited {
|
||||
color: #97f;
|
||||
}
|
||||
a:hover, a:active {
|
||||
color: #99f;
|
||||
}
|
||||
#jsspeccy {
|
||||
margin: 10px;
|
||||
padding-right: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.commentary {
|
||||
float: left;
|
||||
width: 350px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.commentary h1 {
|
||||
background-image: url('images/spectrum48k.png');
|
||||
line-height: 100px;
|
||||
padding-left: 138px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.commentary .version {
|
||||
font-size: 0.4em;
|
||||
}
|
||||
.commentary h2 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
.commentary small {
|
||||
font-size: 0.8em;
|
||||
color: #ccc;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
$(function() {
|
||||
var jsspeccy = JSSpeccy('speccy', {
|
||||
'autostart': false,
|
||||
var jsspeccy = JSSpeccy('jsspeccy-viewport', {
|
||||
'autostart': true,
|
||||
'autoload': true,
|
||||
'scaleFactor': 1,
|
||||
'loadFile': 'scroller1.tap'
|
||||
});
|
||||
|
||||
JSSpeccy.UI({
|
||||
container: 'jsspeccy',
|
||||
controller: jsspeccy
|
||||
});
|
||||
$('#example-games').change(function() {
|
||||
var filename = $(this).val();
|
||||
if (filename) {
|
||||
jsspeccy.loadFromUrl(
|
||||
'http://jsspeccy.zxdemo.org/games/' + filename,
|
||||
{'autoload': true}
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="speccy"></div>
|
||||
<div id="jsspeccy">
|
||||
<div id="jsspeccy-viewport"></div>
|
||||
<ul class="toolbar">
|
||||
<li><button class="stop-start" title="Stop / start">stop / start</button></li>
|
||||
<li><button class="reset" title="Reset the Spectrum">reset</button></li>
|
||||
<li><button class="audio" title="Audio on/off">audio on/off</button></li>
|
||||
<li><button class="open" title="Open file">open file</button></li>
|
||||
<li><button class="about" title="About JSSpeccy">about</button></li>
|
||||
<li>
|
||||
<div>
|
||||
<select class="select-model"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label><input type="checkbox" class="autoload-tapes" checked="checked"> Autoload tapes</label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="panel open-file">
|
||||
<button class="close">close</button>
|
||||
|
||||
<h2>Load from disk: <span class="note">.tap, .tzx, .sna, .z80 files supported</span></h2><input type="file">
|
||||
|
||||
<h2>Load from web:</h2><input type="url"><button class="open-url">Open URL</button>
|
||||
|
||||
<h2>Search World Of Spectrum:</h2>
|
||||
<form class="search-wos">
|
||||
<input type="search">
|
||||
<input type="submit" value="Search title">
|
||||
</form>
|
||||
<select style="width: 250px" size="8" class="wos-matches"></select>
|
||||
<select style="width: 250px" size="8" class="wos-downloads"></select>
|
||||
<button class="open-from-wos">Open file</button>
|
||||
</div>
|
||||
<div class="panel about">
|
||||
<button class="close">close</button>
|
||||
|
||||
<h1>JSSpeccy</h1>
|
||||
<h2>a ZX Spectrum emulator in Javascript</h2>
|
||||
<p>By <a href="http://matt.west.co.tt/">Matt Westcott</a></p>
|
||||
|
||||
<p>Sound routines by Darren Coles</p>
|
||||
|
||||
<p><a href="http://matt.west.co.tt/category/javascript/jsspeccy/">JSSpeccy homepage</a> (including downloads and source code)</p>
|
||||
<p>Based on <a href="http://fuse-emulator.sourceforge.net/">Fuse</a> by Philip Kendall et al. Icons from <a href="http://www.icon-king.com/projects/nuvola/">Nuvola</a> by David Vignoni.</p>
|
||||
<div class="licence">
|
||||
<p>This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="commentary">
|
||||
<h1>JSSpeccy <span class="version">v2.2.1</span></h1>
|
||||
<h2>A ZX Spectrum emulator in Javascript</h2>
|
||||
<p>This is a (mostly) accurate recreation of the 48K and 128K Spectrums. Features currently unsupported are:</p>
|
||||
<ul>
|
||||
<li>Custom tape loaders</li>
|
||||
<li>Some particularly complex multicolour tricks</li>
|
||||
</ul>
|
||||
<p>Tested on Chrome 32, Firefox 26 and Safari 7.0.1.</p>
|
||||
<p>Not sure what to play? Choose a game from this list to get started...</p>
|
||||
<select id="example-games" style="width: 100%">
|
||||
<option value="">Select one...</option>
|
||||
<option value="ant_attack.z80">Ant Attack (1983, Quicksilva)</option>
|
||||
<option value="batty.z80">Batty (1987, Hit-Pak)</option>
|
||||
<option value="bruce_lee.z80">Bruce Lee (1984, U.S. Gold)</option>
|
||||
<option value="chase_hq.z80">Chase H.Q. (1989, Ocean Software)</option>
|
||||
<option value="cyclone.z80">Cyclone (1985, Vortex Software)</option>
|
||||
<option value="dark_star.z80">Dark Star (1984, Design Design Software)</option>
|
||||
<option value="deathchase.z80">Deathchase (1983, Micromega)</option>
|
||||
<option value="elite.z80">Elite (1985, Firebird Software)</option>
|
||||
<option value="everyones_a_wally.z80">Everyone's A Wally (1985, Mikro-Gen)</option>
|
||||
<option value="exolon.z80">Exolon (1987, Hewson Consultants)</option>
|
||||
<option value="fairlight128.z80">Fairlight (1985, The Edge)</option>
|
||||
<option value="flying_shark.z80">Flying Shark (1987, Firebird Software)</option>
|
||||
<option value="the_great_escape.z80">The Great Escape (1986, Ocean Software)</option>
|
||||
<option value="head_over_heels.z80">Head Over Heels (1987, Ocean Software)</option>
|
||||
<option value="hobbit.z80">The Hobbit (1982, Melbourne House)</option>
|
||||
<option value="jet_set_willy.z80">Jet Set Willy (1984, Software Projects)</option>
|
||||
<option value="lords_of_midnight.z80">The Lords Of Midnight (1984, Beyond Software)</option>
|
||||
<option value="manic_miner.z80">Manic Miner (1983, Bug-Byte Software)</option>
|
||||
<option value="quazatron.z80">Quazatron (1986, Hewson Consultants)</option>
|
||||
<option value="rainbow_islands.z80">Rainbow Islands (1990, Ocean Software)</option>
|
||||
<option value="sir_fred.z80">Sir Fred (1986, Made In Spain)</option>
|
||||
<option value="skool_daze.z80">Skool Daze (1984, Microsphere)</option>
|
||||
<option value="three_weeks_in_paradise.z80">Three Weeks In Paradise (1986, Mikro-Gen)</option>
|
||||
<option value="turbo_esprit.z80">Turbo Esprit (1986, Durell Software)</option>
|
||||
<option value="wotef.z80">Way Of The Exploding Fist (1985, Melbourne House)</option>
|
||||
</select>
|
||||
<small>
|
||||
<p>Grab the <a href="https://github.com/gasman/jsspeccy2">source code on Github</a>. Want to include JSSpeccy on your website? <a href="https://github.com/gasman/jsspeccy2/blob/master/Embedding.txt">Embedding instructions</a></p>
|
||||
<p>Created by <a href="http://matt.west.co.tt/">Matt Westcott</a>. <a href="https://twitter.com/gasmanic">Follow me on Twitter</a></p>
|
||||
<p><a href="http://en.wikipedia.org/wiki/File:ZXSpectrum48k.jpg">Spectrum photo</a> by Bill Bertram</p>
|
||||
</small>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,223 @@
|
|||
JSSpeccy.UI = function(opts) {
|
||||
var self = {};
|
||||
|
||||
var container = opts.container;
|
||||
if (typeof(container) === 'string') {
|
||||
container = document.getElementById(container);
|
||||
}
|
||||
var controller = opts.controller;
|
||||
|
||||
var setInnerText;
|
||||
if (document.getElementsByTagName("body")[0].innerText !== undefined) {
|
||||
setInnerText = function (elem, text) {
|
||||
elem.innerText = text;
|
||||
};
|
||||
} else {
|
||||
setInnerText = function (elem, text) {
|
||||
elem.textContent = text;
|
||||
};
|
||||
}
|
||||
|
||||
$(container).addClass('jsspeccy');
|
||||
|
||||
|
||||
/* Set up toolbar */
|
||||
var toolbar = $('.toolbar', container);
|
||||
|
||||
var stopStartButton = $('button.stop-start', toolbar);
|
||||
stopStartButton.click(function() {
|
||||
if (controller.isRunning) {
|
||||
controller.stop();
|
||||
} else {
|
||||
controller.start();
|
||||
}
|
||||
});
|
||||
function refreshStopStartButton() {
|
||||
if (controller.isRunning) {
|
||||
stopStartButton.removeClass('start').addClass('stop');
|
||||
} else {
|
||||
stopStartButton.removeClass('stop').addClass('start');
|
||||
}
|
||||
}
|
||||
controller.onStart.bind(refreshStopStartButton);
|
||||
controller.onStop.bind(refreshStopStartButton);
|
||||
refreshStopStartButton();
|
||||
|
||||
$('button.reset', toolbar).click(function() {
|
||||
controller.reset();
|
||||
});
|
||||
|
||||
var audioButton = $('button.audio', toolbar);
|
||||
audioButton.click(function() {
|
||||
controller.setAudioState(!controller.getAudioState());
|
||||
});
|
||||
function refreshAudioButton(audioState) {
|
||||
audioButton.toggleClass('enabled', audioState);
|
||||
}
|
||||
controller.onChangeAudioState.bind(refreshAudioButton);
|
||||
refreshAudioButton(controller.getAudioState());
|
||||
|
||||
$('button.open', toolbar).click(function() {
|
||||
showPanel('.open-file');
|
||||
});
|
||||
|
||||
$('button.about', toolbar).click(function() {
|
||||
showPanel('.about');
|
||||
});
|
||||
|
||||
var selectModel = $('select.select-model', toolbar);
|
||||
var modelsById = {};
|
||||
for (var i = 0; i < JSSpeccy.Spectrum.MODELS.length; i++) {
|
||||
var model = JSSpeccy.Spectrum.MODELS[i];
|
||||
modelsById[model.id] = model;
|
||||
selectModel.append(
|
||||
$('<option></option>').text(model.name).attr({'value': model.id})
|
||||
);
|
||||
}
|
||||
selectModel.change(function() {
|
||||
var modelId = $(this).val();
|
||||
controller.setModel(modelsById[modelId]);
|
||||
});
|
||||
function refreshModel() {
|
||||
selectModel.val(controller.getModel().id);
|
||||
}
|
||||
refreshModel();
|
||||
controller.onChangeModel.bind(refreshModel);
|
||||
|
||||
var autoloadTapes = $('input.autoload-tapes');
|
||||
|
||||
/* Set up panels */
|
||||
var panels = [];
|
||||
|
||||
function showPanel(selector) {
|
||||
$('.panel', container).not(selector).hide();
|
||||
$('.panel', container).filter(selector).show();
|
||||
controller.deactivateKeyboard();
|
||||
}
|
||||
|
||||
function hidePanels() {
|
||||
$('.panel', container).hide();
|
||||
controller.activateKeyboard();
|
||||
}
|
||||
|
||||
$('.panel button.close', container).click(function() {
|
||||
hidePanels();
|
||||
});
|
||||
|
||||
var openFilePanel = $('.panel.open-file', container);
|
||||
|
||||
var fileSelect = openFilePanel.find('input[type="file"]');
|
||||
fileSelect.change(function() {
|
||||
controller.loadLocalFile(this.files[0], {'autoload': autoloadTapes.is(':checked')});
|
||||
fileSelect.val('');
|
||||
hidePanels();
|
||||
});
|
||||
|
||||
var urlField = openFilePanel.find('input[type="url"]');
|
||||
openFilePanel.find('button.open-url').click(function() {
|
||||
var url = urlField.val();
|
||||
if (url !== '') {
|
||||
controller.loadFromUrl(url, {'autoload': autoloadTapes.is(':checked')});
|
||||
hidePanels();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* World Of Spectrum search interface */
|
||||
|
||||
var wosSearch = openFilePanel.find('form.search-wos');
|
||||
var wosSearchField = wosSearch.find('input[type="search"]');
|
||||
var wosSearchBtn = wosSearch.find('input[type="submit"]');
|
||||
|
||||
var wosMatches = openFilePanel.find('select.wos-matches');
|
||||
var wosDownloads = openFilePanel.find('select.wos-downloads');
|
||||
var wosOpen = openFilePanel.find('button.open-from-wos');
|
||||
|
||||
wosOpen.attr('disabled', 'disabled');
|
||||
|
||||
wosSearch.submit(function() {
|
||||
var query = wosSearchField.val();
|
||||
if (query !== '') {
|
||||
$.getJSON('http://www.worldofspectrum.org/api/infoseek_search_json.cgi?callback=?',
|
||||
{title: query},
|
||||
function(results) {
|
||||
wosMatches.empty();
|
||||
wosDownloads.empty();
|
||||
wosOpen.attr('disabled', 'disabled');
|
||||
if (results.matches) {
|
||||
for (var i = 0; i < results.matches.length; i++) {
|
||||
var result = results.matches[i];
|
||||
var optionText = result.title;
|
||||
if (result.publisher) {
|
||||
optionText += " (" + result.publisher + ")";
|
||||
}
|
||||
var option = $('<option></option>').text(optionText).attr('value', result.id);
|
||||
wosMatches.append(option);
|
||||
}
|
||||
wosMatches.removeAttr('disabled');
|
||||
} else {
|
||||
wosMatches.append('<option>(no matches found)</option>');
|
||||
wosMatches.attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
wosMatches.change(function() {
|
||||
wosDownloads.empty();
|
||||
wosOpen.attr('disabled', 'disabled');
|
||||
var id = $(this).val();
|
||||
if (id) {
|
||||
$.getJSON('http://www.worldofspectrum.org/api/infoseek_select_json.cgi?callback=?',
|
||||
{id: id},
|
||||
function(response) {
|
||||
wosDownloads.empty();
|
||||
if (response.downloads) {
|
||||
for (var i = 0; i < response.downloads.length; i++) {
|
||||
var download = response.downloads[i];
|
||||
var optionText;
|
||||
if (download.origin !== '') {
|
||||
optionText = download.origin + " - " + download.type;
|
||||
} else {
|
||||
optionText = download.type;
|
||||
}
|
||||
var option = $('<option></option>').text(optionText).attr('value', download.link);
|
||||
wosDownloads.append(option);
|
||||
}
|
||||
wosDownloads.removeAttr('disabled');
|
||||
} else {
|
||||
wosDownloads.append('<option>(no downloads available)</option>');
|
||||
wosDownloads.attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
wosDownloads.change(function() {
|
||||
var url = $(this).val();
|
||||
if (url) {
|
||||
wosOpen.removeAttr('disabled');
|
||||
} else {
|
||||
wosOpen.attr('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
|
||||
function loadSelectedFile() {
|
||||
var url = wosDownloads.val();
|
||||
if (url) {
|
||||
controller.loadFromUrl(
|
||||
url.replace('ftp://ftp.worldofspectrum.org/pub/sinclair/', 'http://wosproxy.zxdemo.org/unzip/'),
|
||||
{'autoload': autoloadTapes.is(':checked')}
|
||||
);
|
||||
hidePanels();
|
||||
}
|
||||
}
|
||||
|
||||
wosOpen.click(loadSelectedFile);
|
||||
wosDownloads.dblclick(loadSelectedFile);
|
||||
|
||||
return self;
|
||||
};
|