Initial push; huzzah.
This commit is contained in:
commit
0b1c7ff386
12 changed files with 1188 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2009 - 2011 Ryan McGrath
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
64
build.py
Normal file
64
build.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Build-script that brings together all the JS files for wii.js and
|
||||
takes care of minifying it down to a decent size.
|
||||
|
||||
Before you even ask, no, we're not using uglify.js or Closure. Both
|
||||
of them are simply too aggressive for their own good; while normally this
|
||||
is an excellent feature to have, the JS engine that the Opera version on the Wii
|
||||
uses has a really odd set of quirks when you start trying to get smart with code,
|
||||
and YUI is the only minifier that doesn't appear to totally destroy it all through
|
||||
obfuscation. We'll forego a few bytes to have the thing working. ;P
|
||||
|
||||
Uglify also requires Node.js. While I *love* Node, I'll wait until it's more readily
|
||||
available on Windows - there are a large amount of Windows-based programmers that I'm
|
||||
not keen on keeping out of development.
|
||||
"""
|
||||
|
||||
import os
|
||||
from subprocess import call
|
||||
|
||||
currdir = os.getcwd()
|
||||
|
||||
# The names of our JS files (minus the .js) that we want to build into the
|
||||
# final distribution.
|
||||
DEPENDENCIES = [
|
||||
'wii',
|
||||
'util',
|
||||
'remote',
|
||||
]
|
||||
|
||||
# What we're going to end up injecting as our core build, into core.js.
|
||||
inject_build = ''
|
||||
|
||||
def minify_code(filename, final_filename):
|
||||
"""
|
||||
Dips out to Java/YUI compressor and runs the minifier on the specified file, dumping the
|
||||
output into the specified final_filename.
|
||||
"""
|
||||
#cmd = 'java -jar %s/utilities/yuicompressor-2.4.2.jar %s -o %s' % (currdir, filename, final_filename)
|
||||
call(['java', '-jar', '%s/utilities/yuicompressor-2.4.2.jar' % currdir, filename, '-o', final_filename])
|
||||
|
||||
# Run through each dependency, read it into "inject build", then do a simple split on the
|
||||
# contents of core.js and wrap it all up.
|
||||
for dependency in DEPENDENCIES:
|
||||
f = open('%s/js/src/%s.js' % (currdir, dependency), 'r')
|
||||
inject_build += f.read()
|
||||
f.close()
|
||||
|
||||
# Open core.js, split it on our build spot, wrap junks.
|
||||
f = open('%s/js/src/core.js' % currdir, 'r')
|
||||
core = f.read()
|
||||
f.close()
|
||||
core = core.split('/*{{inject_build}}*/')
|
||||
|
||||
# Write out a non-minified build.
|
||||
f = open('%s/js/wii.js' % currdir, 'w')
|
||||
f.write(core[0])
|
||||
f.write(inject_build)
|
||||
f.write(core[1])
|
||||
f.close()
|
||||
|
||||
# Write out a minified build.
|
||||
minify_code('%s/js/wii.js' % currdir, '%s/js/wii.min.js' % currdir)
|
||||
25
css/demo.css
Normal file
25
css/demo.css
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Wii Demo CSS
|
||||
*
|
||||
* Uses a basic CSS reset, and then some very rudimentary styles to make stuff easy
|
||||
* on the eyes with the Wii console.
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, font, img, kbd, q, s, samp,
|
||||
small, strike, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {
|
||||
margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
|
||||
:focus { outline: 0; }
|
||||
body { line-height: 1; }
|
||||
ol, ul { list-style: none; }
|
||||
table { border-collapse: separate; border-spacing: 0; }
|
||||
caption, th, td { text-align: left; font-weight: normal; }
|
||||
blockquote:before, blockquote:after, q:before, q:after { content: ""; }
|
||||
blockquote, q { quotes: "" ""; }
|
||||
|
||||
html {
|
||||
color: #333;
|
||||
font: normal 30px/34px helvetica, arial, sans-serif;
|
||||
padding: 0px;
|
||||
text-align: center;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
99
index.html
Normal file
99
index.html
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Wii.js Demo</title>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/demo.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Wii.js Demo</h1>
|
||||
<p>
|
||||
This is a demo page meant to show how easy it is to interact with the Nintendo Wii remotes using Javascript. It's
|
||||
powered by <a href="http://github.com/ryanmcgrath/wii-js/">wii-js</a>, a Javascript library that abstracts the differences
|
||||
and pain points associated with using the Wii remotes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This demo is really simple, and works best with two Wii remotes (Wiimotes). Hold either Wiimote #1 or #2 horizontal (sideways), and use
|
||||
the directional pad to move the box floating in the bottom right corner around. Wiimote #1 will turn the box red and fill it with a "1",
|
||||
and Wiimote #2 will turn the box green and fill it with a "2". Have fun!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you find bugs or want to get in touch, come find me at the project home on <a href="http://github.com/ryanmcgrath/wii-js/">GitHub</a>! You
|
||||
can also find me on Twitter (<a href="http://twitter.com/ryanmcgrath">@ryanmcgrath</a>), or my <a href="http://venodesigns.net/">personal website</a>.
|
||||
I'm always interested in what others are building!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
- Ryan McGrath
|
||||
</p>
|
||||
|
||||
<div id="x" style="border: 5px solid #010101; position: absolute; top: 300px; left: 300px; width: 100px; height: 100px; padding: 10px; color: #010101;">x</div>
|
||||
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
|
||||
<script src="js/wii.js"></script>
|
||||
<script>
|
||||
var wiimote = new Wii.Remote(1, {horizontal: true}),
|
||||
wiimote2 = new Wii.Remote(2, {horizontal: true}),
|
||||
x = $("#x");
|
||||
|
||||
wiimote2.when('pressed_up', function() {
|
||||
x.html('2').css({
|
||||
'background-color': 'green',
|
||||
'top': parseInt(x[0].style.top) - 50
|
||||
});
|
||||
});
|
||||
|
||||
wiimote2.when('pressed_down', function() {
|
||||
x.html('2').css({
|
||||
'background-color': 'green',
|
||||
'top': parseInt(x[0].style.top) + 50
|
||||
});
|
||||
});
|
||||
|
||||
wiimote2.when('pressed_right', function() {
|
||||
x.html('2').css({
|
||||
'background-color': 'green',
|
||||
'left': parseInt(x[0].style.left) + 50
|
||||
});
|
||||
|
||||
wiimote2.when('pressed_left', function() {
|
||||
x.html('2').css({
|
||||
'background-color': 'green',
|
||||
'left': parseInt(x[0].style.left) - 50
|
||||
});
|
||||
});
|
||||
|
||||
wiimote.when('pressed_up', function() {
|
||||
x.html('1').css({
|
||||
'background-color': 'red',
|
||||
'top': parseInt(x[0].style.top) - 50
|
||||
});
|
||||
});
|
||||
|
||||
wiimote.when('pressed_down', function() {
|
||||
x.html('1').css({
|
||||
'background-color': 'red',
|
||||
'top': parseInt(x[0].style.top) + 50
|
||||
});
|
||||
});
|
||||
|
||||
wiimote.when('pressed_right', function() {
|
||||
x.html('1').css({
|
||||
'background-color': 'red',
|
||||
'left': parseInt(x[0].style.left) + 50
|
||||
});
|
||||
});
|
||||
|
||||
wiimote.when('pressed_left', function() {
|
||||
x.html('1').css({
|
||||
'background-color': 'red',
|
||||
'left': parseInt(x[0].style.left) - 50
|
||||
});
|
||||
});
|
||||
|
||||
Wii.listen();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
27
js/src/core.js
Normal file
27
js/src/core.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* core.js
|
||||
*
|
||||
* This is the main template file for bringing together
|
||||
* the various libraries that power this entire little eco-system.
|
||||
*
|
||||
* Building the releases requires Python (2.5+); simply run...
|
||||
*
|
||||
* python build.py
|
||||
*
|
||||
* ...from the /js/ directory.
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: Nothing, top-level file.
|
||||
*/
|
||||
|
||||
;(function(running_inside_wii_browser) {
|
||||
/**
|
||||
* If we're not running inside the Nintendo Wii browser, bail out.
|
||||
* In the future, branch here for touch-enabled devices...?
|
||||
*/
|
||||
if(!running_inside_wii_browser) return false;
|
||||
|
||||
/*{{inject_build}}*/
|
||||
|
||||
window.Wii = Wii;
|
||||
})(window.opera && opera.wiiremote);
|
||||
120
js/src/remote.js
Normal file
120
js/src/remote.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* remote.js
|
||||
*
|
||||
* Handles the subscribing to events portion of a Wii remote. It's best to think of this
|
||||
* as a "request" object; it asks to be notified of events, and the actual events are
|
||||
* dispatched from the main wii.js file.
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: wii.js, util.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* var wii_remote = new Wii.Remote(1, {...});
|
||||
*
|
||||
* Instantiates a Wii Remote object. Events can be set on each of these objects,
|
||||
* and the internal game loop will fire events based on the properties subscribed
|
||||
* to here.
|
||||
*
|
||||
* @param remote_id - Number, required. 1 - 4, dictates which Wiimote this object
|
||||
* relates to.
|
||||
* @param opts - Object, optional. Allows you to override internal settings and such,
|
||||
* should you want different behavior.
|
||||
* @returns Wii.Remote instance.
|
||||
*/
|
||||
Wii.Remote = function(remote_id, opts) {
|
||||
this.remote_id = remote_id;
|
||||
this.opts = opts;
|
||||
|
||||
/**
|
||||
* If this is the "main" wii_remote, then the bitwise checks will fail
|
||||
* because it's treated more as a "browsing" device. For these events,
|
||||
* we'll just store the current wii_remote that's denoted as the "browsing"
|
||||
* device and let the normal event/key delegation take care of things.
|
||||
*
|
||||
* The rest of the wii_remotes will go through the DISPATCHER checks that
|
||||
* they've subscribed to.
|
||||
*/
|
||||
var startupStatus = this.isEnabled();
|
||||
if(startupStatus) {
|
||||
if(!startupStatus.isBrowsing) {
|
||||
Wii.extraRemotes.push(this);
|
||||
} else {
|
||||
Wii.currentBrowsingRemote = this;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Wii.Remote.prototype = {
|
||||
opts: {
|
||||
/**
|
||||
* We default the controller to be in the vertical orientation; if
|
||||
* it's overridden as "horizontal" (false), we'll catch it for the different key
|
||||
* events and fire accordingly (e.g, the "up" button is different depending on
|
||||
* how the player is holding the controller).
|
||||
*/
|
||||
horizontal: false
|
||||
},
|
||||
|
||||
/**
|
||||
* A hash of events that this Wii remote is interested in. Each
|
||||
* entry should be a key (evtName) with a value (response).
|
||||
* "evtName" is the event name that corresponds with boolean functions
|
||||
* in the DISPATCHER above, and the response is a remote-bound function
|
||||
* to call on that event.
|
||||
*/
|
||||
evtsInterestedIn: undefined,
|
||||
|
||||
/**
|
||||
* Wiimote.isEnabled()
|
||||
*
|
||||
* Determines the status of the wii_remote this object is curious about. Will return
|
||||
* an updated kPadStatus object if so, false if it's not responding or the data
|
||||
* is sent back as "invalid". This makes it so we don't bother sending events
|
||||
* where they're not applicable.
|
||||
*
|
||||
* @returns object or boolean.
|
||||
*/
|
||||
isEnabled: function() {
|
||||
var remote = opera.wiiremote.update(this.remote_id - 1);
|
||||
return (remote.isEnabled && remote.isDataValid ? remote : false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wiimote.on(evtName, fn)
|
||||
*
|
||||
* Allows you to listen for an event on a given Wiimote. Call this on an instantiated
|
||||
* Wiimote; "this" will be scoped to the Wiimote object. ;)
|
||||
*
|
||||
* @param evtName - String, a supported wii.js (DISPATCHER) event name.
|
||||
* @param fn - Function, callback function to be executed on the event firing. Will be scoped to the Wiimote.
|
||||
* @returns object or undefined - instantiated object this was called on to begin with, undefined if evtName is not supported.
|
||||
*/
|
||||
when: function(evtName, fn) {
|
||||
if(typeof Wii.DISPATCHER[evtName] !== 'undefined') {
|
||||
|
||||
/**
|
||||
* THIS IS INCREDIBLY IMPORTANT, DO NOT REMOVE THIS!.
|
||||
*
|
||||
* The Wii's browser has an (odd...?) bug wherein if you have a prototype chain
|
||||
* set up for a given object, and you default keys on the prototype chain to a blank object ("{}", for instance),
|
||||
* it will _NOT_ make this a unique object, it keeps pointers to the original object that was created by the system
|
||||
* for the first instantiated object.
|
||||
*
|
||||
* This is, needless to say, unlike just about any other JS environment and threw me for a loop for a good 6 hours.
|
||||
* This line ensures that the first time this property is ever referenced, we get a fresh _CORRECTLY ALLOCATED_ chunk
|
||||
* of memory to play with and store things in.
|
||||
*
|
||||
* Note that this also happens with Array structures, and conceivably anything else that would be using a copy-by-reference
|
||||
* technique instead of a full clone. We want an object for this case, though, so we're not doing iterations on event dispatch,
|
||||
* just a 1:1 lookup instead.
|
||||
*/
|
||||
if(this.evtsInterestedIn === undefined) this.evtsInterestedIn = {};
|
||||
|
||||
this.evtsInterestedIn[evtName] = Wii.util.bind(this, fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
66
js/src/util.js
Normal file
66
js/src/util.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* util.js
|
||||
*
|
||||
* A basic utility wrapper; anything extra that's often re-used should
|
||||
* find its way here (e.g, debuggerDiv, bind, etc).
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: wii.js
|
||||
*/
|
||||
|
||||
Wii.util = {
|
||||
/**
|
||||
* Wii.util.debug(err);
|
||||
*
|
||||
* The Wii has... such little options for debugging, but we can try and make this a bit nicer.
|
||||
* This accepts a stack trace (see example code below) and then outputs it to the screen.
|
||||
*
|
||||
* try { ... } catch(e) { Wii.util.debug(e); }
|
||||
*/
|
||||
debug: function(err) {
|
||||
if(Wii.debuggerDiv === null) {
|
||||
Wii.debuggerDiv = document.createElement('div');
|
||||
|
||||
Wii.debuggerDiv.style.cssText = [
|
||||
'width: 90%;',
|
||||
'height: 90%;',
|
||||
'padding: 10px;',
|
||||
'font-size: 26px;',
|
||||
'font-family: monospace;',
|
||||
'overflow: scroll',
|
||||
'position: absolute;',
|
||||
'top: 10px;',
|
||||
'left: 10px;',
|
||||
'color: #f9f9f9;',
|
||||
'background-color: #010101;'
|
||||
].join('');
|
||||
|
||||
document.body.appendChild(Wii.debuggerDiv);
|
||||
}
|
||||
|
||||
if(typeof err === 'string') {
|
||||
Wii.debuggerDiv.innerHTML = err;
|
||||
} else {
|
||||
var msg = '';
|
||||
for(var e in err) { msg += e + '=' + err[e] + '<br>'; }
|
||||
Wii.debuggerDiv.innerHTML = msg;
|
||||
}
|
||||
|
||||
Wii.debuggerDiv.style.display = 'block';
|
||||
},
|
||||
|
||||
/**
|
||||
* Wiimote.util.bind(bindReference, fn)
|
||||
*
|
||||
* Takes a reference (an object to scope to "this" at a later runtime) and binds it to a function (fn).
|
||||
*
|
||||
* @param bindReference - An object to set as the "this" reference for a later function call.
|
||||
* @param fn - A function to bind the "this" object for.
|
||||
* @returns fn - A new function to pass around, wherein it's all scoped as you want it.
|
||||
*/
|
||||
bind: function(bindReference, fn) {
|
||||
return function() {
|
||||
return fn.apply(bindReference, arguments);
|
||||
};
|
||||
}
|
||||
};
|
||||
224
js/src/wii.js
Normal file
224
js/src/wii.js
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/**
|
||||
* wii.js
|
||||
*
|
||||
* Provides a sane, event-based documented wrapper around the Wiimote controls
|
||||
* and API inside the Opera-based Nintendo Wii web browser.
|
||||
*
|
||||
* This is not produced by nor endorsed by Nintendo or Opera. I've written it
|
||||
* on my own because I see a device that's sitting in millions of living rooms
|
||||
* but being sorely neglected because a company couldn't get their act together
|
||||
* in regards to third party development. ;)
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: Nothing.
|
||||
*/
|
||||
|
||||
/* Wii
|
||||
*
|
||||
* Top level namespace. Contains information about the main event loop, etc.
|
||||
*/
|
||||
var Wii = {
|
||||
/**
|
||||
* A "global" reference to the Wiimotes we're currently watching. Lets us run
|
||||
* through on each loop/iteration and poll for a new status on it.
|
||||
*/
|
||||
extraRemotes: [],
|
||||
currentBrowsingRemote: null,
|
||||
setKeyListeners: false,
|
||||
debuggerDiv: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Install some basic low-level event listeners to monitor how
|
||||
* the primary wii_remote is interacting with the browser; it's treated
|
||||
* differently than the other wii_remotes, more as a "browsing" tool than
|
||||
* a controller. Doesn't mean we can't try and mend the gap...
|
||||
*/
|
||||
Wii.installKeyListeners = function() {
|
||||
document.addEventListener('mouseup', Wii.parsePrimaryWiimote, false);
|
||||
document.addEventListener('keydown', Wii.parsePrimaryWiimote, false);
|
||||
document.addEventListener('mousedown', Wii.parsePrimaryWiimote, false);
|
||||
document.addEventListener('keyup', Wii.parsePrimaryWiimote, false);
|
||||
|
||||
/**
|
||||
* Some keys, like the directional ones, get... multiple events?
|
||||
* In this case, just shut. down. everything.
|
||||
*
|
||||
* ...and let the programmer deal with it.
|
||||
*/
|
||||
document.addEventListener('keypress', Wii.parsePrimaryWiimote, false);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.listen()
|
||||
*
|
||||
* The main game loop. This must stay very performant; try to keep things as absolutely
|
||||
* low-level as possible here.
|
||||
*/
|
||||
Wii.listen = function() {
|
||||
if(!Wii.setKeyListeners) Wii.setKeyListeners = Wii.installKeyListeners();
|
||||
|
||||
var i = Wii.extraRemotes.length;
|
||||
|
||||
while(i--) {
|
||||
/* Check if it's enabled; returns a kPadStatus object if so. */
|
||||
var wii_remote = Wii.extraRemotes[i],
|
||||
wii_remoteCurrStatus = wii_remote.isEnabled();
|
||||
|
||||
/* If it's enabled, huzzah, do some checks and fire appropriate events. */
|
||||
if(wii_remoteCurrStatus) {
|
||||
/**
|
||||
* Do this check here as well, as the primary wiimote might've changed...
|
||||
* Note that we don't remove it from the internal remote tracking Array; this is because
|
||||
* if the remote that _was_ the primary one comes back online, it'll take over as the
|
||||
* primary one again as it's the lowest ID in terms of all remotes. This check here will
|
||||
* ensure that whatever remote is the current primary one will default to using other
|
||||
* dispatched events instead of bitwise checks, but should all default back if another one
|
||||
* comes online.
|
||||
*/
|
||||
if(wii_remoteCurrStatus.isBrowsing()) {
|
||||
Wii.currentBrowsingRemote = wii_remote;
|
||||
} else {
|
||||
for(var evt in wii_remote.evtsInterestedIn) {
|
||||
var evtHappened = Wii.DISPATCHER[evt](wii_remote, wii_remoteCurrStatus);
|
||||
if(evtHappened) wii_remote.evtsInterestedIn[evt](wii_remote, wii_remoteCurrStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a better choice than working with intervals; it keeps the amount of "spasm" responses
|
||||
* that one would normally get on the Wii to a bare minimum. This is due to how the two types of
|
||||
* timers in JS work - intervals will queue up no matter what, and if there's a backlog, rapidly
|
||||
* fire through all of them. Timeouts are guaranteed to always have their delay, even though at points
|
||||
* it may end up being more (or less) than 100ms.
|
||||
*
|
||||
* Note that this is set to 100ms - any lower, and the Wii becomes very unresponsive for some reason. The
|
||||
* web browser is... odd, not sure what the deal is. 100ms should be enough for most cases though...
|
||||
*/
|
||||
return setTimeout(Wii.listen, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.parsePrimaryWiimote(e)
|
||||
*
|
||||
* The Wii browser environment is... quite oddball at times. You see, the
|
||||
* primary Wii remote is treated differently than the other Wiimotes; it uses
|
||||
* browser-based events (keydown, mouseup, etc) to communicate which buttons have
|
||||
* been pressed.
|
||||
*
|
||||
* The "primary" Wiimote can also change at any given time (loss of battery in the main, etc).
|
||||
*
|
||||
* Luckily, it's not -impossible- to catch this internally. Our main library event loop catches
|
||||
* if a given Wiimote is marked as the primary one, and will not attempt bitwise operations on it,
|
||||
* merely wait for standard DOM events to trickle up and handle firing them appropriately.
|
||||
*
|
||||
* ...ugh.
|
||||
*
|
||||
* This method is a callback for any DOM-event listeners; accepts an event as its argument.
|
||||
*/
|
||||
Wii.parsePrimaryWiimote = function(e) {
|
||||
/* Cancel whatever the default was, because we're going to try and normalize everything. */
|
||||
e.preventDefault();
|
||||
|
||||
var wii_remote = Wii.currentBrowsingRemote,
|
||||
wii_remoteCurrStatus = wii_remote.isEnabled(),
|
||||
buttonPressed = Wii.PRIMARY_CONTROLLER_DISPATCHER[wii_remote.opts.horizontal ? 'horizontal' : 'vertical'][e.keyCode];
|
||||
|
||||
/**
|
||||
* Filter down and figure out which "event" we're really looking at based on code
|
||||
* matchups; this gets messy pretty quickly...
|
||||
*/
|
||||
if(typeof buttonPressed !== 'undefined' && wii_remote.evtsInterestedIn[buttonPressed] !== 'undefined') {
|
||||
wii_remote.evtsInterestedIn[buttonPressed](wii_remote, wii_remoteCurrStatus);
|
||||
}
|
||||
|
||||
/* Doing this in conjunction with preventDefault() halts an odd clicking bug or two. */
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.PRIMARY_CONTROLLER_DISPATCHER
|
||||
*
|
||||
* In order to keep things as performant as possible, we want DOM events (for the primary controller)
|
||||
* to also be a 1:1 hash map lookup. This is PRIMARILY for the primary ("browsing") controller; all other
|
||||
* controllers get their operations routed through the DISPATCHER below.
|
||||
*/
|
||||
Wii.PRIMARY_CONTROLLER_DISPATCHER = {
|
||||
vertical: {
|
||||
0: 'pressed_a',
|
||||
13: 'pressed_a', /* Older versions of the Wii Browser...? */
|
||||
170: 'pressed_minus',
|
||||
171: 'pressed_b',
|
||||
172: 'pressed_1',
|
||||
173: 'pressed_2',
|
||||
174: 'pressed_plus',
|
||||
175: 'pressed_up',
|
||||
176: 'pressed_down',
|
||||
177: 'pressed_right',
|
||||
178: 'pressed_left'
|
||||
},
|
||||
horizontal: {
|
||||
0: 'pressed_a',
|
||||
13: 'pressed_a', /* Older versions of the Wii Browser...? */
|
||||
170: 'pressed_minus',
|
||||
171: 'pressed_b',
|
||||
172: 'pressed_1',
|
||||
173: 'pressed_2',
|
||||
174: 'pressed_plus',
|
||||
175: 'pressed_left',
|
||||
176: 'pressed_right',
|
||||
177: 'pressed_up',
|
||||
178: 'pressed_down'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.DISPATCHER
|
||||
*
|
||||
* A table of the supported events that we watch for in our game loop, then fire off for respective
|
||||
* Wiimotes. Each index is a function that does a check and returns true or false.
|
||||
*
|
||||
* Many of these functions use bitwise comparisons. Read up on it if you're not familiar. Note that
|
||||
* we also take into account the orientation of the device here!
|
||||
*/
|
||||
Wii.DISPATCHER = {
|
||||
/**
|
||||
* These functions depend on whether or not the controller is meant to be in horizontal mode
|
||||
* or not. Quite... different.
|
||||
*/
|
||||
'pressed_up': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 2;
|
||||
return wii_remoteStatus.hold & 8;
|
||||
},
|
||||
'pressed_right': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 4;
|
||||
return wii_remoteStatus.hold & 2;
|
||||
},
|
||||
'pressed_down': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 1;
|
||||
return wii_remoteStatus.hold & 4;
|
||||
},
|
||||
'pressed_left': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 8;
|
||||
return wii_remoteStatus.hold & 1;
|
||||
},
|
||||
|
||||
'pressed_plus': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 16; },
|
||||
'pressed_minus': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 4096; },
|
||||
'pressed_2': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 256; },
|
||||
'pressed_1': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 512; },
|
||||
'pressed_b': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 1024; },
|
||||
'pressed_a': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 2048; },
|
||||
|
||||
/**
|
||||
* I'm keeping these noted here for legacy reasons, but by and large it's just not even
|
||||
* worth trying to use the Nunchuk with anything in the browser; the primary controller
|
||||
* can never read them, so there's a large chunk of functionality missing...
|
||||
*/
|
||||
'pressed_z': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 8192; },
|
||||
'pressed_c': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 16384; }
|
||||
};
|
||||
437
js/wii.js
Normal file
437
js/wii.js
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
/**
|
||||
* core.js
|
||||
*
|
||||
* This is the main template file for bringing together
|
||||
* the various libraries that power this entire little eco-system.
|
||||
*
|
||||
* Building the releases requires Python (2.5+); simply run...
|
||||
*
|
||||
* python build.py
|
||||
*
|
||||
* ...from the /js/ directory.
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: Nothing, top-level file.
|
||||
*/
|
||||
|
||||
;(function(running_inside_wii_browser) {
|
||||
/**
|
||||
* If we're not running inside the Nintendo Wii browser, bail out.
|
||||
* In the future, branch here for touch-enabled devices...?
|
||||
*/
|
||||
if(!running_inside_wii_browser) return false;
|
||||
|
||||
/**
|
||||
* wii.js
|
||||
*
|
||||
* Provides a sane, event-based documented wrapper around the Wiimote controls
|
||||
* and API inside the Opera-based Nintendo Wii web browser.
|
||||
*
|
||||
* This is not produced by nor endorsed by Nintendo or Opera. I've written it
|
||||
* on my own because I see a device that's sitting in millions of living rooms
|
||||
* but being sorely neglected because a company couldn't get their act together
|
||||
* in regards to third party development. ;)
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: Nothing.
|
||||
*/
|
||||
|
||||
/* Wii
|
||||
*
|
||||
* Top level namespace. Contains information about the main event loop, etc.
|
||||
*/
|
||||
var Wii = {
|
||||
/**
|
||||
* A "global" reference to the Wiimotes we're currently watching. Lets us run
|
||||
* through on each loop/iteration and poll for a new status on it.
|
||||
*/
|
||||
extraRemotes: [],
|
||||
currentBrowsingRemote: null,
|
||||
setKeyListeners: false,
|
||||
debuggerDiv: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Install some basic low-level event listeners to monitor how
|
||||
* the primary wii_remote is interacting with the browser; it's treated
|
||||
* differently than the other wii_remotes, more as a "browsing" tool than
|
||||
* a controller. Doesn't mean we can't try and mend the gap...
|
||||
*/
|
||||
Wii.installKeyListeners = function() {
|
||||
document.addEventListener('mouseup', Wii.parsePrimaryWiimote, false);
|
||||
document.addEventListener('keydown', Wii.parsePrimaryWiimote, false);
|
||||
document.addEventListener('mousedown', Wii.parsePrimaryWiimote, false);
|
||||
document.addEventListener('keyup', Wii.parsePrimaryWiimote, false);
|
||||
|
||||
/**
|
||||
* Some keys, like the directional ones, get... multiple events?
|
||||
* In this case, just shut. down. everything.
|
||||
*
|
||||
* ...and let the programmer deal with it.
|
||||
*/
|
||||
document.addEventListener('keypress', Wii.parsePrimaryWiimote, false);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.listen()
|
||||
*
|
||||
* The main game loop. This must stay very performant; try to keep things as absolutely
|
||||
* low-level as possible here.
|
||||
*/
|
||||
Wii.listen = function() {
|
||||
if(!Wii.setKeyListeners) Wii.setKeyListeners = Wii.installKeyListeners();
|
||||
|
||||
var i = Wii.extraRemotes.length;
|
||||
|
||||
while(i--) {
|
||||
/* Check if it's enabled; returns a kPadStatus object if so. */
|
||||
var wii_remote = Wii.extraRemotes[i],
|
||||
wii_remoteCurrStatus = wii_remote.isEnabled();
|
||||
|
||||
/* If it's enabled, huzzah, do some checks and fire appropriate events. */
|
||||
if(wii_remoteCurrStatus) {
|
||||
/**
|
||||
* Do this check here as well, as the primary wiimote might've changed...
|
||||
* Note that we don't remove it from the internal remote tracking Array; this is because
|
||||
* if the remote that _was_ the primary one comes back online, it'll take over as the
|
||||
* primary one again as it's the lowest ID in terms of all remotes. This check here will
|
||||
* ensure that whatever remote is the current primary one will default to using other
|
||||
* dispatched events instead of bitwise checks, but should all default back if another one
|
||||
* comes online.
|
||||
*/
|
||||
if(wii_remoteCurrStatus.isBrowsing()) {
|
||||
Wii.currentBrowsingRemote = wii_remote;
|
||||
} else {
|
||||
for(var evt in wii_remote.evtsInterestedIn) {
|
||||
var evtHappened = Wii.DISPATCHER[evt](wii_remote, wii_remoteCurrStatus);
|
||||
if(evtHappened) wii_remote.evtsInterestedIn[evt](wii_remote, wii_remoteCurrStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a better choice than working with intervals; it keeps the amount of "spasm" responses
|
||||
* that one would normally get on the Wii to a bare minimum. This is due to how the two types of
|
||||
* timers in JS work - intervals will queue up no matter what, and if there's a backlog, rapidly
|
||||
* fire through all of them. Timeouts are guaranteed to always have their delay, even though at points
|
||||
* it may end up being more (or less) than 100ms.
|
||||
*
|
||||
* Note that this is set to 100ms - any lower, and the Wii becomes very unresponsive for some reason. The
|
||||
* web browser is... odd, not sure what the deal is. 100ms should be enough for most cases though...
|
||||
*/
|
||||
return setTimeout(Wii.listen, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.parsePrimaryWiimote(e)
|
||||
*
|
||||
* The Wii browser environment is... quite oddball at times. You see, the
|
||||
* primary Wii remote is treated differently than the other Wiimotes; it uses
|
||||
* browser-based events (keydown, mouseup, etc) to communicate which buttons have
|
||||
* been pressed.
|
||||
*
|
||||
* The "primary" Wiimote can also change at any given time (loss of battery in the main, etc).
|
||||
*
|
||||
* Luckily, it's not -impossible- to catch this internally. Our main library event loop catches
|
||||
* if a given Wiimote is marked as the primary one, and will not attempt bitwise operations on it,
|
||||
* merely wait for standard DOM events to trickle up and handle firing them appropriately.
|
||||
*
|
||||
* ...ugh.
|
||||
*
|
||||
* This method is a callback for any DOM-event listeners; accepts an event as its argument.
|
||||
*/
|
||||
Wii.parsePrimaryWiimote = function(e) {
|
||||
/* Cancel whatever the default was, because we're going to try and normalize everything. */
|
||||
e.preventDefault();
|
||||
|
||||
var wii_remote = Wii.currentBrowsingRemote,
|
||||
wii_remoteCurrStatus = wii_remote.isEnabled(),
|
||||
buttonPressed = Wii.PRIMARY_CONTROLLER_DISPATCHER[wii_remote.opts.horizontal ? 'horizontal' : 'vertical'][e.keyCode];
|
||||
|
||||
/**
|
||||
* Filter down and figure out which "event" we're really looking at based on code
|
||||
* matchups; this gets messy pretty quickly...
|
||||
*/
|
||||
if(typeof buttonPressed !== 'undefined' && wii_remote.evtsInterestedIn[buttonPressed] !== 'undefined') {
|
||||
wii_remote.evtsInterestedIn[buttonPressed](wii_remote, wii_remoteCurrStatus);
|
||||
}
|
||||
|
||||
/* Doing this in conjunction with preventDefault() halts an odd clicking bug or two. */
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.PRIMARY_CONTROLLER_DISPATCHER
|
||||
*
|
||||
* In order to keep things as performant as possible, we want DOM events (for the primary controller)
|
||||
* to also be a 1:1 hash map lookup. This is PRIMARILY for the primary ("browsing") controller; all other
|
||||
* controllers get their operations routed through the DISPATCHER below.
|
||||
*/
|
||||
Wii.PRIMARY_CONTROLLER_DISPATCHER = {
|
||||
vertical: {
|
||||
0: 'pressed_a',
|
||||
13: 'pressed_a', /* Older versions of the Wii Browser...? */
|
||||
170: 'pressed_minus',
|
||||
171: 'pressed_b',
|
||||
172: 'pressed_1',
|
||||
173: 'pressed_2',
|
||||
174: 'pressed_plus',
|
||||
175: 'pressed_up',
|
||||
176: 'pressed_down',
|
||||
177: 'pressed_right',
|
||||
178: 'pressed_left'
|
||||
},
|
||||
horizontal: {
|
||||
0: 'pressed_a',
|
||||
13: 'pressed_a', /* Older versions of the Wii Browser...? */
|
||||
170: 'pressed_minus',
|
||||
171: 'pressed_b',
|
||||
172: 'pressed_1',
|
||||
173: 'pressed_2',
|
||||
174: 'pressed_plus',
|
||||
175: 'pressed_left',
|
||||
176: 'pressed_right',
|
||||
177: 'pressed_up',
|
||||
178: 'pressed_down'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wii.DISPATCHER
|
||||
*
|
||||
* A table of the supported events that we watch for in our game loop, then fire off for respective
|
||||
* Wiimotes. Each index is a function that does a check and returns true or false.
|
||||
*
|
||||
* Many of these functions use bitwise comparisons. Read up on it if you're not familiar. Note that
|
||||
* we also take into account the orientation of the device here!
|
||||
*/
|
||||
Wii.DISPATCHER = {
|
||||
/**
|
||||
* These functions depend on whether or not the controller is meant to be in horizontal mode
|
||||
* or not. Quite... different.
|
||||
*/
|
||||
'pressed_up': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 2;
|
||||
return wii_remoteStatus.hold & 8;
|
||||
},
|
||||
'pressed_right': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 4;
|
||||
return wii_remoteStatus.hold & 2;
|
||||
},
|
||||
'pressed_down': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 1;
|
||||
return wii_remoteStatus.hold & 4;
|
||||
},
|
||||
'pressed_left': function(wii_remote, wii_remoteStatus) {
|
||||
if(wii_remote.opts.horizontal) return wii_remoteStatus.hold & 8;
|
||||
return wii_remoteStatus.hold & 1;
|
||||
},
|
||||
|
||||
'pressed_plus': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 16; },
|
||||
'pressed_minus': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 4096; },
|
||||
'pressed_2': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 256; },
|
||||
'pressed_1': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 512; },
|
||||
'pressed_b': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 1024; },
|
||||
'pressed_a': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 2048; },
|
||||
|
||||
/**
|
||||
* I'm keeping these noted here for legacy reasons, but by and large it's just not even
|
||||
* worth trying to use the Nunchuk with anything in the browser; the primary controller
|
||||
* can never read them, so there's a large chunk of functionality missing...
|
||||
*/
|
||||
'pressed_z': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 8192; },
|
||||
'pressed_c': function(wii_remote, wii_remoteStatus) { return wii_remoteStatus.hold & 16384; }
|
||||
};
|
||||
/**
|
||||
* util.js
|
||||
*
|
||||
* A basic utility wrapper; anything extra that's often re-used should
|
||||
* find its way here (e.g, debuggerDiv, bind, etc).
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: wii.js
|
||||
*/
|
||||
|
||||
Wii.util = {
|
||||
/**
|
||||
* Wii.util.debug(err);
|
||||
*
|
||||
* The Wii has... such little options for debugging, but we can try and make this a bit nicer.
|
||||
* This accepts a stack trace (see example code below) and then outputs it to the screen.
|
||||
*
|
||||
* try { ... } catch(e) { Wii.util.debug(e); }
|
||||
*/
|
||||
debug: function(err) {
|
||||
if(Wii.debuggerDiv === null) {
|
||||
Wii.debuggerDiv = document.createElement('div');
|
||||
|
||||
Wii.debuggerDiv.style.cssText = [
|
||||
'width: 90%;',
|
||||
'height: 90%;',
|
||||
'padding: 10px;',
|
||||
'font-size: 26px;',
|
||||
'font-family: monospace;',
|
||||
'overflow: scroll',
|
||||
'position: absolute;',
|
||||
'top: 10px;',
|
||||
'left: 10px;',
|
||||
'color: #f9f9f9;',
|
||||
'background-color: #010101;'
|
||||
].join('');
|
||||
|
||||
document.body.appendChild(Wii.debuggerDiv);
|
||||
}
|
||||
|
||||
if(typeof err === 'string') {
|
||||
Wii.debuggerDiv.innerHTML = err;
|
||||
} else {
|
||||
var msg = '';
|
||||
for(var e in err) { msg += e + '=' + err[e] + '<br>'; }
|
||||
Wii.debuggerDiv.innerHTML = msg;
|
||||
}
|
||||
|
||||
Wii.debuggerDiv.style.display = 'block';
|
||||
},
|
||||
|
||||
/**
|
||||
* Wiimote.util.bind(bindReference, fn)
|
||||
*
|
||||
* Takes a reference (an object to scope to "this" at a later runtime) and binds it to a function (fn).
|
||||
*
|
||||
* @param bindReference - An object to set as the "this" reference for a later function call.
|
||||
* @param fn - A function to bind the "this" object for.
|
||||
* @returns fn - A new function to pass around, wherein it's all scoped as you want it.
|
||||
*/
|
||||
bind: function(bindReference, fn) {
|
||||
return function() {
|
||||
return fn.apply(bindReference, arguments);
|
||||
};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* remote.js
|
||||
*
|
||||
* Handles the subscribing to events portion of a Wii remote. It's best to think of this
|
||||
* as a "request" object; it asks to be notified of events, and the actual events are
|
||||
* dispatched from the main wii.js file.
|
||||
*
|
||||
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||
* @Requires: wii.js, util.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* var wii_remote = new Wii.Remote(1, {...});
|
||||
*
|
||||
* Instantiates a Wii Remote object. Events can be set on each of these objects,
|
||||
* and the internal game loop will fire events based on the properties subscribed
|
||||
* to here.
|
||||
*
|
||||
* @param remote_id - Number, required. 1 - 4, dictates which Wiimote this object
|
||||
* relates to.
|
||||
* @param opts - Object, optional. Allows you to override internal settings and such,
|
||||
* should you want different behavior.
|
||||
* @returns Wii.Remote instance.
|
||||
*/
|
||||
Wii.Remote = function(remote_id, opts) {
|
||||
this.remote_id = remote_id;
|
||||
this.opts = opts;
|
||||
|
||||
/**
|
||||
* If this is the "main" wii_remote, then the bitwise checks will fail
|
||||
* because it's treated more as a "browsing" device. For these events,
|
||||
* we'll just store the current wii_remote that's denoted as the "browsing"
|
||||
* device and let the normal event/key delegation take care of things.
|
||||
*
|
||||
* The rest of the wii_remotes will go through the DISPATCHER checks that
|
||||
* they've subscribed to.
|
||||
*/
|
||||
var startupStatus = this.isEnabled();
|
||||
if(startupStatus) {
|
||||
if(!startupStatus.isBrowsing) {
|
||||
Wii.extraRemotes.push(this);
|
||||
} else {
|
||||
Wii.currentBrowsingRemote = this;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Wii.Remote.prototype = {
|
||||
opts: {
|
||||
/**
|
||||
* We default the controller to be in the vertical orientation; if
|
||||
* it's overridden as "horizontal" (false), we'll catch it for the different key
|
||||
* events and fire accordingly (e.g, the "up" button is different depending on
|
||||
* how the player is holding the controller).
|
||||
*/
|
||||
horizontal: false
|
||||
},
|
||||
|
||||
/**
|
||||
* A hash of events that this Wii remote is interested in. Each
|
||||
* entry should be a key (evtName) with a value (response).
|
||||
* "evtName" is the event name that corresponds with boolean functions
|
||||
* in the DISPATCHER above, and the response is a remote-bound function
|
||||
* to call on that event.
|
||||
*/
|
||||
evtsInterestedIn: undefined,
|
||||
|
||||
/**
|
||||
* Wiimote.isEnabled()
|
||||
*
|
||||
* Determines the status of the wii_remote this object is curious about. Will return
|
||||
* an updated kPadStatus object if so, false if it's not responding or the data
|
||||
* is sent back as "invalid". This makes it so we don't bother sending events
|
||||
* where they're not applicable.
|
||||
*
|
||||
* @returns object or boolean.
|
||||
*/
|
||||
isEnabled: function() {
|
||||
var remote = opera.wiiremote.update(this.remote_id - 1);
|
||||
return (remote.isEnabled && remote.isDataValid ? remote : false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wiimote.on(evtName, fn)
|
||||
*
|
||||
* Allows you to listen for an event on a given Wiimote. Call this on an instantiated
|
||||
* Wiimote; "this" will be scoped to the Wiimote object. ;)
|
||||
*
|
||||
* @param evtName - String, a supported wii.js (DISPATCHER) event name.
|
||||
* @param fn - Function, callback function to be executed on the event firing. Will be scoped to the Wiimote.
|
||||
* @returns object or undefined - instantiated object this was called on to begin with, undefined if evtName is not supported.
|
||||
*/
|
||||
when: function(evtName, fn) {
|
||||
if(typeof Wii.DISPATCHER[evtName] !== 'undefined') {
|
||||
|
||||
/**
|
||||
* THIS IS INCREDIBLY IMPORTANT, DO NOT REMOVE THIS!.
|
||||
*
|
||||
* The Wii's browser has an (odd...?) bug wherein if you have a prototype chain
|
||||
* set up for a given object, and you default keys on the prototype chain to a blank object ("{}", for instance),
|
||||
* it will _NOT_ make this a unique object, it keeps pointers to the original object that was created by the system
|
||||
* for the first instantiated object.
|
||||
*
|
||||
* This is, needless to say, unlike just about any other JS environment and threw me for a loop for a good 6 hours.
|
||||
* This line ensures that the first time this property is ever referenced, we get a fresh _CORRECTLY ALLOCATED_ chunk
|
||||
* of memory to play with and store things in.
|
||||
*
|
||||
* Note that this also happens with Array structures, and conceivably anything else that would be using a copy-by-reference
|
||||
* technique instead of a full clone. We want an object for this case, though, so we're not doing iterations on event dispatch,
|
||||
* just a 1:1 lookup instead.
|
||||
*/
|
||||
if(this.evtsInterestedIn === undefined) this.evtsInterestedIn = {};
|
||||
|
||||
this.evtsInterestedIn[evtName] = Wii.util.bind(this, fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
window.Wii = Wii;
|
||||
})(window.opera && opera.wiiremote);
|
||||
1
js/wii.min.js
vendored
Normal file
1
js/wii.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
(function(a){if(!a){return false}var b={extraRemotes:[],currentBrowsingRemote:null,setKeyListeners:false,debuggerDiv:null};b.installKeyListeners=function(){document.addEventListener("mouseup",b.parsePrimaryWiimote,false);document.addEventListener("keydown",b.parsePrimaryWiimote,false);document.addEventListener("mousedown",b.parsePrimaryWiimote,false);document.addEventListener("keyup",b.parsePrimaryWiimote,false);document.addEventListener("keypress",b.parsePrimaryWiimote,false);return true};b.listen=function(){if(!b.setKeyListeners){b.setKeyListeners=b.installKeyListeners()}var e=b.extraRemotes.length;while(e--){var g=b.extraRemotes[e],f=g.isEnabled();if(f){if(f.isBrowsing()){b.currentBrowsingRemote=g}else{for(var c in g.evtsInterestedIn){var d=b.DISPATCHER[c](g,f);if(d){g.evtsInterestedIn[c](g,f)}}}}}return setTimeout(b.listen,100)};b.parsePrimaryWiimote=function(g){g.preventDefault();var f=b.currentBrowsingRemote,d=f.isEnabled(),c=b.PRIMARY_CONTROLLER_DISPATCHER[f.opts.horizontal?"horizontal":"vertical"][g.keyCode];if(typeof c!=="undefined"&&f.evtsInterestedIn[c]!=="undefined"){f.evtsInterestedIn[c](f,d)}return false};b.PRIMARY_CONTROLLER_DISPATCHER={vertical:{0:"pressed_a",13:"pressed_a",170:"pressed_minus",171:"pressed_b",172:"pressed_1",173:"pressed_2",174:"pressed_plus",175:"pressed_up",176:"pressed_down",177:"pressed_right",178:"pressed_left"},horizontal:{0:"pressed_a",13:"pressed_a",170:"pressed_minus",171:"pressed_b",172:"pressed_1",173:"pressed_2",174:"pressed_plus",175:"pressed_left",176:"pressed_right",177:"pressed_up",178:"pressed_down"}};b.DISPATCHER={pressed_up:function(d,c){if(d.opts.horizontal){return c.hold&2}return c.hold&8},pressed_right:function(d,c){if(d.opts.horizontal){return c.hold&4}return c.hold&2},pressed_down:function(d,c){if(d.opts.horizontal){return c.hold&1}return c.hold&4},pressed_left:function(d,c){if(d.opts.horizontal){return c.hold&8}return c.hold&1},pressed_plus:function(d,c){return c.hold&16},pressed_minus:function(d,c){return c.hold&4096},pressed_2:function(d,c){return c.hold&256},pressed_1:function(d,c){return c.hold&512},pressed_b:function(d,c){return c.hold&1024},pressed_a:function(d,c){return c.hold&2048},pressed_z:function(d,c){return c.hold&8192},pressed_c:function(d,c){return c.hold&16384}};b.util={debug:function(c){if(b.debuggerDiv===null){b.debuggerDiv=document.createElement("div");b.debuggerDiv.style.cssText=["width: 90%;","height: 90%;","padding: 10px;","font-size: 26px;","font-family: monospace;","overflow: scroll","position: absolute;","top: 10px;","left: 10px;","color: #f9f9f9;","background-color: #010101;"].join("");document.body.appendChild(b.debuggerDiv)}if(typeof c==="string"){b.debuggerDiv.innerHTML=c}else{var f="";for(var d in c){f+=d+"="+c[d]+"<br>"}b.debuggerDiv.innerHTML=f}b.debuggerDiv.style.display="block"},bind:function(c,d){return function(){return d.apply(c,arguments)}}};b.Remote=function(e,d){this.remote_id=e;this.opts=d;var c=this.isEnabled();if(c){if(!c.isBrowsing){b.extraRemotes.push(this)}else{b.currentBrowsingRemote=this}}};b.Remote.prototype={opts:{horizontal:false},evtsInterestedIn:undefined,isEnabled:function(){var c=opera.wiiremote.update(this.remote_id-1);return(c.isEnabled&&c.isDataValid?c:false)},when:function(d,c){if(typeof b.DISPATCHER[d]!=="undefined"){if(this.evtsInterestedIn===undefined){this.evtsInterestedIn={}}this.evtsInterestedIn[d]=b.util.bind(this,c);return this}return undefined}};window.Wii=b})(window.opera&&opera.wiiremote);
|
||||
104
readme.md
Normal file
104
readme.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
wii-js
|
||||
==============================================================================================
|
||||
The Nintendo Wii is an entertainment system with an utterly _massive_ install base, and when
|
||||
you couple it with the fact that it's got a web browser (mostly) built in, there's a lot of
|
||||
potential for third party development. Sadly, few have opted to do any sort of development for
|
||||
it. While it doesn't help that Nintendo pretty much dropped the ball on this opportunity, the
|
||||
experience of browsing the web on the Wii isn't actually that compelling to begin with.
|
||||
|
||||
That said, I think this can serve one other purpose: it's an ideal environment to teach children
|
||||
how to program! I created this library to sanitize Wii interaction with webpages in the browser,
|
||||
as it's notoriously crippled. It aims to offer a solid, documented, performant API that's easy to
|
||||
understand and pick up. With this library, you can have up to 4 Wii-motes interacting with your
|
||||
webpage at once, a dynamic not found in other web browsing mediums.
|
||||
|
||||
Questions, comments, criticism and praise can be directed to me at the following outlets:
|
||||
|
||||
- You can email me at **ryan [at] venodesigns (dot) net**.
|
||||
- You can hit me up on Twitter: [@ryanmcgrath](http://twitter.com/ryanmcgrath/)
|
||||
- Contact me through **[my website](http://venodesigns.net)**
|
||||
- **Technical issues can be filed on the [wii-js GitHub Issues Tracker](https://github.com/ryanmcgrath/wii-js/issues)**
|
||||
|
||||
Example Usage
|
||||
----------------------------------------------------------------------------------------------
|
||||
``` javascript
|
||||
var wiimote = new Wii.Remote(1, {horizontal: true}),
|
||||
wiimote2 = new Wii.Remote(2, {horizontal: true});
|
||||
|
||||
wiimote.when('pressed_a', function() {
|
||||
alert('Wiimote #1 pressed the A Button!');
|
||||
});
|
||||
|
||||
wiimote2.when('pressed_a', function() {
|
||||
alert('Wiimote #2 pressed the A Button!');
|
||||
});
|
||||
```
|
||||
|
||||
Technical Documentation
|
||||
----------------------------------------------------------------------------------------------
|
||||
The largest issue with making interactive pages that work with the Wii has been that the API has
|
||||
been nothing short of a black hole. When you actually begin to dig in and figure out what's going on,
|
||||
it gets even uglier to see - the primary wiimote, for instance, has a totally different set of signals
|
||||
than the other three.
|
||||
|
||||
wii-js abstracts away most of these differences and/or problems, and works on a very simple event-dispatch
|
||||
system. What this means is that you create an instance of a Wii Remote, subscribe to events, and provide a
|
||||
function to get called when that event has occurred. The following syntax should explain this:
|
||||
|
||||
``` javascript
|
||||
wiimote.when('event_name_here', function() { /* My callback function */ });
|
||||
```
|
||||
|
||||
When instantiating a Wii Remote instance, you can choose to have the library interpret directional pad controls
|
||||
in horizontal or vertical mode. You can change this at any point, if you want, by simply swapping the property.
|
||||
|
||||
``` javascript
|
||||
var wiimote = new Wii.Remote(1, {horizontal: true}); // Horizontal controls
|
||||
var wiimote = new Wii.Remote(1, {horizontal: false}); // Vertical controls
|
||||
|
||||
wiimote.opts.horizontal = true; // Change to horizontal scheme.
|
||||
```
|
||||
|
||||
You can listen for the following events on all controllers:
|
||||
|
||||
- **pressed_up** - The up button was pressed.
|
||||
- **pressed_down** - The down button was pressed.
|
||||
- **pressed_left** - The left button was pressed.
|
||||
- **pressed_right** - The right button was pressed.
|
||||
- **pressed_a** - The A button was pressed.
|
||||
- **pressed_1** - The 1 button was pressed. (_Note: On controller 1, this triggers a menu - work in progress..._)
|
||||
- **pressed_2** - The 2 button was pressed.
|
||||
- **pressed_plus** - The plus (+) button was pressed.
|
||||
- **pressed_minus** - The minus (-) button was pressed.
|
||||
- **roll_change** - The roll of the controller (balance) changed. You can get the current roll in radians with _"this.roll"_; positive is upright, negative is the other.
|
||||
- **distance_change** - The distance of the wiimote (in meters) from the TV/sensor bar has changed. This event isn't totally reliable, but should work for most cases.
|
||||
|
||||
You can listen for the following events on _extra controllers_, but not the primary controller.
|
||||
|
||||
- **pressed_b** - The B button was pressed.
|
||||
- **pressed_c** - The C button (on the Nunchuk) was pressed.
|
||||
- **pressed_z** - The Z button (on the Nunchuk) was pressed.
|
||||
|
||||
You can also get the following properties from any Wii Remote instance; they will return "undefined" if the remote
|
||||
isn't able to see the TV/sensor bar, so be sure to check this!
|
||||
|
||||
- **x** - The x coordinate where the Wii Remote is pointing to on the screen. Generally between 0 and 800, but can be more on wide pages.
|
||||
- **y** - The y coordinate where the Wii Remote is pointing to on the screen. Odd one; can be found as low as -48, as high as the height
|
||||
of the current webpage + toolbar height, if enabled. Tinker with this one for your purposes.
|
||||
|
||||
Why the button limitations?
|
||||
------------------------------------------------------------------------------------------------------------------
|
||||
The Nintendo Wii treats the primary controller differently than the other ones, and doesn't report any action
|
||||
from the Nunchuk, nor does it report when someone has pressed the "B" button on the primary controller (as it's used
|
||||
for scrolling a page).
|
||||
|
||||
The Wii Browser also doesn't report data for Gamecube controllers, the Classic controller, or any other accessories.
|
||||
|
||||
It's a work in progress to see what can be done about these, but it's impossible to guarantee anything will come out
|
||||
of it unless Nintendo and/or Opera can come out with something new.
|
||||
|
||||
|
||||
Licensing, etc
|
||||
-------------------------------------------------------------------------------------------------------------------
|
||||
wii-js is released under an MIT license. Just provide credit where need be if you choose to use this, it's taken quite
|
||||
a bit of my time to decipher the utterly random pieces and intricacies of this Javascript engine. ;)
|
||||
BIN
utilities/yuicompressor-2.4.2.jar
Executable file
BIN
utilities/yuicompressor-2.4.2.jar
Executable file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue