diff --git a/.gitignore b/.gitignore index 26ad7b2..b99919b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store -.sass-cache \ No newline at end of file +.sass-cache +node_modules \ No newline at end of file diff --git a/8CB1fFeb1n8.flv b/8CB1fFeb1n8.flv new file mode 100644 index 0000000..478ee7b Binary files /dev/null and b/8CB1fFeb1n8.flv differ diff --git a/index.html b/index.html deleted file mode 100644 index dafc1a6..0000000 --- a/index.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - -
- -
1 PLAYER GAME
- -
-
-
VIRUS LEVEL
- -
1P
- -
-
-
- -
0
-
- - -
-
SPEED
- -
1P
- -
-
-

LOW

-

MED

-

HI

-
-
-
- - -
-
MUSIC TYPE
-
 
- -
-
-

FEVER

-

CHILL

-

OFF

-
-
-
- -
- Play -
-
- - \ No newline at end of file diff --git a/javascripts/score.js b/javascripts/score.js deleted file mode 100644 index d7a8962..0000000 --- a/javascripts/score.js +++ /dev/null @@ -1,5 +0,0 @@ -Parse.initialize("8kJxzEB8G8Rx3mYk8kM8snrHryd4dtqTMgd7MKpD", "WRSVmPfF7bf8SFl3duTXMx48pTwLWsAZyda3gzFW"); - -app.models.Score = Parse.Object.extend({ - className: "GameScore" -}); diff --git a/javascripts/setup.js b/javascripts/setup.js deleted file mode 100644 index c0ca61d..0000000 --- a/javascripts/setup.js +++ /dev/null @@ -1,72 +0,0 @@ -var speeds = ["LOW", "MED", "HI"]; -var musics = ["FEVER", "CHILD", "OFF"]; - -function Setup() { - this.level = $( "#level_slider" ).slider( "value" ); - this.speed = speeds[ $( "#speed_slider" ).slider( "value" ) ]; - this.music = musics[ $( "#music_slider" ).slider( "value" ) ]; -}; - -function start(){ - var settings = new Setup(); - - window.location.href = "./src/play_game.html?" + jQuery.param(settings); -}; - -$(function(){ - $( "#level_slider" ).slider({ - value:0, - min: 0, - max: 20, - step: 1, - slide: function( event, ui ) { - $( "#level_amount" ).html( ui.value ); - $( "#level_title").addClass('border'); - $( "#music_title").removeClass('border'); - $( "#speed_title").removeClass('border'); - } - }); - - $( "#speed_slider" ).slider({ - min: 0, - max: speeds.length - 1, - step: 1, - slide: function( event, ui ) { - $( "#speed_title").addClass('border'); - $( "#level_title").removeClass('border'); - $( "#music_title").removeClass('border'); - } - }); - - $( "#music_slider" ).slider({ - min: 0, - max: musics.length - 1, - step: 1, - slide: function( event, ui ) { - $( "#music_title").addClass('border'); - $( "#level_title").removeClass('border'); - $( "#speed_title").removeClass('border'); - } - }); - - $("#play").bind('click', function(){ - start(); - return false; - }); -}); - -window.addEventListener('keydown', function(e) { - var code = (e.keyCode ? e.keyCode : e.which); - - switch (code) { - case 13: - start(); - break; - case 38: - console.log('up'); - break; - case 40: - console.log('down'); - break; - } -}); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..925f3c8 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "dr_mojo", + "domains": [ + "dr.mojotech.com" + ], + "subdomain": "dr_mojo", + "scripts": { + "start": "node server.js" + }, + "version": "0.0.0-4", + "engines": { + "node": "0.8.x" + }, + "dependencies": { + "connect": "latest", + "express": "2.5.8", + "jade": "latest", + "node-parse-api": "latest" + } +} \ No newline at end of file diff --git a/public/flash/soundmanager2.swf b/public/flash/soundmanager2.swf new file mode 100755 index 0000000..481eb87 Binary files /dev/null and b/public/flash/soundmanager2.swf differ diff --git a/public/flash/soundmanager2_debug.swf b/public/flash/soundmanager2_debug.swf new file mode 100755 index 0000000..d638a5a Binary files /dev/null and b/public/flash/soundmanager2_debug.swf differ diff --git a/public/flash/soundmanager2_flash9.swf b/public/flash/soundmanager2_flash9.swf new file mode 100755 index 0000000..9bd0544 Binary files /dev/null and b/public/flash/soundmanager2_flash9.swf differ diff --git a/public/flash/soundmanager2_flash9_debug.swf b/public/flash/soundmanager2_flash9_debug.swf new file mode 100755 index 0000000..b2765f1 Binary files /dev/null and b/public/flash/soundmanager2_flash9_debug.swf differ diff --git a/public/flash/soundmanager2_flash_xdomain.zip b/public/flash/soundmanager2_flash_xdomain.zip new file mode 100755 index 0000000..3addaba Binary files /dev/null and b/public/flash/soundmanager2_flash_xdomain.zip differ diff --git a/images/animated_viruses.gif b/public/images/animated_viruses.gif similarity index 100% rename from images/animated_viruses.gif rename to public/images/animated_viruses.gif diff --git a/images/bg.png b/public/images/bg.png similarity index 100% rename from images/bg.png rename to public/images/bg.png diff --git a/images/blue_bottom.png b/public/images/blue_bottom.png similarity index 100% rename from images/blue_bottom.png rename to public/images/blue_bottom.png diff --git a/images/blue_left.png b/public/images/blue_left.png similarity index 100% rename from images/blue_left.png rename to public/images/blue_left.png diff --git a/images/blue_right.png b/public/images/blue_right.png similarity index 100% rename from images/blue_right.png rename to public/images/blue_right.png diff --git a/images/blue_top.png b/public/images/blue_top.png similarity index 100% rename from images/blue_top.png rename to public/images/blue_top.png diff --git a/images/clipboard_top.png b/public/images/clipboard_top.png similarity index 100% rename from images/clipboard_top.png rename to public/images/clipboard_top.png diff --git a/images/game_border.png b/public/images/game_border.png similarity index 100% rename from images/game_border.png rename to public/images/game_border.png diff --git a/public/images/game_border2.png b/public/images/game_border2.png new file mode 100644 index 0000000..6574875 Binary files /dev/null and b/public/images/game_border2.png differ diff --git a/images/game_top.png b/public/images/game_top.png similarity index 100% rename from images/game_top.png rename to public/images/game_top.png diff --git a/images/green_bottom.png b/public/images/green_bottom.png similarity index 100% rename from images/green_bottom.png rename to public/images/green_bottom.png diff --git a/images/green_left.png b/public/images/green_left.png similarity index 100% rename from images/green_left.png rename to public/images/green_left.png diff --git a/images/green_right.png b/public/images/green_right.png similarity index 100% rename from images/green_right.png rename to public/images/green_right.png diff --git a/images/green_top.png b/public/images/green_top.png similarity index 100% rename from images/green_top.png rename to public/images/green_top.png diff --git a/images/magnifying_glass.png b/public/images/magnifying_glass.png similarity index 100% rename from images/magnifying_glass.png rename to public/images/magnifying_glass.png diff --git a/images/mojo_logo.png b/public/images/mojo_logo.png similarity index 100% rename from images/mojo_logo.png rename to public/images/mojo_logo.png diff --git a/images/red_bottom.png b/public/images/red_bottom.png similarity index 100% rename from images/red_bottom.png rename to public/images/red_bottom.png diff --git a/images/red_left.png b/public/images/red_left.png similarity index 100% rename from images/red_left.png rename to public/images/red_left.png diff --git a/images/red_right.png b/public/images/red_right.png similarity index 100% rename from images/red_right.png rename to public/images/red_right.png diff --git a/images/red_top.png b/public/images/red_top.png similarity index 100% rename from images/red_top.png rename to public/images/red_top.png diff --git a/images/virus_blue.png b/public/images/virus_blue.png similarity index 100% rename from images/virus_blue.png rename to public/images/virus_blue.png diff --git a/images/virus_green.png b/public/images/virus_green.png similarity index 100% rename from images/virus_green.png rename to public/images/virus_green.png diff --git a/images/virus_red.png b/public/images/virus_red.png similarity index 100% rename from images/virus_red.png rename to public/images/virus_red.png diff --git a/javascripts/PieceDrawer.js b/public/javascripts/PieceDrawer.js similarity index 100% rename from javascripts/PieceDrawer.js rename to public/javascripts/PieceDrawer.js diff --git a/javascripts/Utils.js b/public/javascripts/Utils.js similarity index 100% rename from javascripts/Utils.js rename to public/javascripts/Utils.js diff --git a/javascripts/board.js b/public/javascripts/board.js similarity index 100% rename from javascripts/board.js rename to public/javascripts/board.js diff --git a/javascripts/collisionDetector.js b/public/javascripts/collisionDetector.js similarity index 100% rename from javascripts/collisionDetector.js rename to public/javascripts/collisionDetector.js diff --git a/javascripts/game.js b/public/javascripts/game.js similarity index 74% rename from javascripts/game.js rename to public/javascripts/game.js index 2386247..36da9af 100644 --- a/javascripts/game.js +++ b/public/javascripts/game.js @@ -1,4 +1,10 @@ -function Game(lvl, speed) { +function Game(lvl, speed, music) { + this.initial = { + level: lvl, + speed: speed, + music: music + }; + this.board = new Board(board_size[0], board_size[1]); this.detector = new CollisionDetector(this.board); this.paused = false; @@ -10,10 +16,15 @@ function Game(lvl, speed) { this.next_pill = new Pill(this.board, this.detector); this.level = new Level(lvl, speed); this.score = 0; + this.highScore = 0; + this.leaders = []; + this.playerName = ""; this.setScore(); this.setInfo(); this.setListeners(); this.populateViruses(this.level.number); + + this.restart = _.bind(this.restart, this); } Game.prototype.newPill = function() { @@ -39,6 +50,12 @@ Game.prototype.newVirus = function(level, num) { Game.prototype.gameOver = function() { this.done = true; this.clock = window.clearInterval(this.clock); + var restart = this.restart; + window.addEventListener('keydown', function (e) { + if (e.keyCode == 13) { + restart(); + } + }) } Game.prototype.setListeners = function() { @@ -54,7 +71,12 @@ Game.prototype.setListeners = function() { //s _this.pillAction('rotate-right'); break; + case 77: + _this.toggleMusic(); + break; case 32: + $("#gameOverModal").trigger('reveal:close'); + $("#nextLevelModal").trigger('reveal:close'); _this.togglePause(); break; case 37: @@ -75,6 +97,10 @@ Game.prototype.setListeners = function() { } } }); + $(".submit-button").on("click", function(e) { + _this.playerName = $("#player-name").val(); + _this.submitHighScore(); + }); } Game.prototype.pillAction = function(action) { @@ -82,15 +108,17 @@ Game.prototype.pillAction = function(action) { switch (action) { case 'rotate-left': this.active_pill.rotateLeft(); + window.Sound.rotate(); break; case 'rotate-right': this.active_pill.rotateRight(); + window.Sound.rotate(); break; case 'left': - this.active_pill.moveLeft(); + this.active_pill.moveLeft() && window.Sound.move(); break; case 'right': - this.active_pill.moveRight(); + this.active_pill.moveRight() && window.Sound.move(); break; case 'down': this.active_pill.moveDown(); @@ -104,16 +132,20 @@ Game.prototype.togglePause = function() { Utils.shading(this.paused, 'Pause'); } -Game.prototype.toggleInstructions = function(){ +Game.prototype.toggleInstructions = function() { this.paused = !this.paused; Utils.shading(this.paused, $('#instructions').html()); } -Game.prototype.toggleHelp = function(){ +Game.prototype.toggleHelp = function() { this.paused = !this.paused; Utils.shading(this.paused, $('.controls').html()); } +Game.prototype.toggleMusic = function() { + window.Sound.toggleOnOff(); +} + Game.prototype.tick = function() { this.pillAction('down'); if (this.checkHit() || this.active_pill.isEmpty()) { @@ -126,8 +158,13 @@ Game.prototype.tick = function() { } //Change this to where the pills are created if (this.board.occupied(Math.floor(this.board.width / 2) - 1, 0)) { - alert("game over"); - this.saveScore(); + if(this.isHighScore()) { + this.noInteractions = true; + $('#gameOverHighScoreModal').reveal(); + }else{ + this.noInteractions = true; + $('#gameOverModal').reveal(); + } this.gameOver(); } else if (this.virusCount == 0) { this.nextLevel(); @@ -144,7 +181,9 @@ Game.prototype.nextLevel = function() { the_game.level.number++; this.setLevel(); the_game.populateViruses(the_game.level.number); - alert("Level " + (parseInt(level, 10) + 1)); + $(".nextLevelNumber").html(the_game.level.number); + $("#nextLevelModal").reveal(); + this.paused = true; the_game.start(this.level.velocity()); } @@ -155,7 +194,7 @@ Game.prototype.findMatches = function(cb) { if (matches.length) { var virus_count = 0; - _.each(matches, function(match_set){ + _.each(matches, function(match_set) { virus_count = _.filter(match_set, function(coord) { var cell = _this.board.occupied(coord.x, coord.y, 1); return (cell.pill.type === 'Virus'); @@ -167,14 +206,16 @@ Game.prototype.findMatches = function(cb) { _.each(matches, function(match_set) { _.each(match_set, function(spot) { var deleting = this.occupied(spot.x, spot.y, 1); - if (deleting != undefined){ + if (deleting != undefined) { if (deleting.connected) { this.occupied(deleting.connected.x, deleting.connected.y, 1).connected = undefined; } var spot = deleting.pos; if (deleting.pill.position[spot]) { + Sound.explode(); this.eraseSpot(deleting.pill.position[spot].x, deleting.pill.position[spot].y, 1); } else { + Sound.explode(); this.eraseSpot(deleting.pill.position.x, deleting.pill.position.y, 1); _this.updateVirusCount(-1); } @@ -239,19 +280,44 @@ Game.prototype.saveScore = function() { } Game.prototype.setScore = function() { - var query = new Parse.Query(app.models.Score); + var game = this; $("#score .score").html(this.score); - query.descending("score").limit(1); - query.first({ - success: function(object) { - $("#highScore .score").html(object.attributes.score); + $.get('/highscore', {}, function(response) { + game.leaders = _.sortBy(response.data, function(leader) { + return parseInt(leader.score); + }).reverse(); + game.highScore = game.leaders[0].score || 0; + $("#highScore .score").html(game.highScore); + }); +} + +Game.prototype.isHighScore = function() { + if(this.leaders && this.leaders[4] && this.leaders[4].score) { + return this.leaders[4].score < this.score; + } else { + return true; + } +} + +Game.prototype.submitHighScore = function() { + var game = this; + var csrf = $("#hidden-csrf").attr('value'); + + $.ajax({ + type: 'POST', + url: '/highscore', + data: { + 'score': game.score, + 'level': game.level.number, + 'name': game.playerName, + '_csrf': csrf }, - error: function(error) { - $("#highScore .score").html('0000000'); - } + success: game.restart, + error: game.restart }); + } Game.prototype.setInfo = function() { @@ -260,6 +326,13 @@ Game.prototype.setInfo = function() { this.setVirusCount(); } +Game.prototype.restart = function() { + url = "play?level=" + this.initial.level + + "&speed=" + this.initial.speed + + "&music=" + this.initial.music; + window.location.href = url; +} + // ---------------------------------------------------------------------------- // -------------------------- Dr. Mojo Scoring -------------------------------- // ---------------------------------------------------------------------------- @@ -281,7 +354,7 @@ Game.prototype.setInfo = function() { Game.prototype.scoring = function(virus_count) { var sum = 0; - for(var i=0; i <= virus_count-1; ++i){ + for(var i=0; i <= virus_count-1; ++i) { sum += this.level.virus_score() * Math.pow(2, i); } diff --git a/javascripts/level.js b/public/javascripts/level.js similarity index 100% rename from javascripts/level.js rename to public/javascripts/level.js diff --git a/javascripts/lib/backbone-min.js b/public/javascripts/lib/backbone-min.js similarity index 100% rename from javascripts/lib/backbone-min.js rename to public/javascripts/lib/backbone-min.js diff --git a/public/javascripts/lib/jasmid/audio.js b/public/javascripts/lib/jasmid/audio.js new file mode 100644 index 0000000..cf67a35 --- /dev/null +++ b/public/javascripts/lib/jasmid/audio.js @@ -0,0 +1,125 @@ +var sampleRate = 44100; /* hard-coded in Flash player */ + +function AudioPlayer(generator, opts) { + if (!opts) opts = {}; + var latency = opts.latency || 1; + var checkInterval = latency * 100 /* in ms */ + + var audioElement = new Audio(); + var webkitAudio = window.AudioContext || window.webkitAudioContext; + var requestStop = false; + + if (audioElement.mozSetup) { + audioElement.mozSetup(2, sampleRate); /* channels, sample rate */ + + var buffer = []; /* data generated but not yet written */ + var minBufferLength = latency * 2 * sampleRate; /* refill buffer when there are only this many elements remaining */ + var bufferFillLength = Math.floor(latency * sampleRate); + + function checkBuffer() { + if (buffer.length) { + var written = audioElement.mozWriteAudio(buffer); + buffer = buffer.slice(written); + } + if (buffer.length < minBufferLength && !generator.finished) { + buffer = buffer.concat(generator.generate(bufferFillLength)); + } + if (!requestStop && (!generator.finished || buffer.length)) { + setTimeout(checkBuffer, checkInterval); + } + } + checkBuffer(); + + return { + 'type': 'Firefox Audio', + 'stop': function() { + requestStop = true; + } + } + } else if (webkitAudio) { + // Uses Webkit Web Audio API if available + var context = new webkitAudio(); + sampleRate = context.sampleRate; + + var channelCount = 2; + var bufferSize = 4096*4; // Higher for less gitches, lower for less latency + + var node = context.createJavaScriptNode(bufferSize, 0, channelCount); + + node.onaudioprocess = function(e) { process(e) }; + + function process(e) { + if (generator.finished) { + node.disconnect(); + return; + } + + var dataLeft = e.outputBuffer.getChannelData(0); + var dataRight = e.outputBuffer.getChannelData(1); + + var generate = generator.generate(bufferSize); + + for (var i = 0; i < bufferSize; ++i) { + dataLeft[i] = generate[i*2]; + dataRight[i] = generate[i*2+1]; + } + } + + // start + node.connect(context.destination); + + return { + 'stop': function() { + // pause + node.disconnect(); + requestStop = true; + }, + 'type': 'Webkit Audio' + } + + } else { + // Fall back to creating flash player + var c = document.createElement('div'); + c.innerHTML = ''; + document.body.appendChild(c); + var swf = document.getElementById('da-swf'); + + var minBufferDuration = latency * 1000; /* refill buffer when there are only this many ms remaining */ + var bufferFillLength = latency * sampleRate; + + function write(data) { + var out = new Array(data.length); + for (var i = data.length-1; i != 0; i--) { + out[i] = Math.floor(data[i]*32768); + } + return swf.write(out.join(' ')); + } + + function checkBuffer() { + if (swf.bufferedDuration() < minBufferDuration) { + write(generator.generate(bufferFillLength)); + }; + if (!requestStop && !generator.finished) setTimeout(checkBuffer, checkInterval); + } + + function checkReady() { + if (swf.write) { + checkBuffer(); + } else { + setTimeout(checkReady, 10); + } + } + checkReady(); + + return { + 'stop': function() { + swf.stop(); + requestStop = true; + }, + 'bufferedDuration': function() { + return swf.bufferedDuration(); + }, + 'type': 'Flash Audio' + } + } +} diff --git a/public/javascripts/lib/jasmid/midifile.js b/public/javascripts/lib/jasmid/midifile.js new file mode 100644 index 0000000..608524e --- /dev/null +++ b/public/javascripts/lib/jasmid/midifile.js @@ -0,0 +1,238 @@ +/* +class to parse the .mid file format +(depends on stream.js) +*/ +function MidiFile(data) { + function readChunk(stream) { + var id = stream.read(4); + var length = stream.readInt32(); + return { + 'id': id, + 'length': length, + 'data': stream.read(length) + }; + } + + var lastEventTypeByte; + + function readEvent(stream) { + var event = {}; + event.deltaTime = stream.readVarInt(); + var eventTypeByte = stream.readInt8(); + if ((eventTypeByte & 0xf0) == 0xf0) { + /* system / meta event */ + if (eventTypeByte == 0xff) { + /* meta event */ + event.type = 'meta'; + var subtypeByte = stream.readInt8(); + var length = stream.readVarInt(); + switch(subtypeByte) { + case 0x00: + event.subtype = 'sequenceNumber'; + if (length != 2) throw "Expected length for sequenceNumber event is 2, got " + length; + event.number = stream.readInt16(); + return event; + case 0x01: + event.subtype = 'text'; + event.text = stream.read(length); + return event; + case 0x02: + event.subtype = 'copyrightNotice'; + event.text = stream.read(length); + return event; + case 0x03: + event.subtype = 'trackName'; + event.text = stream.read(length); + return event; + case 0x04: + event.subtype = 'instrumentName'; + event.text = stream.read(length); + return event; + case 0x05: + event.subtype = 'lyrics'; + event.text = stream.read(length); + return event; + case 0x06: + event.subtype = 'marker'; + event.text = stream.read(length); + return event; + case 0x07: + event.subtype = 'cuePoint'; + event.text = stream.read(length); + return event; + case 0x20: + event.subtype = 'midiChannelPrefix'; + if (length != 1) throw "Expected length for midiChannelPrefix event is 1, got " + length; + event.channel = stream.readInt8(); + return event; + case 0x2f: + event.subtype = 'endOfTrack'; + if (length != 0) throw "Expected length for endOfTrack event is 0, got " + length; + return event; + case 0x51: + event.subtype = 'setTempo'; + if (length != 3) throw "Expected length for setTempo event is 3, got " + length; + event.microsecondsPerBeat = ( + (stream.readInt8() << 16) + + (stream.readInt8() << 8) + + stream.readInt8() + ) + return event; + case 0x54: + event.subtype = 'smpteOffset'; + if (length != 5) throw "Expected length for smpteOffset event is 5, got " + length; + var hourByte = stream.readInt8(); + event.frameRate = { + 0x00: 24, 0x20: 25, 0x40: 29, 0x60: 30 + }[hourByte & 0x60]; + event.hour = hourByte & 0x1f; + event.min = stream.readInt8(); + event.sec = stream.readInt8(); + event.frame = stream.readInt8(); + event.subframe = stream.readInt8(); + return event; + case 0x58: + event.subtype = 'timeSignature'; + if (length != 4) throw "Expected length for timeSignature event is 4, got " + length; + event.numerator = stream.readInt8(); + event.denominator = Math.pow(2, stream.readInt8()); + event.metronome = stream.readInt8(); + event.thirtyseconds = stream.readInt8(); + return event; + case 0x59: + event.subtype = 'keySignature'; + if (length != 2) throw "Expected length for keySignature event is 2, got " + length; + event.key = stream.readInt8(); + event.scale = stream.readInt8(); + return event; + case 0x7f: + event.subtype = 'sequencerSpecific'; + event.data = stream.read(length); + return event; + default: + // console.log("Unrecognised meta event subtype: " + subtypeByte); + event.subtype = 'unknown' + event.data = stream.read(length); + return event; + } + event.data = stream.read(length); + return event; + } else if (eventTypeByte == 0xf0) { + event.type = 'sysEx'; + var length = stream.readVarInt(); + event.data = stream.read(length); + return event; + } else if (eventTypeByte == 0xf7) { + event.type = 'dividedSysEx'; + var length = stream.readVarInt(); + event.data = stream.read(length); + return event; + } else { + throw "Unrecognised MIDI event type byte: " + eventTypeByte; + } + } else { + /* channel event */ + var param1; + if ((eventTypeByte & 0x80) == 0) { + /* running status - reuse lastEventTypeByte as the event type. + eventTypeByte is actually the first parameter + */ + param1 = eventTypeByte; + eventTypeByte = lastEventTypeByte; + } else { + param1 = stream.readInt8(); + lastEventTypeByte = eventTypeByte; + } + var eventType = eventTypeByte >> 4; + event.channel = eventTypeByte & 0x0f; + event.type = 'channel'; + switch (eventType) { + case 0x08: + event.subtype = 'noteOff'; + event.noteNumber = param1; + event.velocity = stream.readInt8(); + return event; + case 0x09: + event.noteNumber = param1; + event.velocity = stream.readInt8(); + if (event.velocity == 0) { + event.subtype = 'noteOff'; + } else { + event.subtype = 'noteOn'; + } + return event; + case 0x0a: + event.subtype = 'noteAftertouch'; + event.noteNumber = param1; + event.amount = stream.readInt8(); + return event; + case 0x0b: + event.subtype = 'controller'; + event.controllerType = param1; + event.value = stream.readInt8(); + return event; + case 0x0c: + event.subtype = 'programChange'; + event.programNumber = param1; + return event; + case 0x0d: + event.subtype = 'channelAftertouch'; + event.amount = param1; + return event; + case 0x0e: + event.subtype = 'pitchBend'; + event.value = param1 + (stream.readInt8() << 7); + return event; + default: + throw "Unrecognised MIDI event type: " + eventType + /* + console.log("Unrecognised MIDI event type: " + eventType); + stream.readInt8(); + event.subtype = 'unknown'; + return event; + */ + } + } + } + + stream = Stream(data); + var headerChunk = readChunk(stream); + if (headerChunk.id != 'MThd' || headerChunk.length != 6) { + throw "Bad .mid file - header not found"; + } + var headerStream = Stream(headerChunk.data); + var formatType = headerStream.readInt16(); + var trackCount = headerStream.readInt16(); + var timeDivision = headerStream.readInt16(); + + if (timeDivision & 0x8000) { + throw "Expressing time division in SMTPE frames is not supported yet" + } else { + ticksPerBeat = timeDivision; + } + + var header = { + 'formatType': formatType, + 'trackCount': trackCount, + 'ticksPerBeat': ticksPerBeat + } + var tracks = []; + for (var i = 0; i < header.trackCount; i++) { + tracks[i] = []; + var trackChunk = readChunk(stream); + if (trackChunk.id != 'MTrk') { + throw "Unexpected chunk - expected MTrk, got "+ trackChunk.id; + } + var trackStream = Stream(trackChunk.data); + while (!trackStream.eof()) { + var event = readEvent(trackStream); + tracks[i].push(event); + //console.log(event); + } + } + + return { + 'header': header, + 'tracks': tracks + } +} diff --git a/public/javascripts/lib/jasmid/replayer.js b/public/javascripts/lib/jasmid/replayer.js new file mode 100644 index 0000000..c7033df --- /dev/null +++ b/public/javascripts/lib/jasmid/replayer.js @@ -0,0 +1,170 @@ +function Replayer(midiFile, synth) { + var trackStates = []; + var beatsPerMinute = 120; + var ticksPerBeat = midiFile.header.ticksPerBeat; + var channelCount = 16; + + for (var i = 0; i < midiFile.tracks.length; i++) { + trackStates[i] = { + 'nextEventIndex': 0, + 'ticksToNextEvent': ( + midiFile.tracks[i].length ? + midiFile.tracks[i][0].deltaTime : + null + ) + }; + } + + function Channel() { + + var generatorsByNote = {}; + var currentProgram = PianoProgram; + + function noteOn(note, velocity) { + if (generatorsByNote[note] && !generatorsByNote[note].released) { + /* playing same note before releasing the last one. BOO */ + generatorsByNote[note].noteOff(); /* TODO: check whether we ought to be passing a velocity in */ + } + generator = currentProgram.createNote(note, velocity); + synth.addGenerator(generator); + generatorsByNote[note] = generator; + } + function noteOff(note, velocity) { + generatorsByNote[note].noteOff(velocity); + } + function setProgram(programNumber) { + currentProgram = PROGRAMS[programNumber] || PianoProgram; + } + + return { + 'noteOn': noteOn, + 'noteOff': noteOff, + 'setProgram': setProgram + } + } + + var channels = []; + for (var i = 0; i < channelCount; i++) { + channels[i] = Channel(); + } + + var nextEventInfo; + var samplesToNextEvent = 0; + + function getNextEvent() { + var ticksToNextEvent = null; + var nextEventTrack = null; + var nextEventIndex = null; + + for (var i = 0; i < trackStates.length; i++) { + if ( + trackStates[i].ticksToNextEvent != null + && (ticksToNextEvent == null || trackStates[i].ticksToNextEvent < ticksToNextEvent) + ) { + ticksToNextEvent = trackStates[i].ticksToNextEvent; + nextEventTrack = i; + nextEventIndex = trackStates[i].nextEventIndex; + } + } + if (nextEventTrack != null) { + /* consume event from that track */ + var nextEvent = midiFile.tracks[nextEventTrack][nextEventIndex]; + if (midiFile.tracks[nextEventTrack][nextEventIndex + 1]) { + trackStates[nextEventTrack].ticksToNextEvent += midiFile.tracks[nextEventTrack][nextEventIndex + 1].deltaTime; + } else { + trackStates[nextEventTrack].ticksToNextEvent = null; + } + trackStates[nextEventTrack].nextEventIndex += 1; + /* advance timings on all tracks by ticksToNextEvent */ + for (var i = 0; i < trackStates.length; i++) { + if (trackStates[i].ticksToNextEvent != null) { + trackStates[i].ticksToNextEvent -= ticksToNextEvent + } + } + nextEventInfo = { + 'ticksToEvent': ticksToNextEvent, + 'event': nextEvent, + 'track': nextEventTrack + } + var beatsToNextEvent = ticksToNextEvent / ticksPerBeat; + var secondsToNextEvent = beatsToNextEvent / (beatsPerMinute / 60); + samplesToNextEvent += secondsToNextEvent * synth.sampleRate; + } else { + nextEventInfo = null; + samplesToNextEvent = null; + self.finished = true; + } + } + + getNextEvent(); + + function generate(samples) { + var data = new Array(samples*2); + var samplesRemaining = samples; + var dataOffset = 0; + + while (true) { + if (samplesToNextEvent != null && samplesToNextEvent <= samplesRemaining) { + /* generate samplesToNextEvent samples, process event and repeat */ + var samplesToGenerate = Math.ceil(samplesToNextEvent); + if (samplesToGenerate > 0) { + synth.generateIntoBuffer(samplesToGenerate, data, dataOffset); + dataOffset += samplesToGenerate * 2; + samplesRemaining -= samplesToGenerate; + samplesToNextEvent -= samplesToGenerate; + } + + handleEvent(); + getNextEvent(); + } else { + /* generate samples to end of buffer */ + if (samplesRemaining > 0) { + synth.generateIntoBuffer(samplesRemaining, data, dataOffset); + samplesToNextEvent -= samplesRemaining; + } + break; + } + } + return data; + } + + function handleEvent() { + var event = nextEventInfo.event; + // console.info(event); + switch (event.type) { + case 'meta': + switch (event.subtype) { + case 'setTempo': + beatsPerMinute = 60000000 / event.microsecondsPerBeat + } + break; + case 'channel': + switch (event.subtype) { + case 'noteOn': + channels[event.channel].noteOn(event.noteNumber, event.velocity); + break; + case 'noteOff': + channels[event.channel].noteOff(event.noteNumber, event.velocity); + break; + case 'programChange': + //console.log('program change to ' + event.programNumber); + channels[event.channel].setProgram(event.programNumber); + break; + } + break; + } + } + + function replay(audio) { + console.log('replay'); + audio.write(generate(44100)); + setTimeout(function() {replay(audio)}, 10); + } + + var self = { + 'replay': replay, + 'generate': generate, + 'finished': false + } + return self; +} diff --git a/public/javascripts/lib/jasmid/stream.js b/public/javascripts/lib/jasmid/stream.js new file mode 100644 index 0000000..9fe7401 --- /dev/null +++ b/public/javascripts/lib/jasmid/stream.js @@ -0,0 +1,68 @@ +/* Wrapper for accessing strings through sequential reads */ +function Stream(str) { + var position = 0; + + function read(length) { + var result = str.substr(position, length); + position += length; + return result; + } + + /* read a big-endian 32-bit integer */ + function readInt32() { + var result = ( + (str.charCodeAt(position) << 24) + + (str.charCodeAt(position + 1) << 16) + + (str.charCodeAt(position + 2) << 8) + + str.charCodeAt(position + 3)); + position += 4; + return result; + } + + /* read a big-endian 16-bit integer */ + function readInt16() { + var result = ( + (str.charCodeAt(position) << 8) + + str.charCodeAt(position + 1)); + position += 2; + return result; + } + + /* read an 8-bit integer */ + function readInt8() { + var result = str.charCodeAt(position); + position += 1; + return result; + } + + function eof() { + return position >= str.length; + } + + /* read a MIDI-style variable-length integer + (big-endian value in groups of 7 bits, + with top bit set to signify that another byte follows) + */ + function readVarInt() { + var result = 0; + while (true) { + var b = readInt8(); + if (b & 0x80) { + result += (b & 0x7f); + result <<= 7; + } else { + /* b is the last byte */ + return result + b; + } + } + } + + return { + 'eof': eof, + 'read': read, + 'readInt32': readInt32, + 'readInt16': readInt16, + 'readInt8': readInt8, + 'readVarInt': readVarInt + } +} diff --git a/public/javascripts/lib/jasmid/synth.js b/public/javascripts/lib/jasmid/synth.js new file mode 100644 index 0000000..1ad6ce7 --- /dev/null +++ b/public/javascripts/lib/jasmid/synth.js @@ -0,0 +1,186 @@ +///var sampleRate = 44100; + +function SineGenerator(freq) { + var self = {'alive': true}; + var period = sampleRate / freq; + var t = 0; + + self.generate = function(buf, offset, count) { + for (; count; count--) { + var phase = t / period; + var result = Math.sin(phase * 2 * Math.PI); + buf[offset++] += result; + buf[offset++] += result; + t++; + } + } + + return self; +} + +function SquareGenerator(freq, phase) { + var self = {'alive': true}; + var period = sampleRate / freq; + var t = 0; + + self.generate = function(buf, offset, count) { + for (; count; count--) { + var result = ( (t / period) % 1 > phase ? 1 : -1); + buf[offset++] += result; + buf[offset++] += result; + t++; + } + } + + return self; +} + +function ADSRGenerator(child, attackAmplitude, sustainAmplitude, attackTimeS, decayTimeS, releaseTimeS) { + var self = {'alive': true} + var attackTime = sampleRate * attackTimeS; + var decayTime = sampleRate * (attackTimeS + decayTimeS); + var decayRate = (attackAmplitude - sustainAmplitude) / (decayTime - attackTime); + var releaseTime = null; /* not known yet */ + var endTime = null; /* not known yet */ + var releaseRate = sustainAmplitude / (sampleRate * releaseTimeS); + var t = 0; + + self.noteOff = function() { + if (self.released) return; + releaseTime = t; + self.released = true; + endTime = releaseTime + sampleRate * releaseTimeS; + } + + self.generate = function(buf, offset, count) { + if (!self.alive) return; + var input = new Array(count * 2); + for (var i = 0; i < count*2; i++) { + input[i] = 0; + } + child.generate(input, 0, count); + + childOffset = 0; + while(count) { + if (releaseTime != null) { + if (t < endTime) { + /* release */ + while(count && t < endTime) { + var ampl = sustainAmplitude - releaseRate * (t - releaseTime); + buf[offset++] += input[childOffset++] * ampl; + buf[offset++] += input[childOffset++] * ampl; + t++; + count--; + } + } else { + /* dead */ + self.alive = false; + return; + } + } else if (t < attackTime) { + /* attack */ + while(count && t < attackTime) { + var ampl = attackAmplitude * t / attackTime; + buf[offset++] += input[childOffset++] * ampl; + buf[offset++] += input[childOffset++] * ampl; + t++; + count--; + } + } else if (t < decayTime) { + /* decay */ + while(count && t < decayTime) { + var ampl = attackAmplitude - decayRate * (t - attackTime); + buf[offset++] += input[childOffset++] * ampl; + buf[offset++] += input[childOffset++] * ampl; + t++; + count--; + } + } else { + /* sustain */ + while(count) { + buf[offset++] += input[childOffset++] * sustainAmplitude; + buf[offset++] += input[childOffset++] * sustainAmplitude; + t++; + count--; + } + } + } + } + + return self; +} + +function midiToFrequency(note) { + return 440 * Math.pow(2, (note-69)/12); +} + +PianoProgram = { + 'attackAmplitude': 0.2, + 'sustainAmplitude': 0.1, + 'attackTime': 0.02, + 'decayTime': 0.3, + 'releaseTime': 0.02, + 'createNote': function(note, velocity) { + var frequency = midiToFrequency(note); + return ADSRGenerator( + SineGenerator(frequency), + this.attackAmplitude * (velocity / 128), this.sustainAmplitude * (velocity / 128), + this.attackTime, this.decayTime, this.releaseTime + ); + } +} + +StringProgram = { + 'createNote': function(note, velocity) { + var frequency = midiToFrequency(note); + return ADSRGenerator( + SineGenerator(frequency), + 0.5 * (velocity / 128), 0.2 * (velocity / 128), + 0.4, 0.8, 0.4 + ); + } +} + +PROGRAMS = { + 41: StringProgram, + 42: StringProgram, + 43: StringProgram, + 44: StringProgram, + 45: StringProgram, + 46: StringProgram, + 47: StringProgram, + 49: StringProgram, + 50: StringProgram +}; + +function Synth(sampleRate) { + + var generators = []; + + function addGenerator(generator) { + generators.push(generator); + } + + function generate(samples) { + var data = new Array(samples*2); + generateIntoBuffer(samples, data, 0); + return data; + } + + function generateIntoBuffer(samplesToGenerate, buffer, offset) { + for (var i = offset; i < offset + samplesToGenerate * 2; i++) { + buffer[i] = 0; + } + for (var i = generators.length - 1; i >= 0; i--) { + generators[i].generate(buffer, offset, samplesToGenerate); + if (!generators[i].alive) generators.splice(i, 1); + } + } + + return { + 'sampleRate': sampleRate, + 'addGenerator': addGenerator, + 'generate': generate, + 'generateIntoBuffer': generateIntoBuffer + } +} diff --git a/javascripts/lib/jquery-1.8.2.min.js b/public/javascripts/lib/jquery-1.8.2.min.js similarity index 100% rename from javascripts/lib/jquery-1.8.2.min.js rename to public/javascripts/lib/jquery-1.8.2.min.js diff --git a/javascripts/lib/jquery-ui-1.8.23.custom.min.js b/public/javascripts/lib/jquery-ui-1.8.23.custom.min.js similarity index 100% rename from javascripts/lib/jquery-ui-1.8.23.custom.min.js rename to public/javascripts/lib/jquery-ui-1.8.23.custom.min.js diff --git a/public/javascripts/lib/soundmanager2-jsmin.js b/public/javascripts/lib/soundmanager2-jsmin.js new file mode 100755 index 0000000..5563173 --- /dev/null +++ b/public/javascripts/lib/soundmanager2-jsmin.js @@ -0,0 +1,108 @@ +/** @license + + + SoundManager 2: JavaScript Sound for the Web + ---------------------------------------------- + http://schillmania.com/projects/soundmanager2/ + + Copyright (c) 2007, Scott Schiller. All rights reserved. + Code provided under the BSD License: + http://schillmania.com/projects/soundmanager2/license.txt + + V2.97a.20121104 +*/ +(function(G){function $($,pa){function aa(a){return c.preferFlash&&t&&!c.ignoreFlash&&void 0!==c.flash[a]&&c.flash[a]}function q(a){return function(d){var e=this._s;!e||!e._a?(e&&e.id?c._wD(l+"ignoring "+d.type+": "+e.id):c._wD(l+"ignoring "+d.type),d=null):d=a.call(this,d);return d}}this.setupOptions={url:$||null,flashVersion:8,debugMode:!0,debugFlash:!1,useConsole:!0,consoleOnly:!0,waitForWindowLoad:!1,bgColor:"#ffffff",useHighPerformance:!1,flashPollingInterval:null,html5PollingInterval:null,flashLoadTimeout:1E3, +wmode:null,allowScriptAccess:"always",useFlashBlock:!1,useHTML5Audio:!0,html5Test:/^(probably|maybe)$/i,preferFlash:!0,noSWFCache:!1};this.defaultOptions={autoLoad:!1,autoPlay:!1,from:null,loops:1,onid3:null,onload:null,whileloading:null,onplay:null,onpause:null,onresume:null,whileplaying:null,onposition:null,onstop:null,onfailure:null,onfinish:null,multiShot:!0,multiShotEvents:!1,position:null,pan:0,stream:!0,to:null,type:null,usePolicyFile:!1,volume:100};this.flash9Options={isMovieStar:null,usePeakData:!1, +useWaveformData:!1,useEQData:!1,onbufferchange:null,ondataerror:null};this.movieStarOptions={bufferTime:3,serverURL:null,onconnect:null,duration:null};this.audioFormats={mp3:{type:['audio/mpeg; codecs="mp3"',"audio/mpeg","audio/mp3","audio/MPA","audio/mpa-robust"],required:!0},mp4:{related:["aac","m4a","m4b"],type:['audio/mp4; codecs="mp4a.40.2"',"audio/aac","audio/x-m4a","audio/MP4A-LATM","audio/mpeg4-generic"],required:!1},ogg:{type:["audio/ogg; codecs=vorbis"],required:!1},wav:{type:['audio/wav; codecs="1"', +"audio/wav","audio/wave","audio/x-wav"],required:!1}};this.movieID="sm2-container";this.id=pa||"sm2movie";this.debugID="soundmanager-debug";this.debugURLParam=/([#?&])debug=1/i;this.versionNumber="V2.97a.20121104";this.altURL=this.movieURL=this.version=null;this.enabled=this.swfLoaded=!1;this.oMC=null;this.sounds={};this.soundIDs=[];this.didFlashBlock=this.muted=!1;this.filePattern=null;this.filePatterns={flash8:/\.mp3(\?.*)?$/i,flash9:/\.mp3(\?.*)?$/i};this.features={buffering:!1,peakData:!1,waveformData:!1, +eqData:!1,movieStar:!1};this.sandbox={type:null,types:{remote:"remote (domain-based) rules",localWithFile:"local with file access (no internet access)",localWithNetwork:"local with network (internet access only, no local access)",localTrusted:"local, trusted (local+internet access)"},description:null,noRemote:null,noLocal:null};this.html5={usingFlash:null};this.flash={};this.ignoreFlash=this.html5Only=!1;var La,c=this,Ma=null,g=null,l="HTML5::",A,u=navigator.userAgent,i=G,R=i.location.href.toString(), +j=document,qa,Na,ra,k,D=[],sa=!0,y,S=!1,T=!1,n=!1,v=!1,ba=!1,m,jb=0,U,w,ta,I,ua,J,K,L,Oa,va,ca,da,ea,M,wa,V,fa,ga,N,Pa,xa,kb=["log","info","warn","error"],Qa,ha,Ra,W=null,ya=null,p,za,O,Sa,ia,ja,P,s,X=!1,Aa=!1,Ta,Ua,Va,ka=0,Y=null,la,B=null,Wa,ma,Z,E,Ba,Ca,Xa,r,Ya=Array.prototype.slice,H=!1,Za,t,Da,$a,C,ab,Ea=u.match(/(ipad|iphone|ipod)/i),F=u.match(/msie/i),lb=u.match(/webkit/i),Fa=u.match(/safari/i)&&!u.match(/chrome/i),Ga=u.match(/opera/i),Ha=u.match(/(mobile|pre\/|xoom)/i)||Ea,Ia=!R.match(/usehtml5audio/i)&& +!R.match(/sm2\-ignorebadua/i)&&Fa&&!u.match(/silk/i)&&u.match(/OS X 10_6_([3-7])/i),bb=void 0!==G.console&&void 0!==console.log,Ja=void 0!==j.hasFocus?j.hasFocus():null,na=Fa&&(void 0===j.hasFocus||!j.hasFocus()),cb=!na,db=/(mp3|mp4|mpa|m4a|m4b)/i,Q=j.location?j.location.protocol.match(/http/i):null,eb=!Q?"http://":"",fb=/^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i,gb="mpeg4 aac flv mov mp4 m4v f4v m4a m4b mp4v 3gp 3g2".split(" "),mb=RegExp("\\.("+gb.join("|")+ +")(\\?.*)?$","i");this.mimePattern=/^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i;this.useAltURL=!Q;Ha&&(c.useHTML5Audio=!0,c.preferFlash=!1,Ea&&(H=c.ignoreFlash=!0));var Ka;try{Ka=void 0!==Audio&&void 0!==(Ga&&void 0!==opera&&10>opera.version()?new Audio(null):new Audio).canPlayType}catch(ob){Ka=!1}this.hasHTML5=Ka;this.setup=function(a){var d=!c.url;void 0!==a&&(n&&B&&c.ok()&&(void 0!==a.flashVersion||void 0!==a.url))&&P(p("setupLate"));ta(a);d&&(V&&void 0!==a.url)&&c.beginDelayedInit();!V&&(void 0!== +a.url&&"complete"===j.readyState)&&setTimeout(M,1);return c};this.supported=this.ok=function(){return B?n&&!v:c.useHTML5Audio&&c.hasHTML5};this.getMovie=function(c){return A(c)||j[c]||i[c]};this.createSound=function(a,d){function e(){f=ia(f);c.sounds[f.id]=new La(f);c.soundIDs.push(f.id);return c.sounds[f.id]}var b,f;b=null;b="soundManager.createSound(): "+p(!n?"notReady":"notOK");if(!n||!c.ok())return P(b),!1;void 0!==d&&(a={id:a,url:d});f=w(a);f.url=la(f.url);f.id.toString().charAt(0).match(/^[0-9]$/)&& +c._wD("soundManager.createSound(): "+p("badID",f.id),2);c._wD("soundManager.createSound(): "+f.id+" ("+f.url+")",1);if(s(f.id,!0))return c._wD("soundManager.createSound(): "+f.id+" exists",1),c.sounds[f.id];ma(f)?(b=e(),c._wD("Creating sound "+f.id+", using HTML5"),b._setup_html5(f)):(8=a)return!1;for(a-=1;0<=a;a--)c=z[a],!c.fired&&b.position>=c.position&&(c.fired=!0,r++,c.method.apply(c.scope,[c.position]));return!0};this._resetOnPosition=function(b){var a,c;a=z.length;if(!a)return!1;for(a-=1;0<=a;a--)c=z[a],c.fired&&b<=c.position&&(c.fired=!1,r--);return!0};v=function(){var a=b._iO,d=a.from,e=a.to,f,h;h=function(){c._wD(b.id+': "to" time of '+e+" reached.");b.clearOnPosition(e,h);b.stop()};f=function(){c._wD(b.id+': playing "from" '+ +d);if(null!==e&&!isNaN(e))b.onPosition(e,h)};null!==d&&!isNaN(d)&&(a.position=d,a.multiShot=!1,f());return a};n=function(){var a,c=b._iO.onposition;if(c)for(a in c)if(c.hasOwnProperty(a))b.onPosition(parseInt(a,10),c[a])};u=function(){var a,c=b._iO.onposition;if(c)for(a in c)c.hasOwnProperty(a)&&b.clearOnPosition(parseInt(a,10))};i=function(){b.isHTML5&&Ta(b)};h=function(){b.isHTML5&&Ua(b)};f=function(a){a||(z=[],r=0);q=!1;b._hasTimer=null;b._a=null;b._html5_canplay=!1;b.bytesLoaded=null;b.bytesTotal= +null;b.duration=b._iO&&b._iO.duration?b._iO.duration:null;b.durationEstimate=null;b.buffered=[];b.eqData=[];b.eqData.left=[];b.eqData.right=[];b.failures=0;b.isBuffering=!1;b.instanceOptions={};b.instanceCount=0;b.loaded=!1;b.metadata={};b.readyState=0;b.muted=!1;b.paused=!1;b.peakData={left:0,right:0};b.waveformData={left:[],right:[]};b.playState=0;b.position=null;b.id3={}};f();this._onTimer=function(a){var c,f=!1,h={};if(b._hasTimer||a){if(b._a&&(a||(0opera.version()?new Audio(null):new Audio,d=b._a,d._called_load=!1,H&&(Ma=d);b.isHTML5=!0;b._a=d;d._s=b;j();b._apply_loop(d,a.loops);a.autoLoad||a.autoPlay?b.load():(d.autobuffer=!1,d.preload="auto");return d}; +j=function(){if(b._a._added_events)return!1;var a;b._a._added_events=!0;for(a in C)C.hasOwnProperty(a)&&b._a&&b._a.addEventListener(a,C[a],!1);return!0};x=function(){var a;c._wD(l+"removing event listeners: "+b.id);b._a._added_events=!1;for(a in C)C.hasOwnProperty(a)&&b._a&&b._a.removeEventListener(a,C[a],!1)};this._onload=function(a){a=!!a||!b.isHTML5&&8===k&&b.duration;c._wD('SMSound._onload(): "'+b.id+'"'+(a?" loaded.":" failed to load? - "+b.url),a?1:2);!a&&!b.isHTML5&&(!0===c.sandbox.noRemote&& +c._wD("SMSound._onload(): "+p("noNet"),1),!0===c.sandbox.noLocal&&c._wD("SMSound._onload(): "+p("noLocal"),1));b.loaded=a;b.readyState=a?3:2;b._onbufferchange(0);b._iO.onload&&b._iO.onload.apply(b,[a]);return!0};this._onbufferchange=function(a){if(0===b.playState||a&&b.isBuffering||!a&&!b.isBuffering)return!1;b.isBuffering=1===a;b._iO.onbufferchange&&(c._wD("SMSound._onbufferchange(): "+a),b._iO.onbufferchange.apply(b));return!0};this._onsuspend=function(){b._iO.onsuspend&&(c._wD("SMSound._onsuspend()"), +b._iO.onsuspend.apply(b));return!0};this._onfailure=function(a,d,e){b.failures++;c._wD('SMSound._onfailure(): "'+b.id+'" count '+b.failures);if(b._iO.onfailure&&1===b.failures)b._iO.onfailure(b,a,d,e);else c._wD("SMSound._onfailure(): ignoring")};this._onfinish=function(){var a=b._iO.onfinish;b._onbufferchange(0);b._resetOnPosition(0);if(b.instanceCount&&(b.instanceCount--,b.instanceCount||(u(),b.playState=0,b.paused=!1,b.instanceCount=0,b.instanceOptions={},b._iO={},h(),b.isHTML5&&(b.position=0)), +(!b.instanceCount||b._iO.multiShotEvents)&&a))c._wD('SMSound._onfinish(): "'+b.id+'"'),a.apply(b)};this._whileloading=function(a,c,d,e){var f=b._iO;b.bytesLoaded=a;b.bytesTotal=c;b.duration=Math.floor(d);b.bufferLength=e;b.durationEstimate=!b.isHTML5&&!f.isMovieStar?f.duration?b.duration>f.duration?b.duration:f.duration:parseInt(b.bytesTotal/b.bytesLoaded*b.duration,10):b.duration;b.isHTML5||(b.buffered=[{start:0,end:b.duration}]);(3!==b.readyState||b.isHTML5)&&f.whileloading&&f.whileloading.apply(b)}; +this._whileplaying=function(a,c,d,e,f){var h=b._iO;if(isNaN(a)||null===a)return!1;b.position=Math.max(0,a);b._processOnPosition();!b.isHTML5&&8opera.version()?new Audio(null):new Audio:null,e,b,f={},g;g=c.audioFormats;for(e in g)if(g.hasOwnProperty(e)&& +(b="audio/"+e,f[e]=a(g[e].type),f[b]=f[e],e.match(db)?(c.flash[e]=!0,c.flash[b]=!0):(c.flash[e]=!1,c.flash[b]=!1),g[e]&&g[e].related))for(b=g[e].related.length-1;0<=b;b--)f["audio/"+g[e].related[b]]=f[e],c.html5[g[e].related[b]]=f[e],c.flash[g[e].related[b]]=f[e];f.canPlayType=d?a:null;c.html5=w(c.html5,f);return!0};da={notReady:"Not loaded yet - wait for soundManager.onready()",notOK:"Audio support is not available.",domError:"soundManager::createMovie(): appendChild/innerHTML call failed. DOM not ready or other error.", +spcWmode:"soundManager::createMovie(): Removing wmode, preventing known SWF loading issue(s)",swf404:"soundManager: Verify that %s is a valid path.",tryDebug:"Try soundManager.debugFlash = true for more security details (output goes to SWF.)",checkSWF:"See SWF output for more debug info.",localFail:"soundManager: Non-HTTP page ("+j.location.protocol+" URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/", +waitFocus:"soundManager: Special case: Waiting for SWF to load with window focus...",waitImpatient:"soundManager: Getting impatient, still waiting for Flash%s...",waitForever:"soundManager: Waiting indefinitely for Flash (will recover if unblocked)...",waitSWF:"soundManager: Retrying, waiting for 100% SWF load...",needFunction:"soundManager: Function object expected for %s",badID:'Warning: Sound ID "%s" should be a string, starting with a non-numeric character',currentObj:"--- soundManager._debug(): Current sound objects ---", +waitEI:"soundManager::initMovie(): Waiting for ExternalInterface call from Flash...",waitOnload:"soundManager: Waiting for window.onload()",docLoaded:"soundManager: Document already loaded",onload:"soundManager::initComplete(): calling soundManager.onload()",onloadOK:"soundManager.onload() complete",init:"soundManager::init()",didInit:"soundManager::init(): Already called?",flashJS:"soundManager: Attempting JS to Flash call...",secNote:"Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html", +badRemove:"Warning: Failed to remove flash movie.",shutdown:"soundManager.disable(): Shutting down",queue:"soundManager: Queueing %s handler",smFail:"soundManager: Failed to initialise.",smError:"SMSound.load(): Exception: JS-Flash communication failed, or JS error.",fbTimeout:"No flash response, applying .swf_timedout CSS...",fbLoaded:"Flash loaded",fbHandler:"soundManager::flashBlockHandler()",manURL:"SMSound.load(): Using manually-assigned URL",onURL:"soundManager.load(): current URL already assigned.", +badFV:'soundManager.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.',as2loop:"Note: Setting stream:false so looping can work (flash 8 limitation)",noNSLoop:"Note: Looping not implemented for MovieStar formats",needfl9:"Note: Switching to flash 9, required for MP4 formats.",mfTimeout:"Setting flashLoadTimeout = 0 (infinite) for off-screen, mobile flash case",needFlash:"soundManager: Fatal error: Flash is needed to play some required formats, but is not available.",gotFocus:"soundManager: Got window focus.", +mfOn:"mobileFlash::enabling on-screen flash repositioning",policy:"Enabling usePolicyFile for data access",setup:"soundManager.setup(): allowed parameters: %s",setupError:'soundManager.setup(): "%s" cannot be assigned with this method.',setupUndef:'soundManager.setup(): Could not find option "%s"',setupLate:"soundManager.setup(): url + flashVersion changes will not take effect until reboot().",h5a:"creating HTML5 Audio() object",noURL:"soundManager: Flash URL required. Call soundManager.setup({url:...}) to get started."}; +p=function(){var a=Ya.call(arguments),c=a.shift(),c=da&&da[c]?da[c]:"",e,b;if(c&&a&&a.length){e=0;for(b=a.length;ek)&&(c._wD(p("needfl9")),c.flashVersion=k=9);c.version=c.versionNumber+(c.html5Only?" (HTML5-only mode)":9===k?" (AS3/Flash 9)":" (AS2/Flash 8)");8'}if(S&&T)return!1;if(c.html5Only)return va(),e(),c.oMC=A(c.movieID),ra(),T=S=!0,!1;var f=d||c.url,g=c.altURL||f,i=fa(),h=O(),k=null,k=j.getElementsByTagName("html")[0],l,q,n,k=k&&k.dir&&k.dir.match(/rtl/i),a=void 0===a?c.id:a;va();c.url=Ra(Q?f:g);d=c.url;c.wmode=!c.wmode&&c.useHighPerformance?"transparent":c.wmode;if(null!==c.wmode&&(u.match(/msie 8/i)||!F&&!c.useHighPerformance)&&navigator.platform.match(/win32|win64/i))m("spcWmode"),c.wmode=null;i={name:a,id:a,src:d,quality:"high", +allowScriptAccess:c.allowScriptAccess,bgcolor:c.bgColor,pluginspage:eb+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)",type:"application/x-shockwave-flash",wmode:c.wmode,hasPriority:"true"};c.debugFlash&&(i.FlashVars="debug=1");c.wmode||delete i.wmode;if(F)f=j.createElement("div"),q=['', +b("movie",d),b("AllowScriptAccess",c.allowScriptAccess),b("quality",i.quality),c.wmode?b("wmode",c.wmode):"",b("bgcolor",c.bgColor),b("hasPriority","true"),c.debugFlash?b("FlashVars",i.FlashVars):"",""].join("");else for(l in f=j.createElement("embed"),i)i.hasOwnProperty(l)&&f.setAttribute(l,i[l]);xa();h=O();if(i=fa())if(c.oMC=A(c.movieID)||j.createElement("div"),c.oMC.id)n=c.oMC.className,c.oMC.className=(n?n+" ":"movieContainer")+(h?" "+h:""),c.oMC.appendChild(f),F&&(l=c.oMC.appendChild(j.createElement("div")), +l.className="sm2-object-box",l.innerHTML=q),T=!0;else{c.oMC.id=c.movieID;c.oMC.className="movieContainer "+h;l=h=null;c.useFlashBlock||(c.useHighPerformance?h={position:"fixed",width:"8px",height:"8px",bottom:"0px",left:"0px",overflow:"hidden"}:(h={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},k&&(h.left=Math.abs(parseInt(h.left,10))+"px")));lb&&(c.oMC.style.zIndex=1E4);if(!c.debugFlash)for(n in h)h.hasOwnProperty(n)&&(c.oMC.style[n]=h[n]);try{F||c.oMC.appendChild(f), +i.appendChild(c.oMC),F&&(l=c.oMC.appendChild(j.createElement("div")),l.className="sm2-object-box",l.innerHTML=q),T=!0}catch(r){throw Error(p("domError")+" \n"+r.toString());}}S=!0;e();c._wD("soundManager::createMovie(): Trying to load "+d+(!Q&&c.altURL?" (alternate URL)":""),1);return!0};ea=function(){if(c.html5Only)return ga(),!1;if(g)return!1;if(!c.url)return m("noURL"),!1;g=c.getMovie(c.id);g||(W?(F?c.oMC.innerHTML=ya:c.oMC.appendChild(W),W=null,S=!0):ga(c.id,c.url),g=c.getMovie(c.id));g&&m("waitEI"); +"function"===typeof c.oninitmovie&&setTimeout(c.oninitmovie,1);return!0};L=function(){setTimeout(Oa,1E3)};Oa=function(){var a,d=!1;if(!c.url||X)return!1;X=!0;r.remove(i,"load",L);if(na&&!Ja)return m("waitFocus"),!1;n||(a=c.getMoviePercent(),c._wD(p("waitImpatient",0a&&(d=!0));setTimeout(function(){a=c.getMoviePercent();if(d)return X=!1,c._wD(p("waitSWF")),i.setTimeout(L,1),!1;n||(c._wD("soundManager: No Flash response within expected time.\nLikely causes: "+ +(0===a?"Loading "+c.movieURL+" may have failed (and/or Flash "+k+"+ not present?), ":"")+"Flash blocked or JS-Flash security error."+(c.debugFlash?" "+p("checkSWF"):""),2),!Q&&a&&(m("localFail",2),c.debugFlash||m("tryDebug",2)),0===a&&c._wD(p("swf404",c.url)),y("flashtojs",!1,": Timed out"+Q?" (Check flash security or flash blockers)":" (No plugin/missing SWF?)"));!n&&cb&&(null===a?c.useFlashBlock||0===c.flashLoadTimeout?(c.useFlashBlock&&za(),m("waitForever")):ha(!0):0===c.flashLoadTimeout?m("waitForever"): +ha(!0))},c.flashLoadTimeout)};ca=function(){if(Ja||!na)return r.remove(i,"focus",ca),!0;Ja=cb=!0;m("gotFocus");X=!1;L();r.remove(i,"focus",ca);return!0};ab=function(){var a,d=[];if(c.useHTML5Audio&&c.hasHTML5){for(a in c.audioFormats)c.audioFormats.hasOwnProperty(a)&&d.push(a+": "+c.html5[a]+(!c.html5[a]&&t&&c.flash[a]?" (using flash)":c.preferFlash&&c.flash[a]&&t?" (preferring flash)":!c.html5[a]?" ("+(c.audioFormats[a].required?"required, ":"")+"and no flash support)":""));c._wD("-- SoundManager 2: HTML5 support tests ("+ +c.html5Test+"): "+d.join(", ")+" --",1)}};U=function(a){if(n)return!1;if(c.html5Only)return c._wD("-- SoundManager 2: loaded --"),n=!0,K(),y("onload",!0),!0;var d=!0,e;if(!c.useFlashBlock||!c.flashLoadTimeout||c.getMoviePercent())n=!0,v&&(e={type:!t&&B?"NO_FLASH":"INIT_TIMEOUT"});c._wD("-- SoundManager 2 "+(v?"failed to load":"loaded")+" ("+(v?"Flash security/load error":"OK")+") --",1);v||a?(c.useFlashBlock&&c.oMC&&(c.oMC.className=O()+" "+(null===c.getMoviePercent()?"swf_timedout":"swf_error")), +J({type:"ontimeout",error:e,ignoreInit:!0}),y("onload",!1),N(e),d=!1):y("onload",!0);v||(c.waitForWindowLoad&&!ba?(m("waitOnload"),r.add(i,"load",K)):(c.waitForWindowLoad&&ba&&m("docLoaded"),K()));return d};Na=function(){var a,d=c.setupOptions;for(a in d)d.hasOwnProperty(a)&&(void 0===c[a]?c[a]=d[a]:c[a]!==d[a]&&(c.setupOptions[a]=c[a]))};ra=function(){m("init");if(n)return m("didInit"),!1;if(c.html5Only)return n||(r.remove(i,"load",c.beginDelayedInit),c.enabled=!0,U()),!0;ea();try{m("flashJS"),g._externalInterfaceTest(!1), +Pa(!0,c.flashPollingInterval||(c.useHighPerformance?10:50)),c.debugMode||g._disableDebug(),c.enabled=!0,y("jstoflash",!0),c.html5Only||r.add(i,"unload",qa)}catch(a){return c._wD("js/flash exception: "+a.toString()),y("jstoflash",!1),N({type:"JS_TO_FLASH_EXCEPTION",fatal:!0}),ha(!0),U(),!1}U();r.remove(i,"load",c.beginDelayedInit);return!0};M=function(){if(V)return!1;V=!0;Na();xa();var a=null,a=null,d=void 0!==G.console&&"function"===typeof console.log,e=R.toLowerCase();-1!==e.indexOf("sm2-usehtml5audio=")&& +(a="1"===e.charAt(e.indexOf("sm2-usehtml5audio=")+18),d&&console.log((a?"Enabling ":"Disabling ")+"useHTML5Audio via URL parameter"),c.setup({useHTML5Audio:a}));-1!==e.indexOf("sm2-preferflash=")&&(a="1"===e.charAt(e.indexOf("sm2-preferflash=")+16),d&&console.log((a?"Enabling ":"Disabling ")+"preferFlash via URL parameter"),c.setup({preferFlash:a}));!t&&c.hasHTML5&&(c._wD("SoundManager: No Flash detected"+(!c.useHTML5Audio?", enabling HTML5.":". Trying HTML5-only mode.")),c.setup({useHTML5Audio:!0, +preferFlash:!1}));Xa();c.html5.usingFlash=Wa();B=c.html5.usingFlash;ab();!t&&B&&(m("needFlash"),c.setup({flashLoadTimeout:1}));j.removeEventListener&&j.removeEventListener("DOMContentLoaded",M,!1);ea();return!0};Ca=function(){"complete"===j.readyState&&(M(),j.detachEvent("onreadystatechange",Ca));return!0};wa=function(){ba=!0;r.remove(i,"load",wa)};Da();r.add(i,"focus",ca);r.add(i,"load",L);r.add(i,"load",wa);j.addEventListener?j.addEventListener("DOMContentLoaded",M,!1):j.attachEvent?j.attachEvent("onreadystatechange", +Ca):(y("onload",!1),N({type:"NO_DOM2_EVENTS",fatal:!0}))}var pa=null;if(void 0===G.SM2_DEFER||!SM2_DEFER)pa=new $;G.SoundManager=$;G.soundManager=pa})(window); \ No newline at end of file diff --git a/javascripts/lib/underscore.js b/public/javascripts/lib/underscore.js similarity index 100% rename from javascripts/lib/underscore.js rename to public/javascripts/lib/underscore.js diff --git a/javascripts/main.js b/public/javascripts/main.js similarity index 71% rename from javascripts/main.js rename to public/javascripts/main.js index 4b2e60d..a4b9a5c 100644 --- a/javascripts/main.js +++ b/public/javascripts/main.js @@ -18,6 +18,7 @@ var the_game; var board_size = [8, 16]; var level = params['level'] || 0; var speed = params['speed'] || 'Low'; +var song = params['music'] || 'fever'; if (canvas) { canvas.setAttribute('width', board_size[0] * block_size + "px"); @@ -30,8 +31,20 @@ if (canvas) { var oracle_ctx = oracle.getContext('2d'); window.onload = function() { - the_game = new Game(level, speed); + Sound.init(); + Sound.musicSet(song); + the_game = new Game(level, speed, song); the_game.start(); + + $('.next-level-button').bind('click', function () { + $('#nextLevelModal').trigger('reveal:close'); + the_game.paused = false; + }); + + $('.restart-button').on('click', function (e) { + e.preventDefault(); + the_game.restart(); + }); } } diff --git a/public/javascripts/midiplayer.js b/public/javascripts/midiplayer.js new file mode 100644 index 0000000..ac3b5a5 --- /dev/null +++ b/public/javascripts/midiplayer.js @@ -0,0 +1,69 @@ +MIDIPlayer = function () { + + this.played = false; +}; + +MIDIPlayer.prototype = { + play: function () { + if (!this.file) { + return false; + } + this.promise.done(_.bind(this.playMidi, this)); + }, + + stop: function () { + if (!this.file) { + return false; + } + + this.promise.done(_.bind(this.stopMidi, this)); + }, + + load: function (url) { + this.file = url; + var self = this; + + this.promise = $.Deferred(); + + var fetch = new XMLHttpRequest(); + fetch.open('GET', url); + fetch.overrideMimeType("text/plain; charset=x-user-defined"); + fetch.onreadystatechange = function() { + if(this.readyState == 4 && this.status == 200) { + var t = this.responseText || "" ; + var ff = []; + var mx = t.length; + for (var z = 0; z < mx; z++) { + ff[z] = String.fromCharCode(t.charCodeAt(z) & 255); + } + self.data = ff.join(""); + self.promise.resolve(); + } + } + fetch.send(); + }, + + playMidi: function () { + if (this.playing) { + return false; + } + + this.midifile = MidiFile(this.data); + this.synth = Synth(44100); + this.replayer = Replayer(this.midifile, this.synth); + this.aplayer = AudioPlayer(this.replayer); + this.playing = true; + }, + + stopMidi: function () { + if (!this.playing) { + return false; + } + + this.aplayer.stop(); + this.playing = false; + } + + +} + diff --git a/public/javascripts/music.js b/public/javascripts/music.js new file mode 100644 index 0000000..e69de29 diff --git a/javascripts/pill.js b/public/javascripts/pill.js similarity index 100% rename from javascripts/pill.js rename to public/javascripts/pill.js diff --git a/public/javascripts/reveal.js b/public/javascripts/reveal.js new file mode 100644 index 0000000..9140d48 --- /dev/null +++ b/public/javascripts/reveal.js @@ -0,0 +1,160 @@ +/* + * jQuery Reveal Plugin 1.0 + * www.ZURB.com + * Copyright 2010, ZURB + * Free to use under the MIT license. + * http://www.opensource.org/licenses/mit-license.php +*/ + + +(function($) { + +/*--------------------------- + Defaults for Reveal +----------------------------*/ + +/*--------------------------- + Listener for data-reveal-id attributes +----------------------------*/ + + $('a[data-reveal-id]').live('click', function(e) { + e.preventDefault(); + var modalLocation = $(this).attr('data-reveal-id'); + $('#'+modalLocation).reveal($(this).data()); + }); + +/*--------------------------- + Extend and Execute +----------------------------*/ + + $.fn.reveal = function(options) { + + + var defaults = { + animation: 'fadeAndPop', //fade, fadeAndPop, none + animationspeed: 300, //how fast animtions are + closeonbackgroundclick: true, //if you click background will modal close? + dismissmodalclass: 'close-reveal-modal' //the class of a button or element that will close an open modal + }; + + //Extend dem' options + var options = $.extend({}, defaults, options); + + return this.each(function() { + +/*--------------------------- + Global Variables +----------------------------*/ + var modal = $(this), + topMeasure = parseInt(modal.css('top')), + topOffset = modal.height() + topMeasure, + locked = false, + modalBG = $('.reveal-modal-bg'); + +/*--------------------------- + Create Modal BG +----------------------------*/ + if(modalBG.length == 0) { + modalBG = $('
').insertAfter(modal); + } + +/*--------------------------- + Open & Close Animations +----------------------------*/ + //Entrance Animations + modal.bind('reveal:open', function () { + modalBG.unbind('click.modalEvent'); + $('.' + options.dismissmodalclass).unbind('click.modalEvent'); + if(!locked) { + lockModal(); + if(options.animation == "fadeAndPop") { + modal.css({'top': $(document).scrollTop()-topOffset, 'opacity' : 0, 'visibility' : 'visible'}); + modalBG.fadeIn(options.animationspeed/2); + modal.delay(options.animationspeed/2).animate({ + "top": $(document).scrollTop()+topMeasure + 'px', + "opacity" : 1 + }, options.animationspeed,unlockModal()); + } + if(options.animation == "fade") { + modal.css({'opacity' : 0, 'visibility' : 'visible', 'top': $(document).scrollTop()+topMeasure}); + modalBG.fadeIn(options.animationspeed/2); + modal.delay(options.animationspeed/2).animate({ + "opacity" : 1 + }, options.animationspeed,unlockModal()); + } + if(options.animation == "none") { + modal.css({'visibility' : 'visible', 'top':$(document).scrollTop()+topMeasure}); + modalBG.css({"display":"block"}); + unlockModal() + } + } + modal.unbind('reveal:open'); + }); + + //Closing Animation + modal.bind('reveal:close', function () { + if(!locked) { + lockModal(); + if(options.animation == "fadeAndPop") { + modalBG.delay(options.animationspeed).fadeOut(options.animationspeed); + modal.animate({ + "top": $(document).scrollTop()-topOffset + 'px', + "opacity" : 0 + }, options.animationspeed/2, function() { + modal.css({'top':topMeasure, 'opacity' : 1, 'visibility' : 'hidden'}); + unlockModal(); + }); + } + if(options.animation == "fade") { + modalBG.delay(options.animationspeed).fadeOut(options.animationspeed); + modal.animate({ + "opacity" : 0 + }, options.animationspeed, function() { + modal.css({'opacity' : 1, 'visibility' : 'hidden', 'top' : topMeasure}); + unlockModal(); + }); + } + if(options.animation == "none") { + modal.css({'visibility' : 'hidden', 'top' : topMeasure}); + modalBG.css({'display' : 'none'}); + } + } + modal.unbind('reveal:close'); + }); + +/*--------------------------- + Open and add Closing Listeners +----------------------------*/ + //Open Modal Immediately + modal.trigger('reveal:open') + + //Close Modal Listeners + var closeButton = $('.' + options.dismissmodalclass).bind('click.modalEvent', function () { + modal.trigger('reveal:close') + }); + + if(options.closeonbackgroundclick) { + modalBG.css({"cursor":"pointer"}) + modalBG.bind('click.modalEvent', function () { + modal.trigger('reveal:close') + }); + } + $('body').keyup(function(e) { + if(e.which===27){ modal.trigger('reveal:close'); } // 27 is the keycode for the Escape key + }); + + +/*--------------------------- + Animations Locks +----------------------------*/ + function unlockModal() { + locked = false; + } + function lockModal() { + locked = true; + } + + });//each call + }//orbit plugin call +})(jQuery); + diff --git a/public/javascripts/score.js b/public/javascripts/score.js new file mode 100644 index 0000000..a6421c8 --- /dev/null +++ b/public/javascripts/score.js @@ -0,0 +1,17 @@ +module.exports = function(parse_app) { + return { + addHighScore: function(name, score, level, callback) { + parse_app.insert('GameScore', { name: name, score: score, level: level }, function (err, response) { + if(callback) + callback(err, response); + }); + }, + + getHighScore: function(callback) { + parse_app.findMany('GameScore', '?order=-score&limit=10', function (err, response) { + if(callback) + callback(err, response.results); + }); + } + } +} diff --git a/public/javascripts/setup.js b/public/javascripts/setup.js new file mode 100644 index 0000000..dcfe45b --- /dev/null +++ b/public/javascripts/setup.js @@ -0,0 +1,238 @@ +var speeds = ["LOW", "MED", "HI"]; +var musics = ["fever", "chill", "off"]; + +drawingConfig = { + level_count: 20, + level_line_size: 340, + level_start_x: 20, + level_start_y: 5, + level_end_y: 15, + level_slice_size: 16 +}; + +function Setup() { + this.level = $( "#level_slider" ).slider( "value" ); + this.speed = speeds[ $( "#speed_slider" ).slider( "value" ) ]; + this.music = musics[ $( "#music_slider" ).slider( "value" ) ]; +}; + +function start(){ + var settings = new Setup(); + + window.location.href = "play?" + jQuery.param(settings); +}; + +function populateLeaderBoard(){ + + var board = $("tbody","#leader_board"), + template = _.template([ + "<% _.times(5, function(i) { %>", + " ", + " <%= ['1st','2nd','3rd','4th','5th'][i] %>", + " <%= leaders[i] && leaders[i].score || '***' %>", + " <%= leaders[i] && leaders[i].level || '***' %>", + " <%= leaders[i] && leaders[i].name || '***' %>", + " ", + "<% }); %>" + ].join("")); + + board.html(template({ + leaders: [] + })); + + $.get("/highscore", {}, function(response) { + board.html(template({ + leaders: _.sortBy(response.data, function(leader){ + return parseInt(leader.score); + }).reverse() + })); + }); + +}; + +function drawLevelLine(){ + var canvas = document.getElementById("level_canvas"), + context = canvas.getContext("2d"); + + context.strokeStyle = 'white'; + context.lineWidth = 2; + + context.beginPath(); + context.moveTo(drawingConfig.level_start_x, 10); + context.lineTo(drawingConfig.level_line_size, 10); + context.stroke(); + context.closePath(); + + for (i=0; i <= drawingConfig.level_count; i++){ + var x = drawingConfig.level_start_x + i * drawingConfig.level_slice_size, + start_y = drawingConfig.level_start_y, + end_y = drawingConfig.level_end_y; + + if(i % 5 == 0){ + start_y -= 5; + end_y += 5; + } + + context.beginPath(); + context.moveTo(x, start_y); + context.lineTo(x, end_y); + context.stroke(); + context.closePath(); + } +} + +$(function(){ + + $("#level_slider").slider({ + value:0, + min: 0, + max: 20, + step: 1, + change: function( event, ui ) { + $("#level_amount").html( ui.value ); + } + }); + $("#level_slider .ui-slider-handle").unbind("keydown"); + + $("#speed_slider").slider({ + min: 0, + max: speeds.length - 1, + step: 1 + }); + $("#speed_slider .ui-slider-handle").unbind("keydown"); + + $("#music_slider").slider({ + min: 0, + max: musics.length - 1, + step: 1, + change: setMusic + }); + $("#music_slider .ui-slider-handle").unbind("keydown"); + + $("#play").bind('click', function(){ + start(); + return false; + }); + + drawLevelLine(); + + populateLeaderBoard(); + + $('#level_slider .ui-slider-handle').focus(function() { + $("#level_title").addClass('border'); + $("#speed_title").removeClass('border'); + $("#music_title").removeClass('border'); + }); + + $('#speed_slider .ui-slider-handle').focus(function() { + $("#speed_title").addClass('border'); + $("#level_title").removeClass('border'); + $("#music_title").removeClass('border'); + }); + + $('#music_slider .ui-slider-handle').focus(function() { + $("#music_title").addClass('border'); + $("#speed_title").removeClass('border'); + $("#level_title").removeClass('border'); + }); + + $("#level_canvas").on("click", function(e) { + var value = Math.round(Math.min(Math.max((e.offsetX - 20) / 320 * 20, 0), 20)); + $("#level_amount").html(value); + $("#level_slider").slider("value", value); + setFocus("level"); + }); + + $("p",".options").each(function(i,element) { + $(element).on("click", function() { + if(i < 3) { + $("#speed_slider").slider("value", i % 3); + setFocus("speed"); + } else { + $("#music_slider").slider("value", i % 3); + setFocus("music"); + } + }); + }); + + setFocus(); + + Sound.init() + setMusic() + +}); + +var focused = 0; +var focus_order = ["level", "speed", "music"]; +var focus_handles = { + level: "#level_slider .ui-slider-handle", + speed: "#speed_slider .ui-slider-handle", + music: "#music_slider .ui-slider-handle" +}; +var focus_values = { + level: "#level_slider", + speed: "#speed_slider", + music: "#music_slider" +}; + +var setFocus = function(option) { + + if(!option) { + $(focus_handles[focus_order[focused]]).focus(); + } else { + $(focus_handles[option]).focus(); + focused = focus_order.indexOf(option); + } + +} + +var setFocusNext = function() { + focused = (focused + 1) % focus_order.length; + $(focus_handles[focus_order[focused]]).focus(); +} + +var setFocusPrev = function() { + focused = focused === 0 ? focus_order.length - 1 : focused - 1; + $(focus_handles[focus_order[focused]]).focus(); +} + +var goLeft = function() { + var new_value = $(focus_values[focus_order[focused]]).slider("value") - 1; + $(focus_values[focus_order[focused]]).slider("value", new_value); +} + +var goRight = function() { + var new_value = $(focus_values[focus_order[focused]]).slider("value") + 1; + $(focus_values[focus_order[focused]]).slider("value", new_value); +} + +var setMusic = function () { + var music = musics[$("#music_slider").slider("value")]; + if (music != "off") { + Sound.musicSet(music); + } else { + Sound.musicStop(); + } +} + +$(window).on('keydown', function(e) { + var code = (e.keyCode ? e.keyCode : e.which); + + switch (code) { + case 13: + start(); + break; + case 37: + goLeft(); + break; + case 38: + setFocusPrev(); + break; + case 39: + goRight(); + break; + case 40: + setFocusNext(); + break; + } +}); \ No newline at end of file diff --git a/public/javascripts/sound.js b/public/javascripts/sound.js new file mode 100644 index 0000000..289b4ee --- /dev/null +++ b/public/javascripts/sound.js @@ -0,0 +1,88 @@ +window.Sound = (function() { + + var sounds = { + rotate: "/wav/rotate.wav", + move: "/wav/move.wav", + explode: "/wav/explode.wav", + fever: "/mp3/fever.mp3", + chill: "/mp3/chill.mp3" + }; + + return { + + song: false, + canPlay: false, + operative: false, + + init: function() { + var _this = this; + soundManager.setup({ + url: '/flash/', + flashLoadTimeout: 5000, + onready: function() { + _.each(sounds, function(url, id) { + soundManager.createSound({ + id: id, + url: url + }); + }); + _this.canPlay = true; + _this.musicPlay(); + }, + ontimeout: function(status) { + console.log('SM2 failed to start. Flash missing, blocked or security error?'); + console.log('The status is ' + status.success + ', the error type is ' + status.error.type); + } + }); + }, + + rotate: function() { + if (this.operative) { + soundManager.play("rotate"); + } + }, + + move: function() { + if (this.operative) { + soundManager.play("move"); + } + }, + + explode: function() { + if (this.operative) { + soundManager.play("explode"); + } + }, + + musicSet: function(song) { + this.song = song; + if(this.canPlay) { + this.musicStop(); + this.musicPlay(); + } + }, + + musicPlay: function() { + if(this.song) { + this.operative = true; + soundManager.play(this.song, { + onfinish: this.musicPlay + }); + } + }, + + musicStop: function() { + this.operative = false; + soundManager.stopAll(); + }, + + toggleOnOff: function(){ + if (this.operative) { + this.musicStop(); + } else { + this.musicPlay(); + } + } + }; + +})(); \ No newline at end of file diff --git a/javascripts/virus.js b/public/javascripts/virus.js similarity index 96% rename from javascripts/virus.js rename to public/javascripts/virus.js index e12cd0b..e9178ed 100644 --- a/javascripts/virus.js +++ b/public/javascripts/virus.js @@ -40,8 +40,8 @@ Virus.prototype.setPosition = function() { var randY = Math.floor(Math.random() * 13) + this.maxY; // we dont ever put anything in the first X rows based on level if (this.board.occupied(randX, randY) === undefined && this.board.inBounds(randX, randY)) { - this.position.x = randX; - this.position.y = randY; + this.position.x = randX; + this.position.y = randY; this.board.addPiece(this, 0); } else { this.setPosition(); diff --git a/public/midi/master01.mid b/public/midi/master01.mid new file mode 100644 index 0000000..05e001b Binary files /dev/null and b/public/midi/master01.mid differ diff --git a/public/midi/master02.mid b/public/midi/master02.mid new file mode 100644 index 0000000..1b3fa04 Binary files /dev/null and b/public/midi/master02.mid differ diff --git a/public/mp3/chill.mp3 b/public/mp3/chill.mp3 new file mode 100644 index 0000000..3402572 Binary files /dev/null and b/public/mp3/chill.mp3 differ diff --git a/public/mp3/fever.mp3 b/public/mp3/fever.mp3 new file mode 100644 index 0000000..dcb9294 Binary files /dev/null and b/public/mp3/fever.mp3 differ diff --git a/styles/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/public/styles/images/ui-bg_diagonals-thick_18_b81900_40x40.png similarity index 100% rename from styles/images/ui-bg_diagonals-thick_18_b81900_40x40.png rename to public/styles/images/ui-bg_diagonals-thick_18_b81900_40x40.png diff --git a/styles/images/ui-bg_diagonals-thick_20_666666_40x40.png b/public/styles/images/ui-bg_diagonals-thick_20_666666_40x40.png similarity index 100% rename from styles/images/ui-bg_diagonals-thick_20_666666_40x40.png rename to public/styles/images/ui-bg_diagonals-thick_20_666666_40x40.png diff --git a/styles/images/ui-bg_flat_10_000000_40x100.png b/public/styles/images/ui-bg_flat_10_000000_40x100.png similarity index 100% rename from styles/images/ui-bg_flat_10_000000_40x100.png rename to public/styles/images/ui-bg_flat_10_000000_40x100.png diff --git a/styles/images/ui-bg_glass_100_f6f6f6_1x400.png b/public/styles/images/ui-bg_glass_100_f6f6f6_1x400.png similarity index 100% rename from styles/images/ui-bg_glass_100_f6f6f6_1x400.png rename to public/styles/images/ui-bg_glass_100_f6f6f6_1x400.png diff --git a/styles/images/ui-bg_glass_100_fdf5ce_1x400.png b/public/styles/images/ui-bg_glass_100_fdf5ce_1x400.png similarity index 100% rename from styles/images/ui-bg_glass_100_fdf5ce_1x400.png rename to public/styles/images/ui-bg_glass_100_fdf5ce_1x400.png diff --git a/styles/images/ui-bg_glass_65_ffffff_1x400.png b/public/styles/images/ui-bg_glass_65_ffffff_1x400.png similarity index 100% rename from styles/images/ui-bg_glass_65_ffffff_1x400.png rename to public/styles/images/ui-bg_glass_65_ffffff_1x400.png diff --git a/styles/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/public/styles/images/ui-bg_gloss-wave_35_f6a828_500x100.png similarity index 100% rename from styles/images/ui-bg_gloss-wave_35_f6a828_500x100.png rename to public/styles/images/ui-bg_gloss-wave_35_f6a828_500x100.png diff --git a/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/public/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png similarity index 100% rename from styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png rename to public/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png diff --git a/styles/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/public/styles/images/ui-bg_highlight-soft_75_ffe45c_1x100.png similarity index 100% rename from styles/images/ui-bg_highlight-soft_75_ffe45c_1x100.png rename to public/styles/images/ui-bg_highlight-soft_75_ffe45c_1x100.png diff --git a/styles/images/ui-icons_222222_256x240.png b/public/styles/images/ui-icons_222222_256x240.png similarity index 100% rename from styles/images/ui-icons_222222_256x240.png rename to public/styles/images/ui-icons_222222_256x240.png diff --git a/styles/images/ui-icons_228ef1_256x240.png b/public/styles/images/ui-icons_228ef1_256x240.png similarity index 100% rename from styles/images/ui-icons_228ef1_256x240.png rename to public/styles/images/ui-icons_228ef1_256x240.png diff --git a/styles/images/ui-icons_ef8c08_256x240.png b/public/styles/images/ui-icons_ef8c08_256x240.png similarity index 100% rename from styles/images/ui-icons_ef8c08_256x240.png rename to public/styles/images/ui-icons_ef8c08_256x240.png diff --git a/styles/images/ui-icons_ffd27a_256x240.png b/public/styles/images/ui-icons_ffd27a_256x240.png similarity index 100% rename from styles/images/ui-icons_ffd27a_256x240.png rename to public/styles/images/ui-icons_ffd27a_256x240.png diff --git a/styles/images/ui-icons_ffffff_256x240.png b/public/styles/images/ui-icons_ffffff_256x240.png similarity index 100% rename from styles/images/ui-icons_ffffff_256x240.png rename to public/styles/images/ui-icons_ffffff_256x240.png diff --git a/styles/jquery-ui-1.8.23.custom.css b/public/styles/jquery-ui-1.8.23.custom.css similarity index 100% rename from styles/jquery-ui-1.8.23.custom.css rename to public/styles/jquery-ui-1.8.23.custom.css diff --git a/public/styles/reveal.css b/public/styles/reveal.css new file mode 100644 index 0000000..511f7ac --- /dev/null +++ b/public/styles/reveal.css @@ -0,0 +1,67 @@ +/* -------------------------------------------------- + Reveal Modals + -------------------------------------------------- */ + + .reveal-modal-bg { + position: fixed; + height: 100%; + width: 100%; + background: #000; + background: rgba(0,0,0,.8); + z-index: 100; + display: none; + top: 0; + left: 0; + } + + .reveal-modal { + visibility: hidden; + top: 100px; + left: 50%; + margin-left: -300px; + width: 520px; + background: #fff; + border:2px solid #d33717; + position: absolute; + z-index: 101; + padding: 20px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + -moz-box-shadow: 0 0 10px rgba(0,0,0,.4); + -webkit-box-shadow: 0 0 10px rgba(0,0,0,.4); + -box-shadow: 0 0 10px rgba(0,0,0,.4); + } + + .reveal-modal.small { width: 200px; margin-left: -140px;} + .reveal-modal.medium { width: 400px; margin-left: -240px;} + .reveal-modal.large { width: 600px; margin-left: -340px;} + .reveal-modal.xlarge { width: 800px; margin-left: -440px;} + + .reveal-modal .close-reveal-modal { + font-size: 22px; + line-height: .5; + position: absolute; + top: 15px; + right: 15px; + color: #fff; + text-shadow: 0 -1px 1px rbga(0,0,0,.6); + font-weight: bold; + cursor: pointer; + } + /* + + NOTES + + Close button entity is × + + Example markup + +
+

Awesome. I have it.

+

Your couch. I it's mine.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ultrices aliquet placerat. Duis pulvinar orci et nisi euismod vitae tempus lorem consectetur. Duis at magna quis turpis mattis venenatis eget id diam.

+ × +
+ + */ diff --git a/styles/styles.css b/public/styles/styles.css similarity index 73% rename from styles/styles.css rename to public/styles/styles.css index 5564b85..53c7003 100644 --- a/styles/styles.css +++ b/public/styles/styles.css @@ -18,10 +18,8 @@ time, mark, audio, video { font: inherit; vertical-align: baseline; } -html { - background-image: url("../images/bg.png"); } - body { + background-image: url("../images/bg.png"); line-height: 1; max-width: 100%; overflow: hidden; } @@ -304,14 +302,18 @@ html { background-position: center; border-bottom: 0px; } +input:focus, +select:focus, +textarea:focus { + outline: none; } + .pane { - width: 55%; + width: 400px; background: black; padding: 5%; color: #FFF; margin: 0px auto; border-radius: 10px; - min-width: 400px; position: relative; border: 14px solid brown; border-image: url(../images/game_border2.png) 20 20 20 20 round round; @@ -319,40 +321,55 @@ html { background-size: 99999999999999999px; background-position: center; z-index: 1; } + .pane #leader_board { + color: white; } + .pane #leader_board table { + width: 100%; + table-layout: fixed; } + .pane #leader_board table th { + padding-bottom: 20px; + text-align: right; } + .pane #leader_board table td { + padding-bottom: 10px; + text-align: right; } + .pane #leader_board table td:first-child { + text-align: left; } .pane .border { - border: 3px solid #CC7A29; + border: 3px solid #CC7A29 !important; padding: 5px; width: 200px; border-radius: 6px; } + .pane #leader_board { + color: white; } .pane .option { - margin: 60px 40px; } + margin-left: 22px; } .pane .option .title { + border: 3px solid transparent; + width: 200px; color: #FFB870; - padding: 5px; + padding: 7px; clear: both; - margin: 22px 0px; } + margin-left: -20px; + margin-top: 10px; + margin-bottom: 30px; } .pane .option .players { float: left; width: 10%; } - .pane .option .sliders { - float: left; - width: 60%; } - .pane .option .sliders p { - color: white; } - .pane .option .sliders .left { - float: left; } - .pane .option .sliders .center { - text-align: center; - margin-left: 20px; } - .pane .option .sliders .right { - float: right; - left: 100%; - margin-right: -27px; - margin-top: -18px; } + .pane .option .sliders .options { + width: 360px; } + .pane .option .sliders .options p { + display: inline-block; + float: left; + width: 50px; } + .pane .option .sliders .options p.center { + padding-left: 29%; } + .pane .option .sliders .options p.right { + padding-left: 29%; } .pane .option .levels { - float: right; - width: 10%; - margin: -50px 40px; } + position: relative; + top: -62px; + left: -24px; + width: 10%; } .pane .option .speeds { float: left; width: 10%; @@ -361,9 +378,10 @@ html { float: left; width: 10%; margin-left: 25px; } - .pane .option #play { - float: right; - margin-right: 10px; } + +#play { + margin-top: 40px; + text-align: center; } .level-number { float: right; @@ -372,7 +390,21 @@ html { -moz-border-radius: 5px; width: 40px; height: 20px; - text-align: center; } + text-align: center; + line-height: 1.3em; } + +#level_canvas_wrap { + height: 20px; } + +#level_canvas { + display: block; + height: 20px; + position: relative; + top: -18px; } + +.ui-slider.ui-slider-horizontal.ui-widget.ui-widget-content.ui-corner-all { + width: 320px; + height: 0px; } .ui-slider-handle.ui-state-default.ui-corner-all { background: black; @@ -381,10 +413,72 @@ html { height: 0; border-left: 20px solid transparent; border-right: 20px solid transparent; - border-top: 20px solid #B2B26B; } + border-top: 20px solid #B2B26B; + border-bottom: 0px solid #B2B26B; + outline: none; } .ui-slider.ui-slider-horizontal.ui-widget.ui-widget-content.ui-corner-all { background: black; content: "LOW MED HI"; border: 0px; align-text: justify; } + +.modal { + border: 4px solid gray !important; } + +.nextLevel { + width: 200px; + float: left; } + +.nextLevelNumber { + float: left; } + +.next-level-button { + width: 16px; + float: right; + text-decoration: none; } + +.restart-button { + margin-right: 20px; + float: right; + width: 80px; } + +.submit-button { + margin-right: 20px; + float: right; + width: 80px; } + +.button-link { + font-size: 12px !important; + padding: 10px 15px; + background: #4479BA; + color: #FFF; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: solid 1px #20538D; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2); + -webkit-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: hand; + cursor: pointer; } + +.button-link:hover { + background: #356094; + border: solid 1px #2A4E77; + text-decoration: none; } + +.button-link:active { + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + background: #2E5481; + border: solid 1px #203E5F; } diff --git a/styles/styles.scss b/public/styles/styles.scss similarity index 73% rename from styles/styles.scss rename to public/styles/styles.scss index 46f3aa2..84558f1 100644 --- a/styles/styles.scss +++ b/public/styles/styles.scss @@ -17,10 +17,8 @@ time, mark, audio, video { font-size: 100%; font: inherit; vertical-align: baseline; } -html { - background-image: url('../images/bg.png'); -} body { + background-image: url('../images/bg.png'); line-height: 1; max-width: 100%; overflow: hidden; @@ -91,7 +89,7 @@ width: 100%; text-align:left; width: 450px; margin: 15px auto; - } + } } #instructions { @@ -138,7 +136,7 @@ text-align: center; max-width: 256px; } -#clipboardLeft { +#clipboardLeft { background-color: #BDBDFF; width: 80%; border-top: solid 10px #FFBD00; @@ -201,7 +199,7 @@ padding-top: 2.4%; from { -webkit-transform: rotate(0deg); } - to { + to { -webkit-transform: rotate(360deg); } } @@ -331,14 +329,19 @@ margin: 25px -125px 0 0; border-bottom: 0px; } +input:focus, +select:focus, +textarea:focus { + outline: none; +} + .pane{ - width: 55%; + width: 400px; background: black; padding: 5%; color: #FFF; margin: 0px auto; border-radius: 10px; - min-width: 400px; position: relative; border: 14px solid brown; border-image: url(../images/game_border2.png) 20 20 20 20 round round; @@ -347,45 +350,69 @@ margin: 25px -125px 0 0; background-position: center; z-index: 1; + #leader_board{ + color: white; + table{ + width: 100%; + table-layout: fixed; + th{ + padding-bottom: 20px; + text-align: right; + } + td{ + padding-bottom: 10px; + text-align: right; + } + td:first-child{ + text-align: left; + } + } + } + .border{ - border: 3px solid #CC7A29; + border: 3px solid #CC7A29 !important; padding: 5px; width: 200px; border-radius: 6px; } + #leader_board{ + color: white; + } + .option{ - margin: 60px 40px; + margin-left: 22px; .title{ + border: 3px solid transparent; + width: 200px; color: #FFB870; - padding: 5px; + padding: 7px; clear: both; - margin: 22px 0px; + margin-left: -20px; + margin-top: 10px; + margin-bottom: 30px; } .players{ float: left; width: 10%; } .sliders{ - float: left; - width: 60%; - p{ - color: white; + .options{ + width: 360px; + p{ + display: inline-block; + float: left; + width: 50px; + } + p.center{ padding-left: 29%; } + p.right{ padding-left: 29%; } } - .left{float: left;} - .center{ - text-align: center; - margin-left: 20px;} - .right{ - float: right; - left: 100%; - margin-right: -27px; - margin-top: -18px;} } .levels{ - float: right; + position: relative; + top: -62px; + left: -24px; width: 10%; - margin: -50px 40px; } .speeds{ float: left; @@ -397,13 +424,14 @@ margin: 25px -125px 0 0; width: 10%; margin-left: 25px; } - #play{ - float: right; - margin-right: 10px; - } } } +#play{ + margin-top: 40px; + text-align: center; +} + .level-number{ float: right; border: 3px solid #0f0; @@ -412,16 +440,38 @@ margin: 25px -125px 0 0; width: 40px; height: 20px; text-align: center; + line-height: 1.3em; +} + +#level_canvas_wrap{ + height: 20px; +} + +#level_canvas{ + display: block; + height: 20px; + position: relative; + top: -18px; +} + +#level_slider.ui-slider.ui-slider-horizontal.ui-widget.ui-widget-content.ui-corner-all{ +} + +.ui-slider.ui-slider-horizontal.ui-widget.ui-widget-content.ui-corner-all{ + width: 320px; + height: 0px; } .ui-slider-handle.ui-state-default.ui-corner-all{ background: black; margin: -16px 0px; - width: 0; - height: 0; + width: 0; + height: 0; border-left: 20px solid transparent; - border-right: 20px solid transparent; + border-right: 20px solid transparent; border-top: 20px solid #B2B26B; + border-bottom: 0px solid #B2B26B; + outline: none; } .ui-slider.ui-slider-horizontal.ui-widget.ui-widget-content.ui-corner-all{ @@ -429,4 +479,71 @@ margin: 25px -125px 0 0; content: "LOW MED HI"; border: 0px; align-text: justify; +} + +.modal{ + border: 4px solid gray !important; +} + +.nextLevel{ + width: 200px; + float: left; +} + +.nextLevelNumber{ + float: left; +} + +.next-level-button{ + width: 16px; + float: right; + text-decoration: none; +} + +.restart-button{ + margin-right: 20px; + float: right; + width: 80px; +} + +.submit-button{ + margin-right: 20px; + float: right; + width: 80px; +} + +.button-link { + font-size: 12px !important; + padding: 10px 15px; + background: #4479BA; + color: #FFF; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: solid 1px #20538D; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2); + -webkit-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-user-select:none; + -moz-user-select:none; + -ms-user-select:none; + user-select:none; + cursor: hand; + cursor: pointer; +} +.button-link:hover { + background: #356094; + border: solid 1px #2A4E77; + text-decoration: none; +} +.button-link:active { + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + background: #2E5481; + border: solid 1px #203E5F; } \ No newline at end of file diff --git a/public/wav/explode.wav b/public/wav/explode.wav new file mode 100644 index 0000000..9e3646f Binary files /dev/null and b/public/wav/explode.wav differ diff --git a/public/wav/move.wav b/public/wav/move.wav new file mode 100644 index 0000000..ff5390e Binary files /dev/null and b/public/wav/move.wav differ diff --git a/public/wav/rotate.wav b/public/wav/rotate.wav new file mode 100644 index 0000000..cce8c08 Binary files /dev/null and b/public/wav/rotate.wav differ diff --git a/server.js b/server.js new file mode 100644 index 0000000..8e957a3 --- /dev/null +++ b/server.js @@ -0,0 +1,53 @@ +var express = require('express'), + port = 8888; + +var oneYear = 31557600000; + +var app = express.createServer(); + +var Parse = require('node-parse-api').Parse; +var APP_ID = "8kJxzEB8G8Rx3mYk8kM8snrHryd4dtqTMgd7MKpD"; +var MASTER_KEY = "DSgulGiahlyXFJ1s9qvSPRW4hdeKrgiozv7U12NS"; +var parse_app = new Parse(APP_ID, MASTER_KEY); + +var score = require('./public/javascripts/score.js')(parse_app); + +app.use(express.static(__dirname + '/public', { maxAge: oneYear })); +app.use(express.cookieParser()); +app.use(express.session({ secret: 'secret' })); +app.use(express.bodyParser()); +app.use(express.csrf()); + +app.dynamicHelpers({ + token: function(req, res) { + return req.session._csrf; + } +}); + +app.set("view engine", "jade"); +app.set('view options', { layout: false }); + +app.get('/play', function(req, res){ + res.render('play_game'); +}); + +app.get('/', function(req, res){ + res.render('index'); +}); + +app.get('/highscore', function(req, res){ + score.getHighScore(function(err, response) { + res.json({"error": err, "data": response}); + }); +}); + +app.post('/highscore', function(req, res) { + score.addHighScore(req.body.name, req.body.score, req.body.level, function(err, data) { + res.json({"error": err, "data": data}); + }) +}); + +app.listen(port); + +console.log('Listening on ' + port + ' in ' + app.settings.env + ' mode ...'); +console.log('Press Ctrl + C to stop.'); diff --git a/src/play_game.html b/src/play_game.html deleted file mode 100644 index 6110baa..0000000 --- a/src/play_game.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - -
-
-

- -

-
-
TOP
-
-
-
-
SCORE
-
-
-
-
-
- -
- -
-
-
- -
-
-
- -
-
- -
-
-

- -

-
-
-

- -

- -
-
LEVEL
-
-
SPEED
-
-
VIRUS
-
-
-
-
-
- -
-
-
a:
-
Rotates vitamin capsules to the right (clockwise).
- -
s:
-
Rotates vitamin capsules to the left (counter clockwise).
- -
Left & Right:
-
Moves vitamin capsules left and right.
- -
Down Arrow:
-
Drops vitamin capsule down quickly.
- -
Space:
-
Pauses the Game.
- -
?:
-
Display gameplay instructions.
-
-
-
-

Goal Of The Game

- -

Destroy the viruses by using the right vitamin capsules. When all of the viruses in the bottle are destroyed, you progress to the next stage. If the bottle gets filled all the way to the top, the game is over.

- -

How To Eliminate Viruses

- -

As the vitamin capsules drop use the Z or X keys to rotate them. Try to match up four of the same color in a vertical or horizontal row. If you do this, all four will disappear. With a little practice you'll be able to get rid of all the viruses!"

-
- -
- h - help -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..bd2b263 --- /dev/null +++ b/views/index.jade @@ -0,0 +1,52 @@ +!!! 5 +html +head + + link(type='text/css', rel='stylesheet', href='http://fonts.googleapis.com/css?family=Press+Start+2P') + link(type='text/css', rel='stylesheet', href='styles/styles.css') + link(type='text/css', rel='stylesheet', href='styles/jquery-ui-1.8.23.custom.css') + + script(type='text/javascript', src='javascripts/lib/jquery-1.8.2.min.js') + script(type='text/javascript', src='javascripts/lib/underscore.js') + script(type='text/javascript', src='javascripts/lib/jquery-ui-1.8.23.custom.min.js') + script(type="text/javascript", src="javascripts/lib/soundmanager2-jsmin.js") + script(type='text/javascript', src='javascripts/setup.js') + script(type='text/javascript', src='javascripts/sound.js') + + body.grid + .arrow-down + .top_section DR. MOJO + .pane + #leader_board + table + thead + tr + th + th SCORE + th LEVEL + th NAME + tbody + .option + #level_title.title VIRUS LEVEL + #level_amount.levels.amount.level-number 0 + .sliders + #level_slider + #level_canvas_wrap + canvas#level_canvas(width='360', height='20') + .option + #speed_title.title SPEED + .sliders + #speed_slider + .options + p.left LOW + p.center MED + p.right HI + .option + #music_title.title MUSIC TYPE + .sliders + #music_slider + .options + p.left FEVER + p.center CHILL + p.right OFF + #play (HIT ENTER TO PLAY) diff --git a/views/play_game.jade b/views/play_game.jade new file mode 100644 index 0000000..b46e257 --- /dev/null +++ b/views/play_game.jade @@ -0,0 +1,106 @@ +html + head + + link(type='text/css', rel='stylesheet', href='http://fonts.googleapis.com/css?family=Press+Start+2P') + link(type='text/css', rel='stylesheet', href='/styles/styles.css') + link(type='text/css', rel='stylesheet', href='/styles/reveal.css') + + script(type="text/javascript", src='/javascripts/lib/underscore.js') + script(type="text/javascript", src='/javascripts/lib/jquery-1.8.2.min.js') + script(type="text/javascript", src='/javascripts/lib/backbone-min.js') + script(type="text/javascript", src="javascripts/lib/soundmanager2-jsmin.js") + script(type="text/javascript", src='http://www.parsecdn.com/js/parse-1.1.1.min.js') + + body + input(name='csrf', value=token, type='hidden', id="hidden-csrf") + #gameOverModal.reveal-modal.modal + p Game Over + .button-link.restart-button Restart + + #gameOverHighScoreModal.reveal-modal.modal + p Congratulations, you have the new highscore! Enter your initials below to become even more famous than you already are. + br + input(name="player-name", value="", type="text", maxlength="3", id="player-name") + .button-link.submit-button Submit + + #nextLevelModal.reveal-modal.modal + .nextLevel Next Level + .nextLevelNumber + .button-link.next-level-button Ok + + #columnOne + // + Begin Column One + #clipboardLeft + p.clipboardTop + img(src='/images/clipboard_top.png') + #highScore + .text TOP + .score + #score + .text SCORE + .score + .clipboardBottom + #dancingViruses + img(src='/images/magnifying_glass.png') + #magnifyingGlass + img#animatedViruses(src='/images/animated_viruses.gif') + #columnTwo + // + Begin Column Two + #hold + #gameTop + canvas#game + #columnThree + // + Begin Column Three + #HUD + p + img(src='/images/mojo_logo.png') + #clipboardRight + p.clipboardTop + img(src='/images/clipboard_top.png') + .info + .level LEVEL + #level_count + .speed SPEED + #speed_count + .virus VIRUS + #viruses + .clipboardBottom + .controls + dl + dt a: + dd Rotates vitamin capsules to the right (clockwise). + dt s: + dd Rotates vitamin capsules to the left (counter clockwise). + dt Left & Right: + dd Moves vitamin capsules left and right. + dt Down Arrow: + dd Drops vitamin capsule down quickly. + dt Space: + dd Pauses the Game. + dt m: + dd Toggle music on/off + dt ?: + dd Display gameplay instructions. + #instructions + p Goal Of The Game + p + | Destroy the viruses by using the right vitamin capsules. When all of the viruses in the bottle are destroyed, you progress to the next stage. If the bottle gets filled all the way to the top, the game is over. + p How To Eliminate Viruses + p + | As the vitamin capsules drop use the A or S keys to rotate them. Try to match up four of the same color in a vertical or horizontal row. If you do this, all four will disappear. With a little practice you'll be able to get rid of all the viruses!" + .helps + | h - help + script(src='/javascripts/PieceDrawer.js') + script(src='/javascripts/board.js') + script(src='/javascripts/collisionDetector.js') + script(src='/javascripts/pill.js') + script(src='/javascripts/virus.js') + script(src='/javascripts/game.js') + script(src='/javascripts/level.js') + script(src='/javascripts/main.js') + script(src='/javascripts/Utils.js') + script(src='/javascripts/sound.js') + script(src='/javascripts/reveal.js')