Compare commits
No commits in common. "gh-pages" and "master" have entirely different histories.
24 changed files with 1963 additions and 709 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.
|
||||||
65
build.py
Normal file
65
build.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/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',
|
||||||
|
'console',
|
||||||
|
'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)
|
||||||
1
css/arta.min.css
vendored
Normal file
1
css/arta.min.css
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pre code{display:block;padding:.5em;background:#222}pre .header,pre .profile .header *,pre .ini .title{color:#fff}pre .comment,pre .javadoc,pre .preprocessor,pre .shebang,pre .profile .summary,pre .diff,pre .pi,pre .doctype,pre .xml .tag,pre .template_comment,pre .css .rules,pre .tex .special{color:#444}pre .string,pre .symbol,pre .diff .change,pre .regexp,pre .xml .attribute,pre .xml .value,pre .smalltalk .char,pre .ini .value{color:#fc3}pre .number,pre .addition{color:#0c6}pre .built_in,pre .literal,pre .vhdl .type,pre .go .constant,pre .go .typename,pre .ini .keyword,pre .lua .title,pre .perl .variable,pre .php .variable,pre .mel .variable,pre .django .variable,pre .css .funtion,pre .smalltalk .method,pre .hexcolor,pre .important,pre .flow,pre .inheritance,pre .parser3 .variable{color:#32aaee}pre .keyword,pre .xml .tag .title,pre .css .tag,pre .css .class,pre .css .id,pre .css .pseudo,pre .css .attr_selector,pre .lisp .title,pre .winutils,pre .tex .command{color:#64a}pre .class .title,pre .ruby .constant,pre .vala .constant,pre .parent,pre .deletion,pre .template_tag,pre .css .keyword,pre .javascript .title,pre .objectivec .class .id,pre .smalltalk .class,pre .lisp .keyword,pre .apache .tag,pre .nginx .variable,pre .envvar,pre .bash .variable,pre .go .built_in,pre .vbscript .built_in,pre .lua .built_in,pre .rsl .built_in,pre .tail,pre .avrasm .label,pre .parser3 .title,pre .tex .formula,pre .tex .formula *{color:#b16}pre .yardoctag,pre .phpdoc,pre .profile .header,pre .ini .title,pre .apache .tag,pre .parser3 .title{font-weight:bold}pre .xml .javascript,pre .xml .css,pre .xml .cdata{opacity:.6}pre code,pre .javascript,pre .css,pre .xml,pre .subst,pre .diff .chunk,pre .css .value,pre .css .attribute,pre .lisp .string,pre .lisp .number,pre .tail .params,pre .container,pre .haskell *,pre .erlang *,pre .erlang_repl *{color:#aaa}
|
||||||
52
css/demo.css
Normal file
52
css/demo.css
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* 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 22px/26px helvetica, arial, sans-serif;
|
||||||
|
padding: 0px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body { overflow: hidden !important; padding: 20px; }
|
||||||
|
|
||||||
|
h1 { background-color: #42a2cc; color: #f9f9f9; font-size: 44px; padding: 10px; }
|
||||||
|
|
||||||
|
a, a:visited {
|
||||||
|
background-color: #42a2cc;
|
||||||
|
color: #f9f9f9;
|
||||||
|
padding: 3px 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover { background-color: #147ba8; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Players". ;) */
|
||||||
|
.player { position: absolute; top: 400px; width: 26px; height: 26px; text-align: center; border: 2px solid #010101; padding: 10px; }
|
||||||
|
.player span { background-color: #f9f9f9; padding: 5px 5px 4px; font-weight: bold; }
|
||||||
|
#player_1 { background-color: #ef1419; color: #ef1419; left: 500px; }
|
||||||
|
#player_2 { background-color: #13c32c; color: #13c32c; left: 580px; }
|
||||||
|
#player_3 { background-color: #e6d01a; color: #e6d01a; left: 660px; }
|
||||||
|
#player_4 { background-color: #af0bb1; color: #af0bb1; left: 740px; }
|
||||||
69
css/presentation.css
Normal file
69
css/presentation.css
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Basic CSS for server sent events demo.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 22px/26px helvetica, arial, sans-serif;
|
||||||
|
padding: 0px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden !important;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
background-color: #01acca;
|
||||||
|
padding: 20px 0;
|
||||||
|
text-indent: 20px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 { font-size: 1.5em;background-color: #e9e9e9; border-bottom: 1px solid #c9c9c9; margin: 0; padding: 15px; }
|
||||||
|
|
||||||
|
a, a:visited { color: #01acca; font-weight: bold; text-decoration: none; }
|
||||||
|
|
||||||
|
ul { margin: 10px 0 0 45px; list-style-type: disc; }
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: 1.3em;
|
||||||
|
line-height: 1.3em;
|
||||||
|
padding: 10px 0;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide {
|
||||||
|
display: none;
|
||||||
|
border: 1px solid #c9c9c9;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
margin: 30px auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #333;
|
||||||
|
color: #f9f9f9;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-family: inconsolata, monospace;
|
||||||
|
width: 92%;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 250 B |
365
index.html
365
index.html
|
|
@ -1,287 +1,108 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
<title>Wii.js Demo</title>
|
||||||
<title>Wii-js by ryanmcgrath</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 all Wii remotes (Wiimotes). Hold a Wiimote horizontal (sideways), and use
|
||||||
|
the directional pad to move a box floating in the bottom right corner around. Wiimote #1 is red, #2 is green, #3 is yellow, #4 is purple.
|
||||||
|
Have fun!
|
||||||
|
</p>
|
||||||
|
|
||||||
<link rel="stylesheet" href="stylesheets/styles.css">
|
<div id="player_1" class="player"><span>1</span></div>
|
||||||
<link rel="stylesheet" href="stylesheets/pygment_trac.css">
|
<div id="player_2" class="player"><span>2</span></div>
|
||||||
<script src="javascripts/scale.fix.js"></script>
|
<div id="player_3" class="player"><span>3</span></div>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
<div id="player_4" class="player"><span>4</span></div>
|
||||||
|
|
||||||
<!--[if lt IE 9]>
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
|
||||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
<script src="js/wii.js"></script>
|
||||||
<![endif]-->
|
<script>
|
||||||
</head>
|
var wiimote = new Wii.Remote(1, {horizontal: true}),
|
||||||
<body>
|
wiimote2 = new Wii.Remote(2, {horizontal: true}),
|
||||||
<div class="wrapper">
|
wiimote3 = new Wii.Remote(3, {horizontal: true}),
|
||||||
<header>
|
wiimote4 = new Wii.Remote(4, {horizontal: true}),
|
||||||
<h1>Wii-js</h1>
|
p1 = $('#player_1'),
|
||||||
<p>A sane, documented, (hopefully) performant event-based library for Wiimote webpage interaction.</p>
|
p2 = $('#player_2'),
|
||||||
<p class="view"><a href="https://github.com/ryanmcgrath/wii-js">View the Project on GitHub <small>ryanmcgrath/wii-js</small></a></p>
|
p3 = $('#player_3'),
|
||||||
<ul>
|
p4 = $('#player_4');
|
||||||
<li><a href="https://github.com/ryanmcgrath/wii-js/zipball/master">Download <strong>ZIP File</strong></a></li>
|
|
||||||
<li><a href="https://github.com/ryanmcgrath/wii-js/tarball/master">Download <strong>TAR Ball</strong></a></li>
|
|
||||||
<li><a href="https://github.com/ryanmcgrath/wii-js">View On <strong>GitHub</strong></a></li>
|
|
||||||
</ul>
|
|
||||||
</header>
|
|
||||||
<section>
|
|
||||||
<h1>wii-js</h1>
|
|
||||||
|
|
||||||
<p>The Nintendo Wii is an entertainment system with an utterly <em>massive</em> install base, and when
|
wiimote.when('pressed_down', function() {
|
||||||
you couple it with the fact that it's got a web browser (mostly) built in, there's a lot of
|
Wii.util.debug({'message': 'You gone and done it now boy!'});
|
||||||
potential for third party development. Sadly, few have opted to do any sort of development for
|
throw new Error('LOL');
|
||||||
it. While it doesn't help that Nintendo pretty much dropped the ball on this opportunity, the
|
p1.css({'top': p1.position().top + 50});
|
||||||
experience of browsing the web on the Wii isn't actually that compelling to begin with.</p>
|
});
|
||||||
|
|
||||||
<p>That said, I think this can serve one other purpose: it's an ideal environment to teach children
|
wiimote.when('pressed_right', function() {
|
||||||
how to program! I created this library to sanitize Wii interaction with webpages in the browser,
|
p1.css({'left': p1.position().left + 50});
|
||||||
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.</p>
|
|
||||||
|
|
||||||
<p>You can find a built source file and a <em>minified</em> source file for production use in the <strong>/js/</strong> directory.
|
wiimote.when('pressed_left', function() {
|
||||||
To play with a live example, load up the demo (_index.html_) on your own server, or feel free to use mine:</p>
|
p1.css({'left': p1.position().left - 50});
|
||||||
|
});
|
||||||
|
|
||||||
<p><strong>wii-js Demo: <a href="http://venodesigns.net/wii/">http://venodesigns.net/wii/</a></strong> </p>
|
wiimote.when('pressed_up', function() {
|
||||||
|
p1.css({'top': p1.position().top - 50});
|
||||||
|
});
|
||||||
|
|
||||||
|
wiimote2.when('pressed_down', function() {
|
||||||
|
p2.css({'top': p2.position().top + 50});
|
||||||
|
});
|
||||||
|
|
||||||
|
wiimote2.when('pressed_right', function() {
|
||||||
|
p2.css({'left': p2.position().left + 50});
|
||||||
|
});
|
||||||
|
|
||||||
<p>Working with the Wii's browser can be odd - it has moderately good support for CSS, so you're never really
|
wiimote2.when('pressed_left', function() {
|
||||||
as bad off as you'd be with a version of Internet Explorer - that said, if you're looking for a good read
|
p2.css({'left': p2.position().left - 50});
|
||||||
on what's supported, check out <strong><a href="http://www.opera.com/docs/specs/opera9/?platform=wii">this article on Opera Wii supported technologies</a></strong>.</p>
|
});
|
||||||
|
|
||||||
<p>Questions, comments, criticism and praise can be directed to me at the following outlets:</p>
|
wiimote2.when('pressed_up', function() {
|
||||||
|
p2.css({'top': p2.position().top - 50});
|
||||||
|
});
|
||||||
|
|
||||||
<ul>
|
wiimote3.when('pressed_down', function() {
|
||||||
<li>You can email me at <strong>ryan [at] venodesigns (dot) net</strong>.<br>
|
p3.css({'top': p3.position().top + 50});
|
||||||
</li>
|
});
|
||||||
<li>You can hit me up on Twitter: <strong><a href="http://twitter.com/ryanmcgrath/">@ryanmcgrath</a></strong><br>
|
|
||||||
</li>
|
wiimote3.when('pressed_right', function() {
|
||||||
<li>Contact me through <strong><a href="http://venodesigns.net">my website</a></strong><br>
|
p3.css({'left': p3.position().left + 50});
|
||||||
</li>
|
});
|
||||||
<li>
|
|
||||||
<strong>Technical issues can be filed on the <a href="https://github.com/ryanmcgrath/wii-js/issues">wii-js GitHub Issues Tracker</a></strong><br>
|
|
||||||
</li>
|
|
||||||
</ul><h2>Example Usage</h2>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="kd">var</span> <span class="nx">wiimote</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Wii</span><span class="p">.</span><span class="nx">Remote</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="nx">horizontal</span><span class="o">:</span> <span class="kc">true</span><span class="p">}),</span>
|
wiimote3.when('pressed_left', function() {
|
||||||
<span class="nx">wiimote2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Wii</span><span class="p">.</span><span class="nx">Remote</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="p">{</span><span class="nx">horizontal</span><span class="o">:</span> <span class="kc">true</span><span class="p">});</span>
|
p3.css({'left': p3.position().left - 50});
|
||||||
|
});
|
||||||
|
|
||||||
<span class="nx">wiimote</span><span class="p">.</span><span class="nx">when</span><span class="p">(</span><span class="s1">'pressed_a'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
|
wiimote3.when('pressed_up', function() {
|
||||||
<span class="nx">alert</span><span class="p">(</span><span class="s1">'Wiimote #1 pressed the A Button!'</span><span class="p">);</span>
|
p3.css({'top': p3.position().top - 50});
|
||||||
<span class="p">});</span>
|
});
|
||||||
|
|
||||||
<span class="nx">wiimote2</span><span class="p">.</span><span class="nx">when</span><span class="p">(</span><span class="s1">'pressed_a'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
|
wiimote4.when('pressed_down', function() {
|
||||||
<span class="nx">alert</span><span class="p">(</span><span class="s1">'Wiimote #2 pressed the A Button!'</span><span class="p">);</span>
|
p4.css({'top': p4.position().top + 50});
|
||||||
<span class="p">});</span>
|
});
|
||||||
|
|
||||||
|
wiimote4.when('pressed_right', function() {
|
||||||
|
p4.css({'left': p4.position().left + 50});
|
||||||
|
});
|
||||||
|
|
||||||
<span class="nx">Wii</span><span class="p">.</span><span class="nx">listen</span><span class="p">();</span>
|
wiimote4.when('pressed_left', function() {
|
||||||
</pre></div>
|
p4.css({'left': p4.position().left - 50});
|
||||||
|
});
|
||||||
|
|
||||||
<h2>Technical Documentation</h2>
|
wiimote4.when('pressed_up', function() {
|
||||||
|
p4.css({'top': p4.position().top - 50});
|
||||||
<p>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
|
Wii.listen({'debug': true});
|
||||||
than the other three.</p>
|
</script>
|
||||||
|
</body>
|
||||||
<p>wii-js abstracts away most of these differences and/or problems, and works on a very simple event-dispatch
|
</html>
|
||||||
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:</p>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="nx">wiimote</span><span class="p">.</span><span class="nx">when</span><span class="p">(</span><span class="s1">'event_name_here'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* My callback function */</span> <span class="p">});</span>
|
|
||||||
</pre></div>
|
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="kd">var</span> <span class="nx">wiimote</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Wii</span><span class="p">.</span><span class="nx">Remote</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="nx">horizontal</span><span class="o">:</span> <span class="kc">true</span><span class="p">});</span> <span class="c1">// Horizontal controls</span>
|
|
||||||
<span class="kd">var</span> <span class="nx">wiimote</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Wii</span><span class="p">.</span><span class="nx">Remote</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="nx">horizontal</span><span class="o">:</span> <span class="kc">false</span><span class="p">});</span> <span class="c1">// Vertical controls</span>
|
|
||||||
|
|
||||||
<span class="nx">wiimote</span><span class="p">.</span><span class="nx">opts</span><span class="p">.</span><span class="nx">horizontal</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// Change to horizontal scheme.</span>
|
|
||||||
</pre></div>
|
|
||||||
|
|
||||||
<p>The final important piece is to start the Wii-event loop; this manages the event dispatcher internally. To do this, simply...</p>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="nx">Wii</span><span class="p">.</span><span class="nx">listen</span><span class="p">();</span>
|
|
||||||
</pre></div>
|
|
||||||
|
|
||||||
<p>You can listen for the following events on all controllers:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_up</strong> - The up button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_down</strong> - The down button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_left</strong> - The left button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_right</strong> - The right button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_a</strong> - The A button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_1</strong> - The 1 button was pressed. (_Note: On controller 1, this triggers a menu - work in progress..._)<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_2</strong> - The 2 button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_plus</strong> - The plus (+) button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_minus</strong> - The minus (-) button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>roll_change</strong> - The roll of the controller (balance) changed. You can get the current roll in radians with <em>"this.roll"</em>; positive is upright, negative is the other.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>distance_change</strong> - 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.<br>
|
|
||||||
</li>
|
|
||||||
</ul><p>You can listen for the following events on <em>extra controllers</em>, but not the primary controller.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_b</strong> - The B button was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_c</strong> - The C button (on the Nunchuk) was pressed.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>pressed_z</strong> - The Z button (on the Nunchuk) was pressed.<br>
|
|
||||||
</li>
|
|
||||||
</ul><p>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!</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>x</strong> - 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.</li>
|
|
||||||
<li>
|
|
||||||
<strong>y</strong> - 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.</li>
|
|
||||||
</ul><h2>Extra Tips and Tricks (Debugging)</h2>
|
|
||||||
|
|
||||||
<p>One semi-useful trick to point out about this library is that each of your callback functions get passed two
|
|
||||||
arguments by default - a reference to the Wiimote you're working with, and the raw Wiimote status object that the
|
|
||||||
Wii reports back to the library. You get this in a normalized fashion, instead of having to deal with the odd polling
|
|
||||||
issues present in the browser.</p>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="kd">var</span> <span class="nx">wiimote</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Wii</span><span class="p">.</span><span class="nx">Remote</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="nx">horizontal</span><span class="o">:</span> <span class="kc">false</span><span class="p">});</span>
|
|
||||||
|
|
||||||
<span class="nx">wiimote</span><span class="p">.</span><span class="nx">when</span><span class="p">(</span><span class="s1">'pressed_a'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">wii_remote</span><span class="p">,</span> <span class="nx">wii_remote_status</span><span class="p">)</span> <span class="p">{</span>
|
|
||||||
<span class="cm">/* Alert an internal confidence level provided by the Wii. */</span>
|
|
||||||
<span class="nx">alert</span><span class="p">(</span><span class="nx">wii_remote_status</span><span class="p">.</span><span class="nx">dpdValidity</span><span class="p">);</span>
|
|
||||||
<span class="p">});</span>
|
|
||||||
</pre></div>
|
|
||||||
|
|
||||||
<p>Debugging Javascript on the Wii is also nothing short of incredibly annoying, so I've made some efforts to patch this
|
|
||||||
up and make life a bit easier. My typical debugging strategy with any Wii-related code would always start with
|
|
||||||
the following. The first thing to do is set the Wii listener to run in debug mode, like so:</p>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="nx">Wii</span><span class="p">.</span><span class="nx">listen</span><span class="p">({</span><span class="nx">debug</span><span class="o">:</span> <span class="kc">true</span><span class="p">});</span>
|
|
||||||
</pre></div>
|
|
||||||
|
|
||||||
<p>With this set, you can log errors with any of the following functions. <code>error</code> can be a string or a complex object.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>console.log(error);</strong> - Tried and true, now supported.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>console.debug(error);</strong> - Same as console.log here, but syntax is supported.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>throw new Error(error);</strong> - Throw them, they'll be logged.<br>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Wii.util.debug(error);</strong> - The core function that handles logging internally.</li>
|
|
||||||
</ul><p>If the typical Wii debugging flow isn't enough for you, go aggressive with this - just be aware that you can crash
|
|
||||||
the Wii's browser if you're using try/catch all over the place, as it's not cheap in Javascript.</p>
|
|
||||||
|
|
||||||
<div class="highlight"><pre><span class="k">try</span> <span class="p">{</span>
|
|
||||||
<span class="c1">// Whatever function to execute</span>
|
|
||||||
<span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="nx">Wii</span><span class="p">.</span><span class="nx">util</span><span class="p">.</span><span class="nx">debug</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span> <span class="p">}</span>
|
|
||||||
</pre></div>
|
|
||||||
|
|
||||||
<h2>Why the button limitations?</h2>
|
|
||||||
|
|
||||||
<p>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).</p>
|
|
||||||
|
|
||||||
<p>The Wii Browser also doesn't report data for Gamecube controllers, the Classic controller, or any other accessories.</p>
|
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
<h2>Known Issues</h2>
|
|
||||||
|
|
||||||
<p><strong>Primary Wiimote is a bit more responsive than the extra 3</strong><br>
|
|
||||||
This library works by polling the status of the three extra Wii-remotes in 100ms intervals and dispatching events
|
|
||||||
based on this. Anything lower than 100ms causes the Wii to run into memory limitations, and the single-threaded
|
|
||||||
nature of the browser doesn't really help this issue.</p>
|
|
||||||
|
|
||||||
<p>The primary Wii-remote uses an odd combination of DOM-esque callbacks; due to this, it reports <em>more frequently</em> than
|
|
||||||
the other Wii-remotes. It's not a showstopper by any means, but for small games it would in theory give a weighted advantage.
|
|
||||||
I'll probably end up throttling this through the library by means of a flag, e.g "game_mode": true in the initial options.</p>
|
|
||||||
|
|
||||||
<h2>Todo List</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Build in functionality for multiple button presses at the same time (difficult to get right in this environment)</li>
|
|
||||||
<li>Determine canceling B-button/scrolling on pages ("app"/"game" style)</li>
|
|
||||||
<li>Determine feasibility of canceling out "1" press on the primary Wii-remote.</li>
|
|
||||||
</ul><h2>Building and Developing</h2>
|
|
||||||
|
|
||||||
<p>If you'd like to help with this library, you're more than welcome to. Simply fork it on GitHub, work away, then
|
|
||||||
issue me a pull request. I generally respond within 24 hours.</p>
|
|
||||||
|
|
||||||
<p>The build system here is pretty simple - edits and changes go into the /js/src/ files, and you can run</p>
|
|
||||||
|
|
||||||
<pre><code>python build.py
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>from the main directory to build a new version. The minifier here is YUI; Closure/UglifyJS are more aggressive, and
|
|
||||||
for some reason throw ridiculous issues in the Wii's browser that I've been unable to track down (and I don't have
|
|
||||||
more time to throw at it).</p>
|
|
||||||
|
|
||||||
<p>In short, the builds require Python/Java, but once you've got them all installed you should only need the command above.</p>
|
|
||||||
|
|
||||||
<h2>How is this different from...?</h2>
|
|
||||||
|
|
||||||
<p>I sadly did not find out about <a href="http://www.bolinfest.com/wii/overview.php">wii.js</a> until after I released this library;
|
|
||||||
with respect to the original author, his work only covers the primary Wii Remote and not the extra ones, nor has it
|
|
||||||
been updated in years. While his approach appears to be the same as mine (or mine the same as his), neither one
|
|
||||||
influenced the other, and they're totally separate works.</p>
|
|
||||||
|
|
||||||
<p>With the exception of wii.js, I do not know of any other (remaining) Wii interaction Javascript libraries. It's for
|
|
||||||
these reasons (and my desire for a simpler API) that I built this. ;)</p>
|
|
||||||
|
|
||||||
<h2>Licensing, etc</h2>
|
|
||||||
|
|
||||||
<p>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. ;)</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
<p>Project maintained by <a href="https://github.com/ryanmcgrath">ryanmcgrath</a></p>
|
|
||||||
<p>Hosted on GitHub Pages — Theme by <a href="https://github.com/orderedlist">orderedlist</a></p>
|
|
||||||
</footer>
|
|
||||||
<!--[if !IE]><script>fixScale(document);</script><![endif]-->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
|
||||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
try {
|
|
||||||
var pageTracker = _gat._getTracker("UA-40660943-3");
|
|
||||||
pageTracker._trackPageview();
|
|
||||||
} catch(err) {}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
fixScale = function(doc) {
|
|
||||||
|
|
||||||
var addEvent = 'addEventListener',
|
|
||||||
type = 'gesturestart',
|
|
||||||
qsa = 'querySelectorAll',
|
|
||||||
scales = [1, 1],
|
|
||||||
meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : [];
|
|
||||||
|
|
||||||
function fix() {
|
|
||||||
meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1];
|
|
||||||
doc.removeEventListener(type, fix, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((meta = meta[meta.length - 1]) && addEvent in doc) {
|
|
||||||
fix();
|
|
||||||
scales = [.25, 1.6];
|
|
||||||
doc[addEvent](type, fix, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
1
js/highlight.min.js
vendored
Normal file
1
js/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
33
js/slides.js
Normal file
33
js/slides.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* slides.js
|
||||||
|
*
|
||||||
|
* Some incredibly ugly quick code for a slides-esque experience
|
||||||
|
* on both the Wii and browsers.
|
||||||
|
*
|
||||||
|
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||||
|
* @Requires: Nothing
|
||||||
|
*/
|
||||||
|
|
||||||
|
var slides = document.getElementsByTagName('div'),
|
||||||
|
count = slides.length,
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
var sizeSlide = function sizeSlide(slide, oldIndex) {
|
||||||
|
slides[oldIndex].style.display = 'none';
|
||||||
|
slide.style.width = (window.innerWidth - 40) + 'px';
|
||||||
|
slide.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if(e.keyCode === 39 && (index + 1) < count) {
|
||||||
|
sizeSlide(slides[index + 1], index);
|
||||||
|
index = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.keyCode === 37 && (index - 1) >= 0) {
|
||||||
|
sizeSlide(slides[index - 1], index);
|
||||||
|
index = index - 1;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
sizeSlide(slides[0], 0);
|
||||||
20
js/src/console.js
Normal file
20
js/src/console.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* console.js
|
||||||
|
*
|
||||||
|
* A lightweight wrapper for the now-common "console" object in browsers.
|
||||||
|
* Really just maps calls over to the wii-js internal Wii.util.debug() call,
|
||||||
|
* but exists so that if you use this in production code for whatever reason
|
||||||
|
* it could still be used to debug on the Wii.
|
||||||
|
*
|
||||||
|
* Note that for this to work, you must be listening in debug mode!
|
||||||
|
*
|
||||||
|
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||||
|
* @Requires: wii.js, util.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(typeof window.console === 'undefined') {
|
||||||
|
window.console = {
|
||||||
|
log: Wii.util.debug,
|
||||||
|
debug: Wii.util.debug
|
||||||
|
};
|
||||||
|
}
|
||||||
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);
|
||||||
130
js/src/remote.js
Normal file
130
js/src/remote.js
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default these properties to undefined, since that's what
|
||||||
|
* the Wii returns anyway, and it's worth it to try and stay (somewhat)
|
||||||
|
* close to the core tech.
|
||||||
|
*/
|
||||||
|
this.x = undefined;
|
||||||
|
this.y = undefined;
|
||||||
|
this.roll = undefined;
|
||||||
|
this.last_known_distance_from_screen = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
92
js/src/util.js
Normal file
92
js/src/util.js
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
/**
|
||||||
|
* 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 = {
|
||||||
|
/**
|
||||||
|
* A placeholder for the original Error function, since we pretty much overwrite it to actually
|
||||||
|
* be useful to us. See "Wii.installListeners()" for more information on this.
|
||||||
|
*/
|
||||||
|
originalErrFunction: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upon first call to Wii.util.debug(), this becomes a div that we keep
|
||||||
|
* a reference to. It's primarily used for logging information to the screen
|
||||||
|
* on the Wii itself.
|
||||||
|
*/
|
||||||
|
msgNode: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.debug) return;
|
||||||
|
|
||||||
|
if(Wii.util.msgNode === null) {
|
||||||
|
Wii.util.msgNode = document.createElement('div');
|
||||||
|
|
||||||
|
Wii.util.msgNode.style.cssText = [
|
||||||
|
'min-width: 780px;',
|
||||||
|
'padding: 10px;',
|
||||||
|
'font-size: 28px;',
|
||||||
|
'line-height: 32px;',
|
||||||
|
'font-family: monospace;',
|
||||||
|
'position: absolute;',
|
||||||
|
'top: 15px;',
|
||||||
|
'left: 0;',
|
||||||
|
'color: #f9f9f9;',
|
||||||
|
'background-color: #010101;',
|
||||||
|
'border-bottom: 2px solid #42a2cc;',
|
||||||
|
'opacity: .7;',
|
||||||
|
'font-weight: bold;'
|
||||||
|
].join('');
|
||||||
|
|
||||||
|
Wii.util.msgNode.addEventListener('click', Wii.util.hideDebugger, false);
|
||||||
|
document.body.appendChild(Wii.util.msgNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof err === 'string') {
|
||||||
|
Wii.util.msgNode.innerHTML = err;
|
||||||
|
} else {
|
||||||
|
var msg = '';
|
||||||
|
for(var e in err) { msg += '<span style="color: #42a2cc; font-weight: bold;">' + e + '</span>=' + err[e] + '<br>'; }
|
||||||
|
Wii.util.msgNode.innerHTML = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wii.util.msgNode.style.display = 'block';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wii.util.hideDebugger()
|
||||||
|
*
|
||||||
|
* Keep this around so we've got an easy reference to use for proper unloading
|
||||||
|
* of event handlers once someone leaves this page.
|
||||||
|
*/
|
||||||
|
hideDebugger: function() { this.style.display = 'none'; },
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
328
js/src/wii.js
Normal file
328
js/src/wii.js
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
/**
|
||||||
|
* 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: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "global" reference to the (current) primary Wiimote. This can change if the
|
||||||
|
* primary Wiimote runs out of battery, and the library shoul transparently account
|
||||||
|
* for this.
|
||||||
|
*/
|
||||||
|
currentBrowsingRemote: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal flag for whether or not we've actually set some document event listeners.
|
||||||
|
* A little messy, sure, but not the biggest guffaw in the world.
|
||||||
|
*/
|
||||||
|
setListeners: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A global debug flag. If this is enabled/set to true, any errors raised will be thrown
|
||||||
|
* up on the screen for the Wii. Should be set when the initial .listen() request is fired.
|
||||||
|
*
|
||||||
|
* e.g, Wii.listen({debug: true});
|
||||||
|
*/
|
||||||
|
debug: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary Wiimote depends on typical DOM-esque events to communicate what actions it's
|
||||||
|
* doing, and the other three use a whole bitmask-esque scenario. This is a "global" Array of
|
||||||
|
* the events we're interested in for the primary Wiimote.
|
||||||
|
*
|
||||||
|
* We catch multiple begin/endpoints for these full event "scopes" for
|
||||||
|
* future use, as it seems like it'll probably be the most performant way
|
||||||
|
* to do rapid-quick checks for the primary Wii-mote as to multiple-button
|
||||||
|
* press scenarios (among some other things).
|
||||||
|
*/
|
||||||
|
primaryWiimoteEvts: ['mouseup', 'mousedown', 'keyup', 'keydown', 'keypress']
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wii.installListeners()
|
||||||
|
*
|
||||||
|
* 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.installListeners = function() {
|
||||||
|
for(var i = 0, j = Wii.primaryWiimoteEvts.length; i < j; i++) {
|
||||||
|
document.addEventListener(Wii.primaryWiimoteEvts[i], Wii.parsePrimaryWiimote, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since the Wii is already a fairly low-spec system, it's definitely worth
|
||||||
|
* cleaning up after ourselves if we can get around to it. This should (hopefully)
|
||||||
|
* take care of most of what we need to worry about.
|
||||||
|
*/
|
||||||
|
window.onbeforeunload = function() {
|
||||||
|
for(var i = 0, j = Wii.primaryWiimoteEvts.length; i < j; i++) {
|
||||||
|
document.removeEventListener(Wii.primaryWiimoteEvts[i], Wii.parsePrimaryWiimote, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Wii.util.msgNode) Wii.util.msgNode.removeEventListener('click', Wii.util.hideDebugger, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is all listening in debug mode, we wanna try and catch everything that could
|
||||||
|
* possibly go wrong in the stack. try/catch is very expensive for the entire program, and can
|
||||||
|
* crash the Wii pretty easily if you push it too much.
|
||||||
|
*
|
||||||
|
* What we'll do instead is just patch the seemingly un-documented window.Error function to suit our
|
||||||
|
* debugging needs, and only try/catch while in debug mode in critical places (e.g, the constant polling
|
||||||
|
* section would be... yeah, not a fun idea).
|
||||||
|
*
|
||||||
|
* With this, "throw new Error(error)" will actually hit over to what we want, and anything else (outside code)
|
||||||
|
* using it will get the on-screen Wii debug which is actually useful. That's why we bother doing this, instead
|
||||||
|
* of just calling Wii.util.debug() everywhere. ;)
|
||||||
|
*/
|
||||||
|
if(Wii.debug) {
|
||||||
|
Wii.util.originalErrFunction = window.Error;
|
||||||
|
window.Error = function() {
|
||||||
|
if(arguments.length > 0) Wii.util.debug(arguments[0]);
|
||||||
|
else Wii.util.originalErrFunction.apply(this, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(optional_opts) {
|
||||||
|
if(typeof optional_opts !== 'undefined') {
|
||||||
|
if(typeof optional_opts.debug !== 'undefined' && optional_opts.debug) Wii.debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Wii.setListeners) Wii.setListeners = Wii.installListeners();
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/* Update these on each poll, since we've got the data anyway. */
|
||||||
|
wii_remote.x = wii_remoteCurrStatus.dpdScreenX;
|
||||||
|
wii_remote.y = wii_remoteCurrStatus.dpdScreenY;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
/* Grab these first, and on every pass. */
|
||||||
|
wii_remote.x = wii_remoteCurrStatus.dpdScreenX;
|
||||||
|
wii_remote.y = wii_remoteCurrStatus.dpdScreenY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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' && typeof wii_remote.evtsInterestedIn[buttonPressed] === 'function') {
|
||||||
|
wii_remote.evtsInterestedIn[buttonPressed](wii_remote, wii_remoteCurrStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Due to the difference in how these controls are caught, we need a second set of roll/distance-changes
|
||||||
|
* run here. Luckily, we can just re-use the dispatcher functions.
|
||||||
|
*/
|
||||||
|
if(typeof wii_remote.evtsInterestedIn['roll_change'] === 'function') {
|
||||||
|
if(Wii.DISPATCHER['roll_change'](wii_remote, wii_remoteCurrStatus)) {
|
||||||
|
wii_remote.evtsInterestedIn['roll_change'](wii_remote, wii_remoteCurrStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof wii_remote.evtsInterestedIn['distance_change'] === 'function') {
|
||||||
|
if(Wii.DISPATCHER['distance_change'](wii_remote, wii_remoteCurrStatus)) {
|
||||||
|
wii_remote.evtsInterestedIn['distance_change'](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. The keys below are keyCodes.
|
||||||
|
*/
|
||||||
|
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; },
|
||||||
|
|
||||||
|
'roll_change': function(wii_remote, wii_remoteStatus) {
|
||||||
|
var roll = Math.atan2(wii_remoteStatus.dpdRollY, wii_remoteStatus.dpdRollX);
|
||||||
|
|
||||||
|
if(roll !== wii_remote.roll) {
|
||||||
|
wii_remote.roll = roll;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
'distance_change': function(wii_remote, wii_remoteStatus) {
|
||||||
|
if(wii_remoteStatus.dpdDistance !== wii_remote.last_known_distance_from_screen) {
|
||||||
|
wii_remote.last_known_distance_from_screen = wii_remoteStatus.dpdDistance;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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; }
|
||||||
|
};
|
||||||
596
js/wii.js
Normal file
596
js/wii.js
Normal file
|
|
@ -0,0 +1,596 @@
|
||||||
|
/**
|
||||||
|
* 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: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "global" reference to the (current) primary Wiimote. This can change if the
|
||||||
|
* primary Wiimote runs out of battery, and the library shoul transparently account
|
||||||
|
* for this.
|
||||||
|
*/
|
||||||
|
currentBrowsingRemote: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal flag for whether or not we've actually set some document event listeners.
|
||||||
|
* A little messy, sure, but not the biggest guffaw in the world.
|
||||||
|
*/
|
||||||
|
setListeners: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A global debug flag. If this is enabled/set to true, any errors raised will be thrown
|
||||||
|
* up on the screen for the Wii. Should be set when the initial .listen() request is fired.
|
||||||
|
*
|
||||||
|
* e.g, Wii.listen({debug: true});
|
||||||
|
*/
|
||||||
|
debug: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary Wiimote depends on typical DOM-esque events to communicate what actions it's
|
||||||
|
* doing, and the other three use a whole bitmask-esque scenario. This is a "global" Array of
|
||||||
|
* the events we're interested in for the primary Wiimote.
|
||||||
|
*
|
||||||
|
* We catch multiple begin/endpoints for these full event "scopes" for
|
||||||
|
* future use, as it seems like it'll probably be the most performant way
|
||||||
|
* to do rapid-quick checks for the primary Wii-mote as to multiple-button
|
||||||
|
* press scenarios (among some other things).
|
||||||
|
*/
|
||||||
|
primaryWiimoteEvts: ['mouseup', 'mousedown', 'keyup', 'keydown', 'keypress']
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wii.installListeners()
|
||||||
|
*
|
||||||
|
* 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.installListeners = function() {
|
||||||
|
for(var i = 0, j = Wii.primaryWiimoteEvts.length; i < j; i++) {
|
||||||
|
document.addEventListener(Wii.primaryWiimoteEvts[i], Wii.parsePrimaryWiimote, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since the Wii is already a fairly low-spec system, it's definitely worth
|
||||||
|
* cleaning up after ourselves if we can get around to it. This should (hopefully)
|
||||||
|
* take care of most of what we need to worry about.
|
||||||
|
*/
|
||||||
|
window.onbeforeunload = function() {
|
||||||
|
for(var i = 0, j = Wii.primaryWiimoteEvts.length; i < j; i++) {
|
||||||
|
document.removeEventListener(Wii.primaryWiimoteEvts[i], Wii.parsePrimaryWiimote, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Wii.util.msgNode) Wii.util.msgNode.removeEventListener('click', Wii.util.hideDebugger, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is all listening in debug mode, we wanna try and catch everything that could
|
||||||
|
* possibly go wrong in the stack. try/catch is very expensive for the entire program, and can
|
||||||
|
* crash the Wii pretty easily if you push it too much.
|
||||||
|
*
|
||||||
|
* What we'll do instead is just patch the seemingly un-documented window.Error function to suit our
|
||||||
|
* debugging needs, and only try/catch while in debug mode in critical places (e.g, the constant polling
|
||||||
|
* section would be... yeah, not a fun idea).
|
||||||
|
*
|
||||||
|
* With this, "throw new Error(error)" will actually hit over to what we want, and anything else (outside code)
|
||||||
|
* using it will get the on-screen Wii debug which is actually useful. That's why we bother doing this, instead
|
||||||
|
* of just calling Wii.util.debug() everywhere. ;)
|
||||||
|
*/
|
||||||
|
if(Wii.debug) {
|
||||||
|
Wii.util.originalErrFunction = window.Error;
|
||||||
|
window.Error = function() {
|
||||||
|
if(arguments.length > 0) Wii.util.debug(arguments[0]);
|
||||||
|
else Wii.util.originalErrFunction.apply(this, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(optional_opts) {
|
||||||
|
if(typeof optional_opts !== 'undefined') {
|
||||||
|
if(typeof optional_opts.debug !== 'undefined' && optional_opts.debug) Wii.debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Wii.setListeners) Wii.setListeners = Wii.installListeners();
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/* Update these on each poll, since we've got the data anyway. */
|
||||||
|
wii_remote.x = wii_remoteCurrStatus.dpdScreenX;
|
||||||
|
wii_remote.y = wii_remoteCurrStatus.dpdScreenY;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
/* Grab these first, and on every pass. */
|
||||||
|
wii_remote.x = wii_remoteCurrStatus.dpdScreenX;
|
||||||
|
wii_remote.y = wii_remoteCurrStatus.dpdScreenY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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' && typeof wii_remote.evtsInterestedIn[buttonPressed] === 'function') {
|
||||||
|
wii_remote.evtsInterestedIn[buttonPressed](wii_remote, wii_remoteCurrStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Due to the difference in how these controls are caught, we need a second set of roll/distance-changes
|
||||||
|
* run here. Luckily, we can just re-use the dispatcher functions.
|
||||||
|
*/
|
||||||
|
if(typeof wii_remote.evtsInterestedIn['roll_change'] === 'function') {
|
||||||
|
if(Wii.DISPATCHER['roll_change'](wii_remote, wii_remoteCurrStatus)) {
|
||||||
|
wii_remote.evtsInterestedIn['roll_change'](wii_remote, wii_remoteCurrStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof wii_remote.evtsInterestedIn['distance_change'] === 'function') {
|
||||||
|
if(Wii.DISPATCHER['distance_change'](wii_remote, wii_remoteCurrStatus)) {
|
||||||
|
wii_remote.evtsInterestedIn['distance_change'](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. The keys below are keyCodes.
|
||||||
|
*/
|
||||||
|
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; },
|
||||||
|
|
||||||
|
'roll_change': function(wii_remote, wii_remoteStatus) {
|
||||||
|
var roll = Math.atan2(wii_remoteStatus.dpdRollY, wii_remoteStatus.dpdRollX);
|
||||||
|
|
||||||
|
if(roll !== wii_remote.roll) {
|
||||||
|
wii_remote.roll = roll;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
'distance_change': function(wii_remote, wii_remoteStatus) {
|
||||||
|
if(wii_remoteStatus.dpdDistance !== wii_remote.last_known_distance_from_screen) {
|
||||||
|
wii_remote.last_known_distance_from_screen = wii_remoteStatus.dpdDistance;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = {
|
||||||
|
/**
|
||||||
|
* A placeholder for the original Error function, since we pretty much overwrite it to actually
|
||||||
|
* be useful to us. See "Wii.installListeners()" for more information on this.
|
||||||
|
*/
|
||||||
|
originalErrFunction: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upon first call to Wii.util.debug(), this becomes a div that we keep
|
||||||
|
* a reference to. It's primarily used for logging information to the screen
|
||||||
|
* on the Wii itself.
|
||||||
|
*/
|
||||||
|
msgNode: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.debug) return;
|
||||||
|
|
||||||
|
if(Wii.util.msgNode === null) {
|
||||||
|
Wii.util.msgNode = document.createElement('div');
|
||||||
|
|
||||||
|
Wii.util.msgNode.style.cssText = [
|
||||||
|
'min-width: 780px;',
|
||||||
|
'padding: 10px;',
|
||||||
|
'font-size: 28px;',
|
||||||
|
'line-height: 32px;',
|
||||||
|
'font-family: monospace;',
|
||||||
|
'position: absolute;',
|
||||||
|
'top: 15px;',
|
||||||
|
'left: 0;',
|
||||||
|
'color: #f9f9f9;',
|
||||||
|
'background-color: #010101;',
|
||||||
|
'border-bottom: 2px solid #42a2cc;',
|
||||||
|
'opacity: .7;',
|
||||||
|
'font-weight: bold;'
|
||||||
|
].join('');
|
||||||
|
|
||||||
|
Wii.util.msgNode.addEventListener('click', Wii.util.hideDebugger, false);
|
||||||
|
document.body.appendChild(Wii.util.msgNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof err === 'string') {
|
||||||
|
Wii.util.msgNode.innerHTML = err;
|
||||||
|
} else {
|
||||||
|
var msg = '';
|
||||||
|
for(var e in err) { msg += '<span style="color: #42a2cc; font-weight: bold;">' + e + '</span>=' + err[e] + '<br>'; }
|
||||||
|
Wii.util.msgNode.innerHTML = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wii.util.msgNode.style.display = 'block';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wii.util.hideDebugger()
|
||||||
|
*
|
||||||
|
* Keep this around so we've got an easy reference to use for proper unloading
|
||||||
|
* of event handlers once someone leaves this page.
|
||||||
|
*/
|
||||||
|
hideDebugger: function() { this.style.display = 'none'; },
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* console.js
|
||||||
|
*
|
||||||
|
* A lightweight wrapper for the now-common "console" object in browsers.
|
||||||
|
* Really just maps calls over to the wii-js internal Wii.util.debug() call,
|
||||||
|
* but exists so that if you use this in production code for whatever reason
|
||||||
|
* it could still be used to debug on the Wii.
|
||||||
|
*
|
||||||
|
* Note that for this to work, you must be listening in debug mode!
|
||||||
|
*
|
||||||
|
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||||
|
* @Requires: wii.js, util.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(typeof window.console === 'undefined') {
|
||||||
|
window.console = {
|
||||||
|
log: Wii.util.debug,
|
||||||
|
debug: Wii.util.debug
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default these properties to undefined, since that's what
|
||||||
|
* the Wii returns anyway, and it's worth it to try and stay (somewhat)
|
||||||
|
* close to the core tech.
|
||||||
|
*/
|
||||||
|
this.x = undefined;
|
||||||
|
this.y = undefined;
|
||||||
|
this.roll = undefined;
|
||||||
|
this.last_known_distance_from_screen = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,setListeners:false,debug:false,primaryWiimoteEvts:["mouseup","mousedown","keyup","keydown","keypress"]};b.installListeners=function(){for(var d=0,c=b.primaryWiimoteEvts.length;d<c;d++){document.addEventListener(b.primaryWiimoteEvts[d],b.parsePrimaryWiimote,false)}window.onbeforeunload=function(){for(var f=0,e=b.primaryWiimoteEvts.length;f<e;f++){document.removeEventListener(b.primaryWiimoteEvts[f],b.parsePrimaryWiimote,false)}if(b.util.msgNode){b.util.msgNode.removeEventListener("click",b.util.hideDebugger,false)}};if(b.debug){b.util.originalErrFunction=window.Error;window.Error=function(){if(arguments.length>0){b.util.debug(arguments[0])}else{b.util.originalErrFunction.apply(this,arguments)}}}return true};b.listen=function(h){if(typeof h!=="undefined"){if(typeof h.debug!=="undefined"&&h.debug){b.debug=true}}if(!b.setListeners){b.setListeners=b.installListeners()}var e=b.extraRemotes.length;while(e--){var g=b.extraRemotes[e],f=g.isEnabled();if(f){if(f.isBrowsing){b.currentBrowsingRemote=g}else{g.x=f.dpdScreenX;g.y=f.dpdScreenY;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];f.x=d.dpdScreenX;f.y=d.dpdScreenY;if(typeof c!=="undefined"&&typeof f.evtsInterestedIn[c]==="function"){f.evtsInterestedIn[c](f,d)}if(typeof f.evtsInterestedIn.roll_change==="function"){if(b.DISPATCHER.roll_change(f,d)){f.evtsInterestedIn.roll_change(f,d)}}if(typeof f.evtsInterestedIn.distance_change==="function"){if(b.DISPATCHER.distance_change(f,d)){f.evtsInterestedIn.distance_change(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},roll_change:function(e,d){var c=Math.atan2(d.dpdRollY,d.dpdRollX);if(c!==e.roll){e.roll=c;return true}return false},distance_change:function(d,c){if(c.dpdDistance!==d.last_known_distance_from_screen){d.last_known_distance_from_screen=c.dpdDistance;return true}return false},pressed_z:function(d,c){return c.hold&8192},pressed_c:function(d,c){return c.hold&16384}};b.util={originalErrFunction:null,msgNode:null,debug:function(c){if(!b.debug){return}if(b.util.msgNode===null){b.util.msgNode=document.createElement("div");b.util.msgNode.style.cssText=["min-width: 780px;","padding: 10px;","font-size: 28px;","line-height: 32px;","font-family: monospace;","position: absolute;","top: 15px;","left: 0;","color: #f9f9f9;","background-color: #010101;","border-bottom: 2px solid #42a2cc;","opacity: .7;","font-weight: bold;"].join("");b.util.msgNode.addEventListener("click",b.util.hideDebugger,false);document.body.appendChild(b.util.msgNode)}if(typeof c==="string"){b.util.msgNode.innerHTML=c}else{var f="";for(var d in c){f+='<span style="color: #42a2cc; font-weight: bold;">'+d+"</span>="+c[d]+"<br>"}b.util.msgNode.innerHTML=f}b.util.msgNode.style.display="block"},hideDebugger:function(){this.style.display="none"},bind:function(c,d){return function(){return d.apply(c,arguments)}}};if(typeof window.console==="undefined"){window.console={log:b.util.debug,debug:b.util.debug}}b.Remote=function(e,d){this.remote_id=e;this.opts=d;this.x=undefined;this.y=undefined;this.roll=undefined;this.last_known_distance_from_screen=undefined;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);
|
||||||
File diff suppressed because one or more lines are too long
106
presentation.html
Normal file
106
presentation.html
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>Introducing Wii.js</title>
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="css/presentation.css">
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="css/arta.min.css">
|
||||||
|
<style>.keyword { color: #ea1fb8 !important; }</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Wii-js</h1>
|
||||||
|
|
||||||
|
<div class="slide" id="intro">
|
||||||
|
<h2>Who I am</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Senior Engineer, myGengo</li>
|
||||||
|
<li>Author of MapRejuice, wrench-js, others</li>
|
||||||
|
<li><a href="http://github.com/ryanmcgrath">github.com/ryanmcgrath</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slide" id="why">
|
||||||
|
<h2>Why did I make this?</h2>
|
||||||
|
<ul>
|
||||||
|
<li>No decent API for the Wii Web Browser existed</li>
|
||||||
|
<li>Nintendo doesn't want it to exist...</li>
|
||||||
|
<li>Great way to teach children how to program</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slide" id="quirks">
|
||||||
|
<h2>Wii Browser & Quirks</h2>
|
||||||
|
<ul id="quirksul">
|
||||||
|
<li><strong>Opera 9.26</strong> - CSS2.1, <canvas>, SVG, and more...</li>
|
||||||
|
<li>Annoying memory limits (~88MB entire system, Browser is far less)</li>
|
||||||
|
<li><strong>Repaint slower</strong>; repeated timeouts less than 100ms causes the Wii to freeze up</li>
|
||||||
|
<li><strong>Only Wii Remotes work</strong>; no other controller types usable. :(</li>
|
||||||
|
<li>Leave type="" off <script> tags, or they won't work sometimes</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slide" id="js">
|
||||||
|
<h2>Wii-js API (Event Based)</h2>
|
||||||
|
<ul style="list-style-type: none; margin-left: 15px;">
|
||||||
|
<li id="code1">
|
||||||
|
<pre>var wiimote = new Wii.Remote(1, {horizontal: true}),
|
||||||
|
wiimote2 = new Wii.Remote(2, {horizontal: true});
|
||||||
|
|
||||||
|
wiimote.when('pressed_a', function() {
|
||||||
|
alert('A Button on Wiimote 1 Pressed!');
|
||||||
|
});
|
||||||
|
|
||||||
|
wiimote2.when('pressed_a', function() {
|
||||||
|
alert('Right Button on Wiimote 2 Pressed!');
|
||||||
|
});
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slide" id="node">
|
||||||
|
<h2>Opera on Wii has [[[Server-Sent-Events]]]!</h2>
|
||||||
|
<ul style="list-style-type: none; margin-left: 15px;">
|
||||||
|
<li id="code2">
|
||||||
|
<pre>var http = require('http');
|
||||||
|
|
||||||
|
function sendEvents(response) {
|
||||||
|
response.write('id: ' + (new Date()).toLocaleTimeString() + '\n');
|
||||||
|
response.write('data: ' + (Math.floor(Math.random() * 10000) + 2) + '\n');
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
sendEvents(response);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.createServer(function(request, response) {
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
});
|
||||||
|
sendEvents(response);
|
||||||
|
}).listen(8000);
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slide" id="thanks">
|
||||||
|
<h2>Thanks!</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Twitter:</strong> <a href="http://twitter.com/ryanmcgrath">@ryanmcgrath</a></li>
|
||||||
|
<li><strong>Wii-js on GitHub:</strong> <a href="http://github.com/ryanmcgrath/wii-js">http://github.com/ryanmcgrath/wii-js</a></li>
|
||||||
|
<li><strong>Demo:</strong> <a href="http://venodesigns.net/wii/">http://venodesigns.net/wii</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/wii.js"></script>
|
||||||
|
<script src="js/slides.js"></script>
|
||||||
|
<script src="js/highlight.min.js"></script>
|
||||||
|
<script>
|
||||||
|
hljs.highlightBlock(document.getElementById('code1'), null, false);
|
||||||
|
hljs.highlightBlock(document.getElementById('code2'), null, false);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
211
readme.md
Normal file
211
readme.md
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
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.
|
||||||
|
|
||||||
|
You can find a built source file and a _minified_ source file for production use in the **/js/** directory.
|
||||||
|
To play with a live example, load up the demo (_index.html_) on your own server, or feel free to use mine:
|
||||||
|
|
||||||
|
**wii-js Demo: [http://venodesigns.net/wii/](http://venodesigns.net/wii/)**
|
||||||
|
|
||||||
|
Working with the Wii's browser can be odd - it has moderately good support for CSS, so you're never really
|
||||||
|
as bad off as you'd be with a version of Internet Explorer - that said, if you're looking for a good read
|
||||||
|
on what's supported, check out **[this article on Opera Wii supported technologies](http://www.opera.com/docs/specs/opera9/?platform=wii)**.
|
||||||
|
|
||||||
|
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!');
|
||||||
|
});
|
||||||
|
|
||||||
|
Wii.listen();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
```
|
||||||
|
|
||||||
|
The final important piece is to start the Wii-event loop; this manages the event dispatcher internally. To do this, simply...
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
Wii.listen();
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Extra Tips and Tricks (Debugging)
|
||||||
|
------------------------------------------------------------------------------------------------------------------
|
||||||
|
One semi-useful trick to point out about this library is that each of your callback functions get passed two
|
||||||
|
arguments by default - a reference to the Wiimote you're working with, and the raw Wiimote status object that the
|
||||||
|
Wii reports back to the library. You get this in a normalized fashion, instead of having to deal with the odd polling
|
||||||
|
issues present in the browser.
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
var wiimote = new Wii.Remote(1, {horizontal: false});
|
||||||
|
|
||||||
|
wiimote.when('pressed_a', function(wii_remote, wii_remote_status) {
|
||||||
|
/* Alert an internal confidence level provided by the Wii. */
|
||||||
|
alert(wii_remote_status.dpdValidity);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Debugging Javascript on the Wii is also nothing short of incredibly annoying, so I've made some efforts to patch this
|
||||||
|
up and make life a bit easier. My typical debugging strategy with any Wii-related code would always start with
|
||||||
|
the following. The first thing to do is set the Wii listener to run in debug mode, like so:
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
Wii.listen({debug: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
With this set, you can log errors with any of the following functions. `error` can be a string or a complex object.
|
||||||
|
|
||||||
|
- **console.log(error);** - Tried and true, now supported.
|
||||||
|
- **console.debug(error);** - Same as console.log here, but syntax is supported.
|
||||||
|
- **throw new Error(error);** - Throw them, they'll be logged.
|
||||||
|
- **Wii.util.debug(error);** - The core function that handles logging internally.
|
||||||
|
|
||||||
|
If the typical Wii debugging flow isn't enough for you, go aggressive with this - just be aware that you can crash
|
||||||
|
the Wii's browser if you're using try/catch all over the place, as it's not cheap in Javascript.
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
try {
|
||||||
|
// Whatever function to execute
|
||||||
|
} catch(e) { Wii.util.debug(e); }
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Known Issues
|
||||||
|
------------------------------------------------------------------------------------------------------------------
|
||||||
|
**Primary Wiimote is a bit more responsive than the extra 3**
|
||||||
|
This library works by polling the status of the three extra Wii-remotes in 100ms intervals and dispatching events
|
||||||
|
based on this. Anything lower than 100ms causes the Wii to run into memory limitations, and the single-threaded
|
||||||
|
nature of the browser doesn't really help this issue.
|
||||||
|
|
||||||
|
The primary Wii-remote uses an odd combination of DOM-esque callbacks; due to this, it reports _more frequently_ than
|
||||||
|
the other Wii-remotes. It's not a showstopper by any means, but for small games it would in theory give a weighted advantage.
|
||||||
|
I'll probably end up throttling this through the library by means of a flag, e.g "game_mode": true in the initial options.
|
||||||
|
|
||||||
|
|
||||||
|
Todo List
|
||||||
|
------------------------------------------------------------------------------------------------------------------
|
||||||
|
- Build in functionality for multiple button presses at the same time (difficult to get right in this environment)
|
||||||
|
- Determine canceling B-button/scrolling on pages ("app"/"game" style)
|
||||||
|
- Determine feasibility of canceling out "1" press on the primary Wii-remote.
|
||||||
|
|
||||||
|
|
||||||
|
Building and Developing
|
||||||
|
------------------------------------------------------------------------------------------------------------------
|
||||||
|
If you'd like to help with this library, you're more than welcome to. Simply fork it on GitHub, work away, then
|
||||||
|
issue me a pull request. I generally respond within 24 hours.
|
||||||
|
|
||||||
|
The build system here is pretty simple - edits and changes go into the /js/src/ files, and you can run
|
||||||
|
|
||||||
|
python build.py
|
||||||
|
|
||||||
|
from the main directory to build a new version. The minifier here is YUI; Closure/UglifyJS are more aggressive, and
|
||||||
|
for some reason throw ridiculous issues in the Wii's browser that I've been unable to track down (and I don't have
|
||||||
|
more time to throw at it).
|
||||||
|
|
||||||
|
In short, the builds require Python/Java, but once you've got them all installed you should only need the command above.
|
||||||
|
|
||||||
|
|
||||||
|
How is this different from...?
|
||||||
|
-------------------------------------------------------------------------------------------------------------------
|
||||||
|
I sadly did not find out about [wii.js](http://www.bolinfest.com/wii/overview.php) until after I released this library;
|
||||||
|
with respect to the original author, his work only covers the primary Wii Remote and not the extra ones, nor has it
|
||||||
|
been updated in years. While his approach appears to be the same as mine (or mine the same as his), neither one
|
||||||
|
influenced the other, and they're totally separate works.
|
||||||
|
|
||||||
|
With the exception of wii.js, I do not know of any other (remaining) Wii interaction Javascript libraries. It's for
|
||||||
|
these reasons (and my desire for a simpler API) that I built this. ;)
|
||||||
|
|
||||||
|
|
||||||
|
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. ;)
|
||||||
117
server.js
Normal file
117
server.js
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* server.js
|
||||||
|
*
|
||||||
|
* An example of a Node.js server that streams a set
|
||||||
|
* of events to a browser. This is primarily aimed at
|
||||||
|
* use with wii-js; since the wii's browser has a set of
|
||||||
|
* quirks like no other, this file can be expected to change.
|
||||||
|
*
|
||||||
|
* This is commented possibly more liberally than it should be,
|
||||||
|
* as it's intended to be easily accessible by those who might not
|
||||||
|
* have worked with Node.js and/or Server Sent Events before.
|
||||||
|
*
|
||||||
|
* @Author: Ryan McGrath <ryan@venodesigns.net>
|
||||||
|
* @Requires: Nothing, sans a little Node.js
|
||||||
|
*/
|
||||||
|
var DEBUG = true,
|
||||||
|
PORT = 8080,
|
||||||
|
http = require('http'),
|
||||||
|
util = require('util'),
|
||||||
|
fs = require('fs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sendEvent()
|
||||||
|
*
|
||||||
|
* Sends down a set of events roughly every ~5 seconds. Not that
|
||||||
|
* many would go so low, but sending more than once every ~500ms is
|
||||||
|
* not recommended. The Wii's browser has some odd memory limitations
|
||||||
|
* that aren't too much fun to be caught up in.
|
||||||
|
*
|
||||||
|
* @response - response object/stream, where data gets written to.
|
||||||
|
*/
|
||||||
|
var sendEvents = function sendEvent(response) {
|
||||||
|
var id = (new Date()).toLocaleTimeString(),
|
||||||
|
data = (Math.floor(Math.random() * 10000) + 2);
|
||||||
|
|
||||||
|
response.write('id: ' + id + '\n');
|
||||||
|
response.write('data: ' + data + '\n');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this up to re-send in a few seconds on the
|
||||||
|
* same request.
|
||||||
|
*/
|
||||||
|
setTimeout(function() {
|
||||||
|
sendEvents(response);
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determineContentType(url)
|
||||||
|
*
|
||||||
|
* Given a url, determines one of three content types that we care
|
||||||
|
* about. I'm an opinionated person and chose not to use Express here,
|
||||||
|
* but if you'd rather not deal with it the static module there is quite
|
||||||
|
* possibly your new best friend.
|
||||||
|
*
|
||||||
|
* @url - string, url to be tested for content-type
|
||||||
|
*/
|
||||||
|
var determineContentType = function determineContentType(url) {
|
||||||
|
if(/\.js/.test(url)) return 'text/javascript';
|
||||||
|
if(/\.css/.test(url)) return 'text/css';
|
||||||
|
return 'text/html';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now we'll set up a simple server that listens our our port we set
|
||||||
|
* above, which will either distribute resources or events depending
|
||||||
|
* on the headers.
|
||||||
|
*/
|
||||||
|
http.createServer(function(request, response) {
|
||||||
|
/**
|
||||||
|
* All but useless to us...
|
||||||
|
*/
|
||||||
|
if(request.url === '/favicon.ico') {
|
||||||
|
response.writeHead(404);
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If things don't appear to be working, uncomment this and check out
|
||||||
|
* what's getting posted over. Certain headers need to be set by the browser;
|
||||||
|
* it's possible they're not getting set for some reason...
|
||||||
|
*/
|
||||||
|
if(DEBUG && request.url !== '/favicon.ico') {
|
||||||
|
util.puts('\n-----------------------------------------------------');
|
||||||
|
util.puts('URL: ' + request.url);
|
||||||
|
for(var key in request.headers) {
|
||||||
|
util.puts(key + ': ' + request.headers[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the comment about DEBUG/headers above. If we have proper headers, we'll
|
||||||
|
* start sending down a stream of events; if not, we'll assume it's a normal request
|
||||||
|
* and serve up some HTML/CSS/JS/etc.
|
||||||
|
*/
|
||||||
|
if(request.headers.accept && request.headers.accept === 'text/event-stream') {
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
});
|
||||||
|
sendEvents(response);
|
||||||
|
} else {
|
||||||
|
var file = request.url === '/' ? '/presentation.html' : request.url;
|
||||||
|
|
||||||
|
response.writeHead(200, {'Content-Type': determineContentType(request.url)});
|
||||||
|
fs.readFile(__dirname + file, 'utf-8', function(err, data) {
|
||||||
|
if(err) throw err;
|
||||||
|
response.write(data);
|
||||||
|
response.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).listen(PORT);
|
||||||
|
|
||||||
|
util.puts('\n--------------------------------------------------------------------');
|
||||||
|
util.puts('Server started and listening on port ' + PORT + '.');
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
.highlight .hll { background-color: #49483e }
|
|
||||||
.highlight { background: #3A3C42; color: #f8f8f2 }
|
|
||||||
.highlight .c { color: #75715e } /* Comment */
|
|
||||||
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
|
|
||||||
.highlight .k { color: #66d9ef } /* Keyword */
|
|
||||||
.highlight .l { color: #ae81ff } /* Literal */
|
|
||||||
.highlight .n { color: #f8f8f2 } /* Name */
|
|
||||||
.highlight .o { color: #f92672 } /* Operator */
|
|
||||||
.highlight .p { color: #f8f8f2 } /* Punctuation */
|
|
||||||
.highlight .cm { color: #75715e } /* Comment.Multiline */
|
|
||||||
.highlight .cp { color: #75715e } /* Comment.Preproc */
|
|
||||||
.highlight .c1 { color: #75715e } /* Comment.Single */
|
|
||||||
.highlight .cs { color: #75715e } /* Comment.Special */
|
|
||||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
|
||||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
|
||||||
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
|
|
||||||
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
|
|
||||||
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
|
|
||||||
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
|
|
||||||
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
|
|
||||||
.highlight .kt { color: #66d9ef } /* Keyword.Type */
|
|
||||||
.highlight .ld { color: #e6db74 } /* Literal.Date */
|
|
||||||
.highlight .m { color: #ae81ff } /* Literal.Number */
|
|
||||||
.highlight .s { color: #e6db74 } /* Literal.String */
|
|
||||||
.highlight .na { color: #a6e22e } /* Name.Attribute */
|
|
||||||
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
|
|
||||||
.highlight .nc { color: #a6e22e } /* Name.Class */
|
|
||||||
.highlight .no { color: #66d9ef } /* Name.Constant */
|
|
||||||
.highlight .nd { color: #a6e22e } /* Name.Decorator */
|
|
||||||
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
|
|
||||||
.highlight .ne { color: #a6e22e } /* Name.Exception */
|
|
||||||
.highlight .nf { color: #a6e22e } /* Name.Function */
|
|
||||||
.highlight .nl { color: #f8f8f2 } /* Name.Label */
|
|
||||||
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
|
|
||||||
.highlight .nx { color: #a6e22e } /* Name.Other */
|
|
||||||
.highlight .py { color: #f8f8f2 } /* Name.Property */
|
|
||||||
.highlight .nt { color: #f92672 } /* Name.Tag */
|
|
||||||
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
|
|
||||||
.highlight .ow { color: #f92672 } /* Operator.Word */
|
|
||||||
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
|
|
||||||
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
|
|
||||||
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
|
|
||||||
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
|
|
||||||
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
|
|
||||||
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
|
|
||||||
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
|
|
||||||
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
|
|
||||||
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
|
|
||||||
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
|
|
||||||
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
|
|
||||||
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
|
|
||||||
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
|
|
||||||
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
|
|
||||||
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
|
|
||||||
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
|
|
||||||
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
|
|
||||||
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
|
|
||||||
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
|
|
||||||
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
|
|
||||||
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
|
|
||||||
|
|
@ -1,356 +0,0 @@
|
||||||
@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);
|
|
||||||
html {
|
|
||||||
background: #6C7989;
|
|
||||||
background: #6c7989 -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #6c7989), color-stop(100%, #434b55)) fixed;
|
|
||||||
background: #6c7989 -webkit-linear-gradient(#6c7989, #434b55) fixed;
|
|
||||||
background: #6c7989 -moz-linear-gradient(#6c7989, #434b55) fixed;
|
|
||||||
background: #6c7989 -o-linear-gradient(#6c7989, #434b55) fixed;
|
|
||||||
background: #6c7989 -ms-linear-gradient(#6c7989, #434b55) fixed;
|
|
||||||
background: #6c7989 linear-gradient(#6c7989, #434b55) fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 50px 0;
|
|
||||||
margin: 0;
|
|
||||||
font: 14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
color: #555;
|
|
||||||
font-weight: 300;
|
|
||||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAYAAABNChwpAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAUdEVYdENyZWF0aW9uIFRpbWUAMy82LzEygrTcTAAAAFRJREFUSIljfPDggZRf5RIGGNjUHsNATz6jXmSL1Kb2GLiAX+USBnrymRgGGDCORgFmoNAXjEbBaBSMRsFoFIxGwWgUjEbBaBSMRsFoFIxGwWgUAABYNujumib3wAAAAABJRU5ErkJggg==') fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
width: 640px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #DEDEDE;
|
|
||||||
-webkit-border-radius: 8px;
|
|
||||||
-moz-border-radius: 8px;
|
|
||||||
-ms-border-radius: 8px;
|
|
||||||
-o-border-radius: 8px;
|
|
||||||
border-radius: 8px;
|
|
||||||
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px;
|
|
||||||
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header, section, footer {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #069;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0 0 20px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong {
|
|
||||||
color: #222;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
-webkit-border-radius: 8px 8px 0 0;
|
|
||||||
-moz-border-radius: 8px 8px 0 0;
|
|
||||||
-ms-border-radius: 8px 8px 0 0;
|
|
||||||
-o-border-radius: 8px 8px 0 0;
|
|
||||||
border-radius: 8px 8px 0 0;
|
|
||||||
background: #C6EAFA;
|
|
||||||
background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ddfbfc), color-stop(100%, #c6eafa));
|
|
||||||
background: -webkit-linear-gradient(#ddfbfc, #c6eafa);
|
|
||||||
background: -moz-linear-gradient(#ddfbfc, #c6eafa);
|
|
||||||
background: -o-linear-gradient(#ddfbfc, #c6eafa);
|
|
||||||
background: -ms-linear-gradient(#ddfbfc, #c6eafa);
|
|
||||||
background: linear-gradient(#ddfbfc, #c6eafa);
|
|
||||||
position: relative;
|
|
||||||
padding: 15px 20px;
|
|
||||||
border-bottom: 1px solid #B2D2E1;
|
|
||||||
}
|
|
||||||
header h1 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 1.2;
|
|
||||||
color: #069;
|
|
||||||
text-shadow: rgba(255, 255, 255, 0.9) 0 1px 0;
|
|
||||||
}
|
|
||||||
header.without-description h1 {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
header p {
|
|
||||||
margin: 0;
|
|
||||||
color: #61778B;
|
|
||||||
width: 300px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
header p.view {
|
|
||||||
display: none;
|
|
||||||
font-weight: 700;
|
|
||||||
text-shadow: rgba(255, 255, 255, 0.9) 0 1px 0;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
header p.view a {
|
|
||||||
color: #06c;
|
|
||||||
}
|
|
||||||
header p.view small {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
header ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
right: 20px;
|
|
||||||
top: 20px;
|
|
||||||
height: 38px;
|
|
||||||
padding: 1px 0;
|
|
||||||
background: #5198DF;
|
|
||||||
background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #77b9fb), color-stop(100%, #3782cd));
|
|
||||||
background: -webkit-linear-gradient(#77b9fb, #3782cd);
|
|
||||||
background: -moz-linear-gradient(#77b9fb, #3782cd);
|
|
||||||
background: -o-linear-gradient(#77b9fb, #3782cd);
|
|
||||||
background: -ms-linear-gradient(#77b9fb, #3782cd);
|
|
||||||
background: linear-gradient(#77b9fb, #3782cd);
|
|
||||||
border-radius: 5px;
|
|
||||||
-webkit-box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0;
|
|
||||||
-moz-box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0;
|
|
||||||
box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
header ul:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
left: -5px;
|
|
||||||
top: -4px;
|
|
||||||
right: -5px;
|
|
||||||
bottom: -6px;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
-webkit-border-radius: 8px;
|
|
||||||
-moz-border-radius: 8px;
|
|
||||||
-ms-border-radius: 8px;
|
|
||||||
-o-border-radius: 8px;
|
|
||||||
border-radius: 8px;
|
|
||||||
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0;
|
|
||||||
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0;
|
|
||||||
}
|
|
||||||
header ul li {
|
|
||||||
width: 79px;
|
|
||||||
float: left;
|
|
||||||
border-right: 1px solid #3A7CBE;
|
|
||||||
height: 38px;
|
|
||||||
}
|
|
||||||
header ul li.single {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
header ul li + li {
|
|
||||||
width: 78px;
|
|
||||||
border-left: 1px solid #8BBEF3;
|
|
||||||
}
|
|
||||||
header ul li + li + li {
|
|
||||||
border-right: none;
|
|
||||||
width: 79px;
|
|
||||||
}
|
|
||||||
header ul a {
|
|
||||||
line-height: 1;
|
|
||||||
font-size: 11px;
|
|
||||||
color: #fff;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 400;
|
|
||||||
padding-top: 6px;
|
|
||||||
height: 40px;
|
|
||||||
text-shadow: rgba(0, 0, 0, 0.4) 0 -1px 0;
|
|
||||||
}
|
|
||||||
header ul a strong {
|
|
||||||
font-size: 14px;
|
|
||||||
display: block;
|
|
||||||
color: #fff;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
padding: 15px 20px;
|
|
||||||
font-size: 15px;
|
|
||||||
border-top: 1px solid #fff;
|
|
||||||
background: -webkit-gradient(linear, 50% 0%, 50% 700, color-stop(0%, #fafafa), color-stop(100%, #dedede));
|
|
||||||
background: -webkit-linear-gradient(#fafafa, #dedede 700px);
|
|
||||||
background: -moz-linear-gradient(#fafafa, #dedede 700px);
|
|
||||||
background: -o-linear-gradient(#fafafa, #dedede 700px);
|
|
||||||
background: -ms-linear-gradient(#fafafa, #dedede 700px);
|
|
||||||
background: linear-gradient(#fafafa, #dedede 700px);
|
|
||||||
-webkit-border-radius: 0 0 8px 8px;
|
|
||||||
-moz-border-radius: 0 0 8px 8px;
|
|
||||||
-ms-border-radius: 0 0 8px 8px;
|
|
||||||
-o-border-radius: 0 0 8px 8px;
|
|
||||||
border-radius: 0 0 8px 8px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
color: #222;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 0 20px;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
p, ul, ol, table, pre, dl {
|
|
||||||
margin: 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3 {
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #393939;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3, h4, h5, h6 {
|
|
||||||
color: #494949;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 0 -20px 20px;
|
|
||||||
padding: 15px 20px 1px 40px;
|
|
||||||
font-style: italic;
|
|
||||||
background: #ccc;
|
|
||||||
background: rgba(0, 0, 0, 0.06);
|
|
||||||
color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
code, pre {
|
|
||||||
font-family: Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
|
|
||||||
color: #333;
|
|
||||||
font-size: 12px;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 20px;
|
|
||||||
background: #3A3C42;
|
|
||||||
color: #f8f8f2;
|
|
||||||
margin: 0 -20px 20px;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
color: #f8f8f2;
|
|
||||||
}
|
|
||||||
li pre {
|
|
||||||
margin-left: -60px;
|
|
||||||
padding-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
text-align: left;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
color: #222;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
background: #aaa;
|
|
||||||
height: 1px;
|
|
||||||
margin: 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
width: 640px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px 0 0;
|
|
||||||
color: #ccc;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
footer a {
|
|
||||||
color: #fff;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
footer p {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
footer p + p {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print, screen and (max-width: 740px) {
|
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
-webkit-border-radius: 0;
|
|
||||||
-moz-border-radius: 0;
|
|
||||||
-ms-border-radius: 0;
|
|
||||||
-o-border-radius: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
-moz-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
-webkit-border-radius: 0;
|
|
||||||
-moz-border-radius: 0;
|
|
||||||
-ms-border-radius: 0;
|
|
||||||
-o-border-radius: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 20px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
footer p {
|
|
||||||
float: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
footer p + p {
|
|
||||||
float: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media print, screen and (max-width:580px) {
|
|
||||||
header ul {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
header p.view {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
header p {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media print {
|
|
||||||
header p.view a small:before {
|
|
||||||
content: 'at http://github.com/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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