This commit is contained in:
Pedro de Oliveira 2014-08-12 22:52:43 +01:00
parent a4c47b200f
commit 1ac9c57735
16 changed files with 527 additions and 17 deletions

23
_index.html Normal file
View File

@ -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>

107
css/jsspeccy.css Normal file
View File

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
images/48x48_sound_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
images/48x48_sound_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
images/spectrum48k.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,23 +1,180 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html> <html>
<head> <head>
<title>My wonderful page</title> <title>jsspeccy</title>
<script src="jdataview.js"></script> <link rel="stylesheet" href="css/jsspeccy.css">
<script src="jsspeccy-core.min.js"></script> <script src="js/jdataview.js"></script>
<script src="jquery-1.7.2.min.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>
<script> <style>
$(function() { body {
var jsspeccy = JSSpeccy('speccy', { margin: 0;
'autostart': false, 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('jsspeccy-viewport', {
'autostart': true,
'autoload': true, 'autoload': true,
'scaleFactor': 1,
'loadFile': 'scroller1.tap' 'loadFile': 'scroller1.tap'
}); });
});
</script> JSSpeccy.UI({
</head> container: 'jsspeccy',
<body> controller: jsspeccy
<div id="speccy"></div> });
</body> $('#example-games').change(function() {
</html> var filename = $(this).val();
if (filename) {
jsspeccy.loadFromUrl(
'http://jsspeccy.zxdemo.org/games/' + filename,
{'autoload': true}
);
}
})
});
</script>
</head>
<body>
<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 &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.</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>

223
js/ui.js Normal file
View File

@ -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;
};