diff --git a/_index.html b/_index.html new file mode 100644 index 0000000..ebff57f --- /dev/null +++ b/_index.html @@ -0,0 +1,23 @@ + + + + My wonderful page + + + + + + + +
+ + \ No newline at end of file diff --git a/css/jsspeccy.css b/css/jsspeccy.css new file mode 100644 index 0000000..884b2e0 --- /dev/null +++ b/css/jsspeccy.css @@ -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; +} diff --git a/images/48x48_folder_blue_open.png b/images/48x48_folder_blue_open.png new file mode 100644 index 0000000..f7314b3 Binary files /dev/null and b/images/48x48_folder_blue_open.png differ diff --git a/images/48x48_kaboodleloop.png b/images/48x48_kaboodleloop.png new file mode 100644 index 0000000..389efac Binary files /dev/null and b/images/48x48_kaboodleloop.png differ diff --git a/images/48x48_messagebox_info.png b/images/48x48_messagebox_info.png new file mode 100644 index 0000000..079f8fc Binary files /dev/null and b/images/48x48_messagebox_info.png differ diff --git a/images/48x48_package_games_arcade.png b/images/48x48_package_games_arcade.png new file mode 100644 index 0000000..78e3b20 Binary files /dev/null and b/images/48x48_package_games_arcade.png differ diff --git a/images/48x48_player_pause.png b/images/48x48_player_pause.png new file mode 100644 index 0000000..cfd880e Binary files /dev/null and b/images/48x48_player_pause.png differ diff --git a/images/48x48_player_play.png b/images/48x48_player_play.png new file mode 100644 index 0000000..b2da347 Binary files /dev/null and b/images/48x48_player_play.png differ diff --git a/images/48x48_sound_off.png b/images/48x48_sound_off.png new file mode 100644 index 0000000..e330f01 Binary files /dev/null and b/images/48x48_sound_off.png differ diff --git a/images/48x48_sound_on.png b/images/48x48_sound_on.png new file mode 100644 index 0000000..a5cb40c Binary files /dev/null and b/images/48x48_sound_on.png differ diff --git a/images/spectrum48k.png b/images/spectrum48k.png new file mode 100644 index 0000000..d062ebb Binary files /dev/null and b/images/spectrum48k.png differ diff --git a/index.html b/index.html index 6c35c5c..431a5cb 100644 --- a/index.html +++ b/index.html @@ -1,23 +1,180 @@ - - My wonderful page - - - + + jsspeccy + + + + + + - - - -
- - \ No newline at end of file + + 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} + ); + } + }) + }); + + + +
+
+ + +
+ + +

Load from disk: .tap, .tzx, .sna, .z80 files supported

+ +

Load from web:

+ +

Search World Of Spectrum:

+
+ + +
+ + + +
+
+ + +

JSSpeccy

+

a ZX Spectrum emulator in Javascript

+

By Matt Westcott

+ +

Sound routines by Darren Coles

+ +

JSSpeccy homepage (including downloads and source code)

+

Based on Fuse by Philip Kendall et al. Icons from Nuvola by David Vignoni.

+
+

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.

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.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

+
+
+
+
+

JSSpeccy v2.2.1

+

A ZX Spectrum emulator in Javascript

+

This is a (mostly) accurate recreation of the 48K and 128K Spectrums. Features currently unsupported are:

+ +

Tested on Chrome 32, Firefox 26 and Safari 7.0.1.

+

Not sure what to play? Choose a game from this list to get started...

+ + +

Grab the source code on Github. Want to include JSSpeccy on your website? Embedding instructions

+

Created by Matt Westcott. Follow me on Twitter

+

Spectrum photo by Bill Bertram

+
+
+ + diff --git a/jdataview.js b/js/jdataview.js similarity index 100% rename from jdataview.js rename to js/jdataview.js diff --git a/jquery-1.7.2.min.js b/js/jquery-1.7.2.min.js similarity index 100% rename from jquery-1.7.2.min.js rename to js/jquery-1.7.2.min.js diff --git a/jsspeccy-core.min.js b/js/jsspeccy-core.min.js similarity index 100% rename from jsspeccy-core.min.js rename to js/jsspeccy-core.min.js diff --git a/js/ui.js b/js/ui.js new file mode 100644 index 0000000..11dda0e --- /dev/null +++ b/js/ui.js @@ -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( + $('').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 = $('').text(optionText).attr('value', result.id); + wosMatches.append(option); + } + wosMatches.removeAttr('disabled'); + } else { + wosMatches.append(''); + 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 = $('').text(optionText).attr('value', download.link); + wosDownloads.append(option); + } + wosDownloads.removeAttr('disabled'); + } else { + wosDownloads.append(''); + 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; +};