├── .documentup.json ├── .gitignore ├── LICENSE ├── README.md ├── demos ├── 00-helloworld.js ├── 01-painter.js ├── 02-writer.js ├── 03-game-goblins │ ├── README.md │ ├── game.js │ └── resources │ │ ├── background.png │ │ ├── entry.wav │ │ ├── hero.png │ │ ├── monster.png │ │ └── tada.wav ├── 04-audiocontext │ ├── audio.js │ └── conga1.wav ├── 05-game-fruits │ ├── README.md │ ├── fruits.js │ ├── music.wav │ ├── sounds │ │ ├── bomb.wav │ │ ├── fall.wav │ │ ├── floor.wav │ │ ├── fruit.wav │ │ ├── heart.wav │ │ └── timebullet.wav │ └── sprites │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ ├── b1.png │ │ ├── b2.png │ │ ├── b3.png │ │ ├── bg.jpg │ │ ├── fox1.png │ │ ├── fox2.png │ │ ├── fox3.png │ │ ├── fox4.png │ │ ├── fox5.png │ │ ├── fox6.png │ │ ├── fox7.png │ │ ├── fox8.png │ │ └── heart.png └── 06-irc-client │ ├── README.md │ ├── input.js │ ├── irc.js │ ├── nicklist.js │ └── output.js ├── lib ├── audiobuffersource.js ├── audiocontext.js ├── canvas.js ├── canvascontext2d.js ├── common.js ├── core.js ├── five.js ├── image.js └── window.js ├── make.js ├── package.json ├── screenshot.png └── test ├── .gitignore ├── audio.js ├── canvas.js ├── canvascontext2d.js ├── image.js ├── img-ref ├── canvas-blank.png ├── context2d-drawImage-save-restore-matrix.png ├── context2d-drawImage-scale-mirror.png ├── context2d-drawImage-translate-imageontop.png ├── context2d-drawImage.png ├── context2d-fillRect-black-sq.png ├── context2d-fillRect-black-sq2.png ├── context2d-fillRect-blue-sq.png ├── context2d-fillRect-many-rgb-colored.png ├── context2d-fillRect-red-sq.png ├── context2d-fillRect-scale-redonblue.png ├── context2d-fillRect-translate-redonblue.png ├── context2d-fillText-hello-big-arial.png ├── context2d-fillText-hello-big-courier.png ├── context2d-fillText-hello-black.png ├── context2d-fillText-hello-green.png ├── context2d-fillText-scale-redonblue.png ├── context2d-fillText-translate-redonblue.png ├── context2d-strokePath-lineTo-translate-blueonright.png └── context2d-strokePath-lineTo.png ├── resources └── image.png ├── test.js └── window.js /.documentup.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Node-Five", 3 | "twitter": [ 4 | "arturadib" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Mozilla Corporation 2 | All rights reserved. 3 | 4 | You may use this project under the terms of the New BSD license as follows: 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of Mozilla Corporation nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node-Five 2 | 3 | _This is a highly experimental, rapidly-changing project. The APIs might change overnight._ 4 | 5 | **Node-Five** brings a subset of HTML5 APIs to Node.js. Presently the main focus is on low-level graphics and audio (for example, `Canvas` and `AudioContext`), but other higher-level APIs are welcome. (HTML/CSS layout engine, anyone?) 6 | 7 | Node-Five is written in JavaScript on top of [Node-Qt](http://github.com/arturadib/node-qt). 8 | 9 | 10 | 11 | 12 | #### Hello world 13 | 14 | This example illustrates a minimal use of the HTML5 Canvas API: 15 | 16 | ![Screenshot](https://github.com/arturadib/node-five/raw/master/screenshot.png) 17 | 18 | ```javascript 19 | var five = require('path-to-node-five-dir'), 20 | window = new five.Window(300, 150), 21 | canvas = new five.Canvas(window), 22 | ctx = canvas.getContext('2d'); 23 | 24 | ctx.fillStyle = 'black'; 25 | ctx.fillText('hello node, hello qt', 20, 20); 26 | ``` 27 | 28 | For other examples see the [demos/](five/tree/master/demos) directory. 29 | 30 | 31 | 32 | 33 | 34 | # Getting started 35 | 36 | Since the project is under heavy development, no npm packages are available at the moment. To start, clone the latest development version and install the necessary dependencies: 37 | 38 | ``` 39 | $ git clone git://github.com/arturadib/node-five.git 40 | $ cd node-five 41 | $ npm install 42 | ``` 43 | 44 | If everything went well you should be able to run the demos, for example: 45 | 46 | ``` 47 | $ node demos/03-game-goblins/game.js 48 | ``` 49 | 50 | To run the unit tests: 51 | 52 | ``` 53 | $ node make test 54 | ``` 55 | 56 | (Since different platforms generate different images based on several factors, it's likely the image-based regression tests will fail on your setup. Ignore those errors). 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 69 | 70 | 71 | 72 | 73 | # API reference 74 | 75 | 76 | ## five 77 | 78 | Main object. Typical usage is: 79 | 80 | ``` 81 | var five = require('path-to-node-five-dir'); 82 | ``` 83 | 84 | #### useInterval() 85 | Add event handler to Node's event loop via setTimeout(). This is the default event loop integration. 86 | 87 | #### useTick() 88 | Add event handler to Node's event loop via `process.nextTick()`. 89 | This should used in applications that require more instant responsiveness (CPU-intensive!). 90 | 91 | #### stop() 92 | Stop Node-Five's event loop. Applications never exit without a call to this method. 93 | 94 | ## five.Window(width, height) 95 | 96 | Native window constructor with the given `height` and `width`. 97 | 98 | #### width = 640 99 | Gets/sets window width in pixels 100 | 101 | #### height = 480 102 | Gets/sets window height in pixels 103 | 104 | #### close() 105 | Closes window. It can't be reopened. 106 | 107 | #### addEventListener(event, callback) 108 | Binds `event` to `callback`. Supported events are listed below. 109 | 110 | #### removeEventListener(event, callback) 111 | Removes callback from `event` handler. 112 | 113 | #### Supported events for `addEventListener()` 114 | 115 | + `mousedown`: 116 | Callback will be passed `{ clientX, clientY, button }`. `button` values correspond 117 | to the convenience constants `five.LeftButton`, `five.RightButton`, etc. 118 | See http://developer.qt.nokia.com/doc/qt-4.8/qt.html#MouseButton-enum 119 | for a list of supported button codes 120 | 121 | + `mouseup`: 122 | Callback will be passed `{ clientX, clientY, button }` as in `mousedown`. 123 | 124 | + `mousemove`: 125 | Callback will be passed `{ clientX, clientY }`. 126 | 127 | + `keydown`: 128 | Callback will be passed `{ key, char }`. 129 | Key values correspond to the convenience constants `five.Key_Esc`, `five.Key_Left`, etc. 130 | See http://developer.qt.nokia.com/doc/qt-4.8/qt.html#Key-enum for the list of supported keys. 131 | `char` is the corresponding Unicode character, if available. 132 | 133 | + `keyup`: 134 | Callback will be passed `{ key, char }`. Details as in `keydown`. 135 | 136 | ## five.Canvas(window) 137 | 138 | Class/constructor for Canvas objects, either off-screen (no `window` argument) or inside an existing `window`. 139 | 140 | #### width = 300 141 | 142 | #### height = 150 143 | 144 | #### viewWidth = width 145 | (Non-standard) Width of the canvas view. Will crop canvas and create 146 | scroll bars if smaller than the canvas `width`. 147 | 148 | #### viewHeight = height 149 | (Non-standard) Height of the canvas view. Will crop canvas and create scroll bars if smaller 150 | than the canvas `height`. 151 | 152 | #### scrollTop = 0 153 | Vertical scroll position. To be used when `height > viewHeight`. 154 | Maximum value is `height - viewHeight`. 155 | 156 | #### scrollLeft = 0 157 | Horizontal scroll position. To be used when `width > viewWidth`. 158 | Maximum value is `width - viewWidth`. 159 | 160 | #### top = 0 161 | (Non-standard) Top position of the canvas with respect to parent window (if any), in pixels. 162 | Only absolute positioning is available. 163 | 164 | #### left = 0 165 | (Non-standard) Left position of the canvas with respect to parent window (if any), in pixels. 166 | Only absolute positioning is available. 167 | 168 | #### getContext(type) 169 | Presently only supports `type == '2d'`. 170 | 171 | #### toDataURL() 172 | 173 | ## five.Canvas.getContext('2d') 174 | 175 | The documentation below is for exposed methods after a `ctx = canvas.getContext('2d')` 176 | call, which returns a `CanvasRenderingContext2D` object. 177 | 178 | #### ctx.fillStyle = 'color' 179 | Currently only supports color types (e.g. `'rgb()'`, `'rgba()'`, `'blue'`, `'#aabbcc'`, etc) 180 | 181 | #### ctx.strokeStyle = 'color' 182 | Currently only supports color types (e.g. `'rgb()'`, `'rgba()'`, `'blue'`, `'#aabbcc'`, etc) 183 | 184 | #### ctx.font = '10px family' 185 | Currently only supports the format above 186 | 187 | #### ctx.fillRect(x, y, w, h) 188 | 189 | #### ctx.fillText(text, x, y) 190 | 191 | #### ctx.translate(x, y) 192 | 193 | #### ctx.scale(x, y) 194 | 195 | #### ctx.beginPath() 196 | 197 | #### ctx.closePath() 198 | 199 | #### ctx.moveTo(x, y) 200 | 201 | #### ctx.lineTo(x, y) 202 | 203 | #### ctx.stroke() 204 | 205 | #### ctx.drawImage(image, x, y) 206 | Presently only supports images created via `five.Image()` 207 | 208 | ## five.Image() 209 | 210 | Constructor for image objects. Intended to mirror `Image()` constructor from browsers. 211 | 212 | #### src = 'file_name' 213 | Presently only supports local paths, e.g. `./images/file.png` 214 | 215 | #### complete 216 | 217 | #### onload = callback 218 | 219 | ## five.AudioContext 220 | 221 | Class/constructor for AudioContext objects. 222 | 223 | #### destination 224 | Currently returns null 225 | 226 | #### createBufferSource() 227 | Returns a new AudioBufferSourceNode object 228 | 229 | ## five.AudioContext.createBufferSource() 230 | 231 | The documentation below is for exposed methods after a `source = context.createBufferSource()` 232 | call, which returns an `AudioBufferSourceNode` object. 233 | 234 | #### source.buffer = 'filename' 235 | Non-compliant. Presently must specify local filename. 236 | 237 | #### source.connect(dest) 238 | `dest` is currently a dummy variable. The destination will always 239 | be the default audio device 240 | 241 | #### source.loop = `true/false` 242 | 243 | #### source.noteOn(when) 244 | Currently `when = 0`, i.e. immediately 245 | -------------------------------------------------------------------------------- /demos/00-helloworld.js: -------------------------------------------------------------------------------- 1 | var five = require('..'), 2 | window = new five.Window(640, 480), 3 | canvas = new five.Canvas(window), 4 | ctx = canvas.getContext('2d'); 5 | 6 | canvas.width = window.width; 7 | canvas.height = window.height; 8 | 9 | ctx.font = '16px arial'; 10 | ctx.fillText('Hello world, from Node-Five', 20, 20); 11 | -------------------------------------------------------------------------------- /demos/01-painter.js: -------------------------------------------------------------------------------- 1 | var five = require('..'); 2 | 3 | var window = new five.Window(640, 480), 4 | canvas = new five.Canvas(window), 5 | ctx = canvas.getContext('2d'); 6 | 7 | canvas.width = window.width; 8 | canvas.height = window.height; 9 | 10 | ctx.strokeStyle = 'blue'; 11 | 12 | var holdingButton = false; 13 | 14 | window.addEventListener('mousedown', function(e) { 15 | ctx.fillRect(e.clientX, e.clientY, 1, 1); 16 | ctx.beginPath(); 17 | ctx.moveTo(e.clientX, e.clientY); 18 | 19 | holdingButton = true; 20 | }); 21 | 22 | window.addEventListener('mouseup', function(e) { 23 | ctx.fillRect(e.clientX, e.clientY, 1, 1); 24 | ctx.beginPath(); 25 | ctx.moveTo(e.clientX, e.clientY); 26 | 27 | holdingButton = false; 28 | }); 29 | 30 | window.addEventListener('mousemove', function(e) { 31 | if (!holdingButton) 32 | return; 33 | 34 | ctx.lineTo(e.clientX, e.clientY); 35 | ctx.stroke(); 36 | }); 37 | -------------------------------------------------------------------------------- /demos/02-writer.js: -------------------------------------------------------------------------------- 1 | var five = require('..'); 2 | 3 | var window = new five.Window(640, 480), 4 | canvas = new five.Canvas(window), 5 | ctx = canvas.getContext('2d'); 6 | 7 | canvas.width = window.width; 8 | canvas.height = window.height; 9 | 10 | // All units in pixels 11 | const kFontSize = 14, 12 | kInitialX = 10, 13 | kLinePadding = 4, 14 | kCharWidth = kFontSize - 4; 15 | 16 | var x = kInitialX, y = kFontSize + kLinePadding; 17 | 18 | // Reset writing area 19 | ctx.fillStyle = 'white'; 20 | ctx.fillRect(0, 0, 640, 480); 21 | 22 | // Defaults 23 | ctx.fillStyle = 'blue'; 24 | ctx.font = kFontSize + 'px courier'; 25 | 26 | // 27 | // Key handler 28 | // 29 | 30 | window.addEventListener('keydown', function(e) { 31 | if (e.key === five.Key_Enter || e.key === five.Key_Return) { 32 | clearCursor(x, y); 33 | x = kInitialX; 34 | y += kFontSize + kLinePadding; 35 | showCursor(x, y); 36 | return; 37 | } 38 | 39 | if (e.key === five.Key_Backspace) { 40 | if (x === kInitialX) 41 | return; 42 | 43 | clearCursor(x, y); 44 | x -= kCharWidth; 45 | 46 | // Erase character 47 | ctx.save(); 48 | ctx.fillStyle = 'white'; 49 | ctx.fillRect(x, y - kFontSize, kCharWidth, kFontSize + 4); 50 | ctx.restore(); 51 | 52 | showCursor(x, y); 53 | return; 54 | } 55 | 56 | // All others 57 | 58 | if (e.char.length === 0) 59 | return; 60 | 61 | clearCursor(x, y); 62 | ctx.fillText(e.char, x, y); 63 | x += kCharWidth; 64 | showCursor(x, y); 65 | }); 66 | 67 | // 68 | // Cursor blinking 69 | // 70 | 71 | var cursorOn = false; 72 | 73 | function clearCursor(x, y) { 74 | ctx.save(); 75 | ctx.fillStyle = 'white'; 76 | ctx.fillRect(x, y-13, 3, 16); 77 | ctx.restore(); 78 | cursorOn = false; 79 | } 80 | 81 | function showCursor(x, y) { 82 | ctx.save(); 83 | ctx.fillStyle = 'black'; 84 | ctx.fillRect(x, y-13, 3, 16); 85 | ctx.restore(); 86 | cursorOn = true; 87 | } 88 | 89 | setInterval(function() { 90 | if (cursorOn) 91 | clearCursor(x, y); 92 | else 93 | showCursor(x, y); 94 | }, 500); 95 | -------------------------------------------------------------------------------- /demos/03-game-goblins/README.md: -------------------------------------------------------------------------------- 1 | This example is adapted from the tutorial: 2 | 3 | + http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/ 4 | + https://github.com/lostdecade/simple_canvas_game 5 | -------------------------------------------------------------------------------- /demos/03-game-goblins/game.js: -------------------------------------------------------------------------------- 1 | // Simple canvas game: Get the goblins! 2 | // 3 | // Adapted from the tutorial: 4 | // http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/ 5 | // https://github.com/lostdecade/simple_canvas_game 6 | 7 | 8 | var five = require('../..'); 9 | 10 | // Create the canvas 11 | var window = new five.Window(512, 480); 12 | var canvas = new five.Canvas(window); 13 | var ctx = canvas.getContext("2d"); 14 | 15 | canvas.width = window.width; 16 | canvas.height = window.height; 17 | 18 | // Background image 19 | var bgReady = false; 20 | var bgImage = new five.Image(); 21 | bgImage.onload = function () { 22 | bgReady = true; 23 | }; 24 | bgImage.src = __dirname + "/resources/background.png"; 25 | 26 | // Hero image 27 | var heroReady = false; 28 | var heroImage = new five.Image(); 29 | heroImage.onload = function () { 30 | heroReady = true; 31 | }; 32 | heroImage.src = __dirname + "/resources/hero.png"; 33 | 34 | // Monster image 35 | var monsterReady = false; 36 | var monsterImage = new five.Image(); 37 | monsterImage.onload = function () { 38 | monsterReady = true; 39 | }; 40 | monsterImage.src = __dirname + "/resources/monster.png"; 41 | 42 | // Game objects 43 | var hero = { 44 | speed: 256 // movement in pixels per second 45 | }; 46 | var monster = {}; 47 | var monstersCaught = 0; 48 | 49 | // Handle keyboard controls 50 | var keysDown = {}; 51 | 52 | window.addEventListener("keydown", function (e) { 53 | keysDown[e.key] = true; 54 | }); 55 | 56 | window.addEventListener("keyup", function (e) { 57 | delete keysDown[e.key]; 58 | }, false); 59 | 60 | // Reset the game when the player catches a monster 61 | var reset = function () { 62 | hero.x = canvas.width / 2; 63 | hero.y = canvas.height / 2; 64 | 65 | // Throw the monster somewhere on the screen randomly 66 | monster.x = 32 + (Math.random() * (canvas.width - 64)); 67 | monster.y = 32 + (Math.random() * (canvas.height - 64)); 68 | }; 69 | 70 | // Update game objects 71 | var update = function (modifier) { 72 | if (five.Key_Up in keysDown) { // up 73 | hero.y -= hero.speed * modifier; 74 | } 75 | if (five.Key_Down in keysDown) { // down 76 | hero.y += hero.speed * modifier; 77 | } 78 | if (five.Key_Left in keysDown) { // left 79 | hero.x -= hero.speed * modifier; 80 | } 81 | if (five.Key_Right in keysDown) { // right 82 | hero.x += hero.speed * modifier; 83 | } 84 | 85 | // Are they touching? 86 | if ( 87 | hero.x <= (monster.x + 32) 88 | && monster.x <= (hero.x + 32) 89 | && hero.y <= (monster.y + 32) 90 | && monster.y <= (hero.y + 32) 91 | ) { 92 | ++monstersCaught; 93 | playSound(__dirname + '/resources/tada.wav'); 94 | reset(); 95 | } 96 | }; 97 | 98 | // Draw everything 99 | var render = function () { 100 | if (bgReady) { 101 | ctx.drawImage(bgImage, 0, 0); 102 | } 103 | 104 | if (heroReady) { 105 | ctx.drawImage(heroImage, hero.x, hero.y); 106 | } 107 | 108 | if (monsterReady) { 109 | ctx.drawImage(monsterImage, monster.x, monster.y); 110 | } 111 | 112 | // Score 113 | ctx.fillStyle = "rgb(250, 250, 250)"; 114 | ctx.font = "24px Helvetica"; 115 | // ctx.textAlign = "left"; 116 | // ctx.textBaseline = "top"; 117 | ctx.fillText("Goblins caught: " + monstersCaught, 50, 70); 118 | }; 119 | 120 | // The main game loop 121 | var main = function () { 122 | var now = Date.now(); 123 | var delta = now - then; 124 | 125 | update(delta / 1000); 126 | render(); 127 | 128 | then = now; 129 | }; 130 | 131 | 132 | function playSound(path) { 133 | var context = new five.AudioContext(), 134 | source = context.createBufferSource(); 135 | source.buffer = path; // currently non-compliant 136 | source.connect(context.destination); 137 | source.noteOn(0); // play sound 138 | } 139 | 140 | // Audio intro 141 | playSound(__dirname + '/resources/entry.wav'); 142 | 143 | // Let's play this game! 144 | reset(); 145 | var then = Date.now(); 146 | setInterval(main, 1); // Execute as fast as possible 147 | -------------------------------------------------------------------------------- /demos/03-game-goblins/resources/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/03-game-goblins/resources/background.png -------------------------------------------------------------------------------- /demos/03-game-goblins/resources/entry.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/03-game-goblins/resources/entry.wav -------------------------------------------------------------------------------- /demos/03-game-goblins/resources/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/03-game-goblins/resources/hero.png -------------------------------------------------------------------------------- /demos/03-game-goblins/resources/monster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/03-game-goblins/resources/monster.png -------------------------------------------------------------------------------- /demos/03-game-goblins/resources/tada.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/03-game-goblins/resources/tada.wav -------------------------------------------------------------------------------- /demos/04-audiocontext/audio.js: -------------------------------------------------------------------------------- 1 | var five = require('../..'); 2 | 3 | var context = new five.AudioContext(); 4 | 5 | var source = context.createBufferSource(); 6 | source.buffer = __dirname + '/conga1.wav'; // currently non-compliant 7 | source.connect(context.destination); 8 | source.noteOn(0); // play sound 9 | -------------------------------------------------------------------------------- /demos/04-audiocontext/conga1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/04-audiocontext/conga1.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/README.md: -------------------------------------------------------------------------------- 1 | Demo adapted from: 2 | 3 | https://developer.mozilla.org/en-US/demos/detail/fruits 4 | 5 | Copyright (c) 2012 E. Azuar # -LaNsHoR- lanshor@gmail.com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | -------------------------------------------------------------------------------- /demos/05-game-fruits/fruits.js: -------------------------------------------------------------------------------- 1 | /* Adapted to Node-Five by Artur B Adib */ 2 | /* Original source: https://developer.mozilla.org/en-US/demos/detail/fruits */ 3 | 4 | /*** Fruits! v0.3 ***/ 5 | 6 | /* Copyright (c) 2012 E. Azuar # -LaNsHoR- lanshor@gmail.com 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | var five = require('../..'); 23 | 24 | var window = new five.Window(800,600); 25 | 26 | //Aliases & Global Vars 27 | var game; 28 | var keyleft=0; 29 | var keyup=0; 30 | var keyright=0; 31 | 32 | //Global Functions 33 | function newGame() 34 | { 35 | if(game) 36 | { 37 | game.gameOver(); 38 | setTimeout(function(){game=0;newGame();},50); 39 | return; 40 | } 41 | game=new Game(); 42 | game.init(); 43 | } 44 | 45 | function gameLoop() 46 | { 47 | if(!game.gameover) 48 | { 49 | game.loop(); 50 | setTimeout(gameLoop,20) //about 50fps 51 | } 52 | } 53 | 54 | function keyControlDown(event) 55 | { 56 | var control=true; 57 | switch(event.key) 58 | { 59 | case five.Key_Left: keyleft=1; control=false;break; 60 | case five.Key_Up: keyup=1; control=false;break; 61 | case five.Key_Right: keyright=1;control=false;break; 62 | case five.Key_Down: control=false; 63 | } 64 | return control; 65 | } 66 | 67 | function keyControlUp(event) 68 | { 69 | var control=true; 70 | switch(event.key) 71 | { 72 | case five.Key_Left: keyleft=0; control=false;break; 73 | case five.Key_Up: keyup=0; control=false;break; 74 | case five.Key_Right: keyright=0;control=false;break; 75 | case five.Key_Down: control=false; 76 | } 77 | return control; 78 | } 79 | 80 | //Sound Control 81 | function Sound(id) 82 | { 83 | var context = new five.AudioContext(), 84 | source = context.createBufferSource(); 85 | 86 | source.buffer = __dirname+"/sounds/"+id+".wav"; 87 | source.connect(context.destination); 88 | 89 | this.play=function() 90 | { 91 | source.noteOn(0); // play sound 92 | } 93 | } 94 | 95 | var sounds=new Object(); 96 | sounds.bomb=new Sound("bomb"); 97 | sounds.fruit=new Sound("fruit"); 98 | sounds.heart=new Sound("heart"); 99 | sounds.fall=new Sound("fall"); 100 | sounds.timebullet=new Sound("timebullet"); 101 | sounds.floor=new Sound("floor"); 102 | 103 | //Game Class 104 | function Game() 105 | { 106 | //--graphics 107 | this.context=0; 108 | //--sprites 109 | this.bg=0; 110 | this.fruits_sprites=0; 111 | this.player_sprites=0; 112 | this.heart=0; 113 | this.clock=0; 114 | this.floor=0; 115 | //--entities 116 | this.fruits=0; 117 | this.player=0; 118 | this.bonus_floor=0; 119 | this.bonus_heart=0; 120 | this.bonus_clock=0; 121 | //--game control 122 | this.loaded=0; 123 | this.points=0; 124 | this.health=0; 125 | this.frames=0; 126 | this.with_floor=0; 127 | this.with_clock=0; 128 | this.floor_end=0; 129 | this.clock_end=0; 130 | this.gameover=false; 131 | //--time 132 | this.start=0; 133 | this.seconds=0; 134 | //--others 135 | this.n_fruits=0; 136 | this.n_resources=21; 137 | this.idata=0; //image data of tunned background 138 | //------------------------------------------------------ 139 | this.init=function() 140 | { 141 | //-- graphics 142 | var canvas=new five.Canvas(window); 143 | canvas.width = window.width; 144 | canvas.height = window.height; 145 | 146 | this.context=canvas.getContext('2d'); 147 | this.context.fillStyle="black"; 148 | this.context.fillText("loading... please, wait",50,50); 149 | 150 | //-- reset vars 151 | this.points=0; 152 | this.health=150; 153 | this.frames=0; 154 | this.with_floor=0; 155 | this.with_clock=0; 156 | this.bonus_heart=0; 157 | this.bonus_clock=0; 158 | this.bonus_floor=0; 159 | this.floor_end=0; 160 | this.clock_end=0; 161 | this.n_fruits=2; 162 | this.gameover=false; 163 | this.start=new Date(); 164 | this.player=new Player(); 165 | this.fruits=new Array(); 166 | for(var i=0;i900){this.n_fruits=11;return;} 236 | if(this.seconds>600){this.n_fruits=10;return;} 237 | if(this.seconds>300){this.n_fruits=9;return;} 238 | if(this.seconds>45) {this.n_fruits=8;return;} 239 | if(this.seconds>30) {this.n_fruits=7;return;} 240 | if(this.seconds>25) {this.n_fruits=6;return;} 241 | if(this.seconds>15) {this.n_fruits=5;return;} 242 | if(this.seconds>10) {this.n_fruits=4;return;} 243 | if(this.seconds>5) {this.n_fruits=3;return;} 244 | } 245 | this.loop=function() 246 | { 247 | //control 248 | var now=new Date(); 249 | this.increment(); 250 | this.seconds=(now-this.start)/1000; 251 | this.frames++; 252 | //clean and draw background 253 | if(!this.with_clock) 254 | this.context.drawImage(this.bg,0,0); 255 | // else 256 | // this.context.putImageData(this.idata,0,0); 257 | //draw floor 258 | if(this.with_floor) 259 | { 260 | this.context.fillStyle="rgba(100,175,255,0.5)"; 261 | this.context.fillRect(0,595,800,5); 262 | } 263 | //draw health hearts 264 | for(var i=0;ithis.health-30) 267 | break; 268 | this.context.drawImage(this.heart,i+5,5); 269 | } 270 | var mod=this.health%30; 271 | if(mod) 272 | this.context.drawImage(this.heart, 0, 0, mod, 30, i+5, 5, mod, 30); 273 | //draw and move bonus 274 | if(this.bonus_heart) 275 | { 276 | this.bonus_heart.render(this.context); 277 | if(this.bonus_heart.y>600) 278 | this.bonus_heart=0; 279 | } 280 | if(this.bonus_clock) 281 | { 282 | this.bonus_clock.render(this.context); 283 | if(this.bonus_clock.y>600) 284 | this.bonus_clock=0; 285 | } 286 | if(this.bonus_floor) 287 | { 288 | this.bonus_floor.render(this.context); 289 | if(this.bonus_floor.y>600) 290 | this.bonus_floor=0; 291 | } 292 | //draw and move fruits 293 | for(var i=0;i600) 299 | { 300 | sounds.fall.play(); 301 | if(this.fruits[i].type!=9) 302 | this.health-=5; 303 | this.fruits[i]=new Fruit(); 304 | } 305 | if(this.fruits[i].y>this.player.y && this.fruits[i].ythis.player.x && this.fruits[i].xthis.player.y && this.bonus_heart.ythis.player.x && this.bonus_heart.xthis.player.y && this.bonus_clock.ythis.player.x && this.bonus_clock.xthis.player.y && this.bonus_floor.ythis.player.x && this.bonus_floor.xthis.clock_end) 343 | this.with_clock=0; 344 | if(this.seconds>this.floor_end) 345 | this.with_floor=0; 346 | //bonus respawn 347 | if(this.seconds>30 && !this.bonus_heart && this.frames%1500==0) 348 | this.bonus_heart=new Bonus(1); 349 | if(this.seconds>45 && !this.bonus_clock && this.frames%750==0 && Math.random()>0.7) 350 | this.bonus_clock=new Bonus(2); 351 | if(this.seconds>20 && !this.bonus_floor && this.frames%500==0 && Math.random()>0.9) 352 | this.bonus_floor=new Bonus(3); 353 | //draw Player 354 | this.player.render(this.context); 355 | if(this.health<0) 356 | this.gameOver(); 357 | } // this.loop 358 | } // Game() 359 | 360 | //Fruit Class 361 | function Fruit() 362 | { 363 | this.type=Math.round(Math.random()*7)+1; 364 | if(Math.random()>0.95) 365 | this.type=9; //fruit bomb 366 | this.v=Math.random()*5+1; //v in pixels/frame 367 | this.y=-30; //coord y 368 | this.x=Math.random()*750+25; //coord x 369 | this.img=new five.Image(); //sprite 370 | this.img.src=__dirname+"/sprites/"+this.type+".png"; 371 | this.points=0; 372 | switch(this.type) 373 | { 374 | case 2:this.points=10;break; 375 | case 3:this.points=15;break; 376 | case 8:this.points=25;break; 377 | case 7:this.points=50;break; 378 | case 5:this.points=75;break; 379 | case 6:this.points=100;break; 380 | case 4:this.points=150;break; 381 | case 1:this.points=250;break; 382 | } 383 | this.render=function(context) 384 | { 385 | context.drawImage(this.img,this.x,this.y); 386 | if(game.with_floor && this.y>=565) 387 | { 388 | this.y=565; 389 | return; 390 | } 391 | if(game.with_clock) 392 | this.y+=(this.v*0.25); //time-bullet! 393 | else 394 | this.y+=this.v; //fall 395 | } 396 | } // Fruit() 397 | 398 | //Bonus Class 399 | function Bonus(type) 400 | { 401 | this.type=type; //1=heart; 2=clock; 3=floor 402 | this.v=Math.random()*5+1; //v in pixels/frame 403 | this.y=-30; //coord y 404 | this.x=Math.random()*750+25; //coord x 405 | this.img=new five.Image(); //sprite 406 | this.img.src=__dirname+"/sprites/b"+this.type+".png"; 407 | this.render=function(context) 408 | { 409 | context.drawImage(this.img,this.x,this.y); 410 | if(game.with_floor && this.y>=565) 411 | { 412 | this.y=565; 413 | return; 414 | } 415 | if(game.with_clock) 416 | this.y+=(this.v*0.25); //time-bullet! 417 | else 418 | this.y+=this.v; //fall 419 | } 420 | } // Bonus() 421 | 422 | //Player Class 423 | function Player() 424 | { 425 | this.x=100; 426 | this.y=550; 427 | this.vx=0; 428 | this.vy=0; 429 | this.frames=0; 430 | this.right=0; 431 | //- Sprites 432 | this.sprites=new Array(); 433 | for(var i=1;i<=8;i++) 434 | { 435 | this.sprites[i]=new five.Image(); 436 | this.sprites[i].src=__dirname+"/sprites/fox"+i+".png"; 437 | } 438 | this.render=function(context) 439 | { 440 | this.frames++; 441 | //- Movement 442 | if(keyleft) 443 | { 444 | if(this.right) 445 | this.vx=-1; //Fast stop 446 | else 447 | this.vx--; 448 | this.right=0; 449 | } 450 | else if(keyright) 451 | { 452 | if(!this.right) 453 | this.vx=1; //Fast stop 454 | else 455 | this.vx++; 456 | this.right=1; 457 | } 458 | else 459 | this.vx*=0.9; 460 | //- Limits 461 | if(this.x<0 || this.x>750) 462 | this.vx=0; 463 | this.vx=Math.max(this.vx,-15); //vx limits 464 | this.vx=Math.min(this.vx,+15); //vx limits 465 | //- Jump 466 | if(this.vy!=0) //Gravity 467 | this.vy+=1.5; 468 | if(keyup && this.y==550) 469 | this.vy=-20; 470 | if(((keyright && this.x==0) || (keyleft && this.x==750)) && this.y<550) //Super-Jump 471 | { 472 | if(this.x==0) 473 | keyleft=0; 474 | else 475 | keyright=0; 476 | this.vy=-20; 477 | } 478 | if(this.y>550) 479 | { 480 | this.vy=0; 481 | this.y=550; 482 | } 483 | this.y+=this.vy; 484 | this.x+=this.vx; 485 | this.x=Math.max(this.x,0); //scroll limits 486 | this.x=Math.min(this.x,750); //scroll limits 487 | //Render 488 | var i=5; 489 | if(this.vy==0 && Math.abs(this.vx)>0.2) //Run 490 | i=(Math.round(this.frames/2)%8)+1; 491 | else if(this.vy>0) //Jumping 492 | i=8; 493 | else if(this.vy<0) //Falling 494 | i=4; 495 | if(this.right) 496 | { 497 | context.save(); 498 | context.translate(800, 0); 499 | context.scale(-1, 1); 500 | context.drawImage(this.sprites[i], 800-this.x-50, this.y); 501 | context.restore(); 502 | } 503 | else 504 | context.drawImage(this.sprites[i],this.x,this.y); 505 | } 506 | } // Player() 507 | 508 | //Window control and event listeners 509 | window.addEventListener('keydown', keyControlDown); 510 | window.addEventListener('keyup', keyControlUp); 511 | 512 | // Play music 513 | { 514 | var context = new five.AudioContext(), 515 | source = context.createBufferSource(); 516 | 517 | source.buffer = __dirname+"/music.wav"; 518 | source.loop = true; 519 | source.connect(context.destination); 520 | source.noteOn(0); 521 | } 522 | 523 | newGame(); 524 | -------------------------------------------------------------------------------- /demos/05-game-fruits/music.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/music.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sounds/bomb.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sounds/bomb.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sounds/fall.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sounds/fall.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sounds/floor.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sounds/floor.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sounds/fruit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sounds/fruit.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sounds/heart.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sounds/heart.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sounds/timebullet.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sounds/timebullet.wav -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/1.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/2.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/3.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/4.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/5.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/6.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/7.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/8.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/9.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/b1.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/b2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/b2.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/b3.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/bg.jpg -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox1.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox2.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox3.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox4.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox5.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox6.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox7.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/fox8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/fox8.png -------------------------------------------------------------------------------- /demos/05-game-fruits/sprites/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/demos/05-game-fruits/sprites/heart.png -------------------------------------------------------------------------------- /demos/06-irc-client/README.md: -------------------------------------------------------------------------------- 1 | Extraordinarly simple IRC client, inspired by LimeChat. 2 | 3 | To use it you'll have to pull the npm package `irc`: 4 | 5 | ``` 6 | $ npm install irc 7 | ``` 8 | 9 | Then node-run `irc.js` as usual with the necessary Node-Five flags. 10 | -------------------------------------------------------------------------------- /demos/06-irc-client/input.js: -------------------------------------------------------------------------------- 1 | var five = global.five, 2 | window = global.window; 3 | 4 | var inputCallback = null, 5 | text = ''; 6 | 7 | // All units in pixels 8 | const kFontSize = 12, 9 | kInitialX = 10, 10 | kCharWidth = kFontSize - 4, 11 | kBgColor = 'black', 12 | kCursorColor = 'white', 13 | kColor = 'white', 14 | kLineColor = 'white'; 15 | 16 | 17 | // 18 | // Initialize input area 19 | // 20 | 21 | var canvas = new five.Canvas(window), 22 | ctx = canvas.getContext('2d'); 23 | 24 | canvas.width = window.width; 25 | canvas.height = 40; 26 | canvas.top = window.height - canvas.height; 27 | canvas.left = 0; 28 | 29 | ctx.fillStyle = kColor; 30 | ctx.font = kFontSize + 'px courier'; 31 | 32 | function clearInput() { 33 | ctx.save(); 34 | ctx.fillStyle = kBgColor; 35 | ctx.fillRect(0, 0, canvas.width, canvas.height); 36 | // Separation line 37 | ctx.fillStyle = kLineColor; 38 | ctx.fillRect(0, 0, canvas.width, 1); 39 | ctx.restore(); 40 | } 41 | 42 | clearInput(); 43 | var x = kInitialX, y = canvas.height/2 + kFontSize/2; 44 | 45 | // 46 | // Key handler 47 | // 48 | 49 | window.addEventListener('keydown', function(e) { 50 | if (e.key === five.Key_Enter || e.key === five.Key_Return) { 51 | clearCursor(x, y); 52 | x = kInitialX; 53 | clearInput(); 54 | showCursor(x, y); 55 | 56 | if (inputCallback) 57 | inputCallback(text); 58 | text = ''; 59 | 60 | return; 61 | } 62 | 63 | if (e.key === five.Key_Backspace) { 64 | if (x === kInitialX) 65 | return; 66 | 67 | clearCursor(x, y); 68 | x -= kCharWidth; 69 | 70 | // Erase character 71 | ctx.save(); 72 | ctx.fillStyle = kBgColor; 73 | ctx.fillRect(x, y - kFontSize, kCharWidth, kFontSize + 4); 74 | ctx.restore(); 75 | 76 | showCursor(x, y); 77 | return; 78 | } 79 | 80 | // All others 81 | 82 | if (e.char.length === 0) 83 | return; 84 | 85 | clearCursor(x, y); 86 | ctx.fillText(e.char, x, y); 87 | text += e.char; 88 | x += kCharWidth; 89 | showCursor(x, y); 90 | }); 91 | 92 | // 93 | // Cursor blinking 94 | // 95 | 96 | var cursorOn = false; 97 | 98 | function clearCursor(x, y) { 99 | ctx.save(); 100 | ctx.fillStyle = kBgColor; 101 | ctx.fillRect(x, y-13, 2, 16); 102 | ctx.restore(); 103 | cursorOn = false; 104 | } 105 | 106 | function showCursor(x, y) { 107 | ctx.save(); 108 | ctx.fillStyle = kCursorColor; 109 | ctx.fillRect(x, y-13, 2, 16); 110 | ctx.restore(); 111 | cursorOn = true; 112 | } 113 | 114 | setInterval(function() { 115 | if (cursorOn) 116 | clearCursor(x, y); 117 | else 118 | showCursor(x, y); 119 | }, 500); 120 | 121 | // 122 | // Exports 123 | // 124 | 125 | exports.onInput = function(callback) { 126 | inputCallback = callback; 127 | } 128 | -------------------------------------------------------------------------------- /demos/06-irc-client/irc.js: -------------------------------------------------------------------------------- 1 | // 2 | // Globals 3 | // 4 | global.five = require('../..'); 5 | global.window = new five.Window(1000, 800); 6 | 7 | var irc = require('irc'), 8 | inputArea = require('./input'), 9 | outputArea = require('./output'), 10 | nicklistArea = require('./nicklist'); 11 | 12 | const kServer = 'irc.mozilla.org', 13 | kNick = 'McTest', 14 | kChannel = '#labs'; 15 | 16 | outputArea.push('~Connecting to '+kServer+' '+kChannel+' as "'+kNick+'"...'); 17 | 18 | var client = new irc.Client(kServer, kNick, { 19 | channels: [kChannel] 20 | }); 21 | 22 | inputArea.onInput(function(text) { 23 | outputArea.push('$'+kNick+': ' + text); 24 | client.say(kChannel, text); 25 | }); 26 | 27 | client.addListener('names', function (channel, nicks) { 28 | var nickArr = []; 29 | for (nick in nicks) { 30 | nickArr.push(nick); 31 | } 32 | nickArr.sort(); 33 | nicklistArea.update(nickArr); 34 | }); 35 | 36 | client.addListener('message', function (from, to, message) { 37 | outputArea.push('@'+from+': ' + message); 38 | }); 39 | 40 | client.addListener('quit', function (nick, reason, channels, message) { 41 | outputArea.push('~'+nick+' has left IRC: ' + (reason || 'no reason')); 42 | }); 43 | 44 | client.addListener('join', function (channel, nick, message) { 45 | outputArea.push('~'+nick+' has joined'); 46 | }); 47 | -------------------------------------------------------------------------------- /demos/06-irc-client/nicklist.js: -------------------------------------------------------------------------------- 1 | var five = global.five, 2 | window = global.window; 3 | 4 | // All units in pixels 5 | const kFontSize = 12, 6 | kBgColor = '#002', 7 | kColor = 'rgb(200,170,120)', 8 | kLineColor = 'white', 9 | kPaddingX = 8, 10 | kPaddingY = 4, 11 | kLineIncrement = kFontSize + 4, 12 | kWidth = 150; 13 | 14 | // 15 | // Initialize nick list area 16 | // 17 | 18 | var canvas = new five.Canvas(window), 19 | ctx = canvas.getContext('2d'); 20 | 21 | canvas.width = kWidth; 22 | canvas.height = window.height - 40; 23 | canvas.viewHeight = canvas.height; 24 | canvas.top = 0; 25 | canvas.left = window.width - kWidth; 26 | ctx.font = kFontSize + 'px courier'; 27 | 28 | function clearCanvas() { 29 | ctx.save(); 30 | ctx.fillStyle = kBgColor; 31 | ctx.fillRect(0, 0, canvas.width, canvas.height); 32 | ctx.fillStyle = kLineColor; 33 | ctx.fillRect(0, 0, 1, canvas.height); 34 | ctx.restore(); 35 | } 36 | clearCanvas(); 37 | 38 | // 39 | // Exports 40 | // 41 | 42 | exports.update = function(nickArr) { 43 | // Reset y to very top 44 | var y = kFontSize + kPaddingY; 45 | 46 | var height = y + nickArr.length*kLineIncrement; 47 | if (height > canvas.height) 48 | canvas.height = height; 49 | 50 | clearCanvas(); 51 | 52 | nickArr.forEach(function(nick) { 53 | // Simple output 54 | ctx.save(); 55 | ctx.fillStyle = kColor; 56 | ctx.fillText(nick, kPaddingX, y); 57 | ctx.restore(); 58 | 59 | y += kLineIncrement; 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /demos/06-irc-client/output.js: -------------------------------------------------------------------------------- 1 | var five = global.five, 2 | window = global.window; 3 | 4 | // All units in pixels 5 | const kFontSize = 12, 6 | kBgColor = 'black', 7 | kColor = 'white', 8 | kNickColor = 'rgb(200, 170, 120)', 9 | kInfoColor = '#666', 10 | kSelfColor = 'rgb(100, 120, 140)', 11 | kPaddingX = 8, 12 | kPaddingY = 8, 13 | kLineIncrement = kFontSize + 4; 14 | 15 | // 16 | // Initialize output area 17 | // 18 | 19 | var canvas = new five.Canvas(window), 20 | ctx = canvas.getContext('2d'); 21 | 22 | canvas.width = window.width - 150; 23 | canvas.height = window.height - 40; 24 | canvas.viewHeight = canvas.height; 25 | canvas.top = 0; 26 | canvas.left = 0; 27 | ctx.font = kFontSize + 'px courier'; 28 | 29 | var y, textArr = []; 30 | 31 | function printLine(line) { 32 | // 33 | // Info line: "~Line contents" 34 | // 35 | var infoMatch = line.match(/^\~/); 36 | if (infoMatch) { 37 | // Remove '~' 38 | line = line.replace(/^\~/, ''); 39 | ctx.save(); 40 | ctx.fillStyle = kInfoColor; 41 | ctx.fillText(line, kPaddingX, y); 42 | ctx.restore(); 43 | y += kLineIncrement; 44 | return; 45 | } 46 | 47 | // 48 | // Simple output 49 | // 50 | ctx.save(); 51 | ctx.fillStyle = kColor; 52 | ctx.fillText(line.substr(1), kPaddingX, y); 53 | ctx.restore(); 54 | 55 | // 56 | // Message line: "@nick Message contents" 57 | // Prints colored nick on top of simple output 58 | // 59 | var nickMatch = line.match(/^\@([\w|\d|\-]+)/); 60 | if (nickMatch) { 61 | ctx.save(); 62 | ctx.fillStyle = kNickColor; 63 | ctx.fillText(nickMatch[1], kPaddingX, y); 64 | ctx.restore(); 65 | } 66 | 67 | // 68 | // Self message line: "$nick Message contents" 69 | // Prints colored nick on top of simple output 70 | // 71 | var selfMatch = line.match(/^\$([\w|\d|\-]+)/); 72 | if (selfMatch) { 73 | ctx.save(); 74 | ctx.fillStyle = kSelfColor; 75 | ctx.fillText(selfMatch[1], kPaddingX, y); 76 | ctx.restore(); 77 | } 78 | 79 | y += kLineIncrement; 80 | } 81 | 82 | // Re-paints the entire canvas 83 | function refresh() { 84 | // Reset y to very top 85 | y = kFontSize + kPaddingY; 86 | 87 | ctx.save(); 88 | ctx.fillStyle = kBgColor; 89 | ctx.fillRect(0, 0, canvas.width, canvas.height); 90 | ctx.restore(); 91 | 92 | textArr.forEach(function(text) { 93 | printLine(text); 94 | }); 95 | 96 | canvas.scrollTop = canvas.height; 97 | } 98 | 99 | refresh(); 100 | 101 | // 102 | // Exports 103 | // 104 | 105 | exports.push = function(text) { 106 | text = text.toString(); 107 | textArr.push(text); 108 | 109 | if (y > canvas.height) { 110 | canvas.height += kLineIncrement; 111 | refresh(); 112 | } else { 113 | printLine(text); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/audiobuffersource.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | //@ 31 | //@ ## five.AudioContext.createBufferSource() 32 | //@ 33 | //@ The documentation below is for exposed methods after a `source = context.createBufferSource()` 34 | //@ call, which returns an `AudioBufferSourceNode` object. 35 | 36 | var qt = require('node-qt'); 37 | 38 | // 39 | // Class AudioBufferSourceNode() 40 | // 41 | module.exports = function() { 42 | 43 | /////////////////////////////////////////////////////////////////////////// 44 | // Private 45 | // 46 | 47 | var sound_; // QSound 48 | 49 | /////////////////////////////////////////////////////////////////////////// 50 | // Public 51 | // 52 | 53 | var obj = Object.create({ 54 | //@ 55 | //@ #### source.buffer = 'filename' 56 | //@ Non-compliant. Presently must specify local filename. 57 | set buffer(val) { 58 | sound_ = new qt.QSound(val); 59 | }, 60 | 61 | //@ 62 | //@ #### source.connect(dest) 63 | //@ `dest` is currently a dummy variable. The destination will always 64 | //@ be the default audio device 65 | connect: function() { 66 | }, 67 | 68 | //@ 69 | //@ #### source.loop = `true/false` 70 | set loop(val) { 71 | if (!sound_) 72 | return; 73 | 74 | if (val) 75 | sound_.setLoops(-1); 76 | else 77 | sound_.setLoops(0); 78 | }, 79 | 80 | //@ 81 | //@ #### source.noteOn(when) 82 | //@ Currently `when = 0`, i.e. immediately 83 | noteOn: function() { 84 | if (!sound_) 85 | return; 86 | 87 | sound_.play(); 88 | }, 89 | }); // prototype 90 | 91 | /////////////////////////////////////////////////////////////////////////// 92 | // Constructor 93 | // 94 | 95 | return obj; 96 | } 97 | -------------------------------------------------------------------------------- /lib/audiocontext.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | 31 | ////////////////////////////////////////////////////////////////////////////// 32 | // 33 | // Only a very simplified version of the API is currently implemented, and we 34 | // do so with a temporary hack based on QSound, which can only operate with 35 | // local files. We should really be using QAudioOutput, which is part of Qt's 36 | // low level multimedia facilities (QtMultimedia). 37 | // 38 | // Do not use the Phonon module. It's too high-level, and introduces terrible 39 | // platform dependencies (GStream lib on Linux, DirectX SDK on Windows, etc). 40 | // 41 | 42 | 43 | //@ 44 | //@ ## five.AudioContext 45 | //@ 46 | //@ Class/constructor for AudioContext objects. 47 | 48 | var qt = require('node-qt'), 49 | AudioBufferSourceNode = require('./audiobuffersource.js'); 50 | 51 | // 52 | // Class AudioContext() 53 | // 54 | module.exports = function() { 55 | 56 | /////////////////////////////////////////////////////////////////////////// 57 | // Private 58 | // 59 | 60 | /////////////////////////////////////////////////////////////////////////// 61 | // Public 62 | // 63 | 64 | var obj = Object.create({ 65 | //@ 66 | //@ #### destination 67 | //@ Currently returns null 68 | get destination() { 69 | return null; 70 | }, 71 | 72 | //@ 73 | //@ #### createBufferSource() 74 | //@ Returns a new AudioBufferSourceNode object 75 | createBufferSource: function() { 76 | return new AudioBufferSourceNode; 77 | } 78 | }); // prototype 79 | 80 | /////////////////////////////////////////////////////////////////////////// 81 | // Constructor 82 | // 83 | 84 | return obj; 85 | } 86 | -------------------------------------------------------------------------------- /lib/canvas.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | //@ 31 | //@ ## five.Canvas(window) 32 | //@ 33 | //@ Class/constructor for Canvas objects, either off-screen (no `window` argument) or inside an existing `window`. 34 | 35 | var qt = require('node-qt'), 36 | CanvasRenderingContext2D = require('./canvascontext2d'); 37 | 38 | // Holds references to objects that shouldn't get garbage collected during a Node-Five session 39 | // e.g. Windows, painting devices (Pixmaps), etc 40 | var refStore = []; 41 | 42 | // 43 | // Class Canvas() 44 | // 45 | module.exports = function(parent) { 46 | 47 | /////////////////////////////////////////////////////////////////////////// 48 | // Private 49 | // 50 | 51 | var pixmap_, painter_, 52 | parentWidget_ = parent ? parent.__widget : null, 53 | scrollArea_ = null, 54 | widget_ = null, 55 | geom_ = { 56 | width: 300, // Spec default 57 | height: 150, // Spec default 58 | viewWidth: undefined, 59 | viewHeight: undefined, 60 | top: 0, 61 | left: 0 62 | }; 63 | 64 | function resetPixmap() { 65 | pixmap_ = new qt.QPixmap(geom_.width, geom_.height); 66 | // Spec default is transparent black 67 | pixmap_.fill(new qt.QColor(0, 0, 0, 0)); 68 | 69 | if (painter_.isActive()) 70 | painter_.end(); 71 | painter_.begin(pixmap_); 72 | } 73 | 74 | function updateGeometry() { 75 | if (!scrollArea_ || !widget_) 76 | return; 77 | 78 | scrollArea_.move(geom_.left, geom_.top); 79 | scrollArea_.resize(geom_.viewWidth || geom_.width, geom_.viewHeight || geom_.height); 80 | widget_.resize(geom_.width, geom_.height); 81 | 82 | // Avoid having one scrollbar come up due to another scrollbar appearing 83 | // This will crop the content though, corresponding to the scrollbar's width/height 84 | if (geom_.viewWidth && geom_viewWidth < geom_.width) 85 | scrollArea_.setHorizontalScrollBarPolicy(0); // auto 86 | else 87 | scrollArea_.setHorizontalScrollBarPolicy(1); // off 88 | 89 | if (geom_.viewHeight && geom_.viewHeight < geom_.height) 90 | scrollArea_.setVerticalScrollBarPolicy(0); // auto 91 | else 92 | scrollArea_.setVerticalScrollBarPolicy(1); // off 93 | } 94 | 95 | /////////////////////////////////////////////////////////////////////////// 96 | // Public 97 | // 98 | 99 | var obj = Object.create({ 100 | //@ 101 | //@ #### width = 300 102 | set width(val) { 103 | geom_.width = val; 104 | resetPixmap(); // as per spec 105 | updateGeometry(); 106 | }, 107 | 108 | get width() { 109 | return geom_.width; 110 | }, 111 | 112 | //@ 113 | //@ #### height = 150 114 | set height(val) { 115 | geom_.height = val; 116 | resetPixmap(); // as per spec 117 | updateGeometry(); 118 | }, 119 | 120 | get height() { 121 | return geom_.height; 122 | }, 123 | 124 | //@ 125 | //@ #### viewWidth = width 126 | //@ (Non-standard) Width of the canvas view. Will crop canvas and create 127 | //@ scroll bars if smaller than the canvas `width`. 128 | set viewWidth(val) { 129 | geom_.viewWidth = val; 130 | updateGeometry(); 131 | }, 132 | 133 | get viewWidth() { 134 | return geom_.viewWidth; 135 | }, 136 | 137 | //@ 138 | //@ #### viewHeight = height 139 | //@ (Non-standard) Height of the canvas view. Will crop canvas and create scroll bars if smaller 140 | //@ than the canvas `height`. 141 | set viewHeight(val) { 142 | geom_.viewHeight = val; 143 | updateGeometry(); 144 | }, 145 | 146 | get viewHeight() { 147 | return geom_.viewHeight; 148 | }, 149 | 150 | //@ 151 | //@ #### scrollTop = 0 152 | //@ Vertical scroll position. To be used when `height > viewHeight`. 153 | //@ Maximum value is `height - viewHeight`. 154 | set scrollTop(val) { 155 | if (typeof val === 'number') 156 | scrollArea_.verticalScrollBar().setValue(val); 157 | }, 158 | 159 | get scrollTop() { 160 | return scrollArea_.verticalScrollBar().value(); 161 | }, 162 | 163 | //@ 164 | //@ #### scrollLeft = 0 165 | //@ Horizontal scroll position. To be used when `width > viewWidth`. 166 | //@ Maximum value is `width - viewWidth`. 167 | set scrollLeft(val) { 168 | if (typeof val === 'number') 169 | scrollArea_.horizontalScrollBar().setValue(val); 170 | }, 171 | 172 | get scrollLeft() { 173 | return scrollArea_.horizontalScrollBar().value(); 174 | }, 175 | 176 | //@ 177 | //@ #### top = 0 178 | //@ (Non-standard) Top position of the canvas with respect to parent window (if any), in pixels. 179 | //@ Only absolute positioning is available. 180 | set top(val) { 181 | geom_.top = val; 182 | updateGeometry(); 183 | }, 184 | 185 | get top() { 186 | return geom_.top; 187 | }, 188 | 189 | //@ 190 | //@ #### left = 0 191 | //@ (Non-standard) Left position of the canvas with respect to parent window (if any), in pixels. 192 | //@ Only absolute positioning is available. 193 | set left(val) { 194 | geom_.left = val; 195 | updateGeometry(); 196 | }, 197 | 198 | get left() { 199 | return geom_.left; 200 | }, 201 | 202 | //@ 203 | //@ #### getContext(type) 204 | //@ Presently only supports `type == '2d'`. 205 | getContext: function(type) { 206 | if (type === '2d') 207 | return new CanvasRenderingContext2D(painter_, parentWidget_); 208 | }, 209 | 210 | //@ 211 | //@ #### toDataURL() 212 | toDataURL: function() { 213 | // HACK 214 | // Ideally we'd save the pixmap to a QBuffer-QByteArray in memory, then run QByteArray.toBase64() 215 | // This would require bindings to QBuffer and QByteArray though, so some other time... 216 | var fs = require('fs'); 217 | pixmap_.save('__tmp_canvas.png'); 218 | var buf = fs.readFileSync('__tmp_canvas.png'); 219 | fs.unlink('__tmp_canvas.png'); 220 | return 'data:image/png;base64,' + buf.toString('base64'); 221 | } 222 | }); // prototype 223 | 224 | /////////////////////////////////////////////////////////////////////////// 225 | // Constructor 226 | // 227 | 228 | painter_ = new qt.QPainter; 229 | resetPixmap(); 230 | 231 | // Painting on a window? 232 | if (parentWidget_) { 233 | // Create widget wrapper (for scroll support) and the widget itself 234 | scrollArea_ = new qt.QScrollArea(parentWidget_); 235 | widget_ = new qt.QWidget(scrollArea_); 236 | scrollArea_.setWidget(widget_); 237 | 238 | scrollArea_.setFrameShape(0); // no borders 239 | scrollArea_.setFocusPolicy(0); // no focus (prevents Qt from intercepting Tab + arrow keys) 240 | widget_.setFocusPolicy(0); // no focus 241 | 242 | scrollArea_.resize(geom_.width, geom_.height); 243 | widget_.resize(geom_.width, geom_.height); 244 | scrollArea_.show(); 245 | widget_.show(); 246 | 247 | // Bind pixmap to widget 248 | widget_.paintEvent(function() { 249 | var painter_ = new qt.QPainter; 250 | painter_.begin(widget_); 251 | painter_.drawPixmap(0, 0, pixmap_); 252 | painter_.end(); 253 | }); 254 | } 255 | 256 | refStore.push(obj); 257 | return obj; 258 | } 259 | -------------------------------------------------------------------------------- /lib/canvascontext2d.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | ////////////////////////////////////////////////////////////////////////////// 31 | // 32 | // The implementation below is based on QPainter, whose API is fortuitously 33 | // very similar to that of 2d canvas context 34 | // 35 | 36 | //@ 37 | //@ ## five.Canvas.getContext('2d') 38 | //@ 39 | //@ The documentation below is for exposed methods after a `ctx = canvas.getContext('2d')` 40 | //@ call, which returns a `CanvasRenderingContext2D` object. 41 | 42 | var qt = require('node-qt'); 43 | 44 | // getStyle() 45 | // Helper function for fillStyle and strokeStyle getters 46 | // Returns appropriate value from given style state 47 | function getStyle(state) { 48 | if (!(state instanceof qt.QColor)) 49 | return null; // not yet supported 50 | 51 | // It's a QColor 52 | var color = state; 53 | 54 | if (color.alpha() === 255) 55 | // '#aabbcc' 56 | return color.name(); 57 | else 58 | // 'rgba(1, 2, 3, 0.5)' 59 | return 'rgba('+color.red()+', '+color.green()+', '+color.blue()+', '+(color.alpha()/255).toFixed(2)+')'; 60 | } 61 | 62 | // setStyle() 63 | // Helper function for fillStyle and strokeStyle setters 64 | // Returns internal state from user-given value 65 | function setStyle(val) { 66 | if (!(typeof val === 'string')) 67 | return null; // not yet supported 68 | 69 | val = val.replace(/\s/g, ''); 70 | 71 | // 'rgb(1, 2, 3) 72 | var rgbMatch = val.match(/rgb\((\d+)\,(\d+)\,(\d+).*\)/); 73 | if (rgbMatch) 74 | return new qt.QColor(rgbMatch[1], rgbMatch[2], rgbMatch[3]); 75 | 76 | // 'rgba(1, 2, 3, 0.5)' 77 | var rgbaMatch = val.match(/rgba\((\d+)\,(\d+)\,(\d+)\,([\d|\.]+).*\)/); 78 | if (rgbaMatch) 79 | return new qt.QColor(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], 255*rgbaMatch[4]); 80 | 81 | // '#abc', '#aabbcc', etc, 'blue', 'black', etc 82 | // These are handled by QColor itself 83 | return new qt.QColor(val); 84 | } 85 | 86 | // 87 | // Class CanvasRenderingContext2D() 88 | // 89 | module.exports = function(painter, widget) { 90 | 91 | /////////////////////////////////////////////////////////////////////////// 92 | // Private 93 | // 94 | 95 | var painter_ = painter, 96 | widget_ = widget, 97 | path_ = null, 98 | stack_ = []; 99 | 100 | var state_ = { 101 | fillStyle: new qt.QColor('black'), 102 | strokeStyle: new qt.QColor('black'), 103 | font: new qt.QFont('sans-serif', 10), 104 | matrix: new qt.QMatrix(1, 0, 0, 1, 0, 0) 105 | } 106 | 107 | function cloneState(state) { 108 | return { 109 | fillStyle: new qt.QColor(state.fillStyle), 110 | strokeStyle: new qt.QColor(state.strokeStyle), 111 | font: new qt.QFont(state.font), 112 | matrix: new qt.QMatrix(state.matrix) 113 | } 114 | } 115 | 116 | function updateWidget() { 117 | if (widget_) 118 | widget_.update(); 119 | } 120 | 121 | /////////////////////////////////////////////////////////////////////////// 122 | // Public 123 | // 124 | 125 | var obj = Object.create({ 126 | save: function() { 127 | // Can't push state_ directly as that would keep the state by reference 128 | // We need to push by value so that the saved state is not modified by ensuing state-changing operations 129 | stack_.push( cloneState(state_) ); 130 | }, 131 | 132 | restore: function() { 133 | if (stack_.length > 0) 134 | state_ = stack_.pop(); 135 | }, 136 | 137 | get fillStyle() { 138 | return getStyle(state_.fillStyle); 139 | }, 140 | 141 | //@ 142 | //@ #### ctx.fillStyle = 'color' 143 | //@ Currently only supports color types (e.g. `'rgb()'`, `'rgba()'`, `'blue'`, `'#aabbcc'`, etc) 144 | set fillStyle(val) { 145 | state_.fillStyle = setStyle(val) || state_.fillStyle; 146 | }, 147 | 148 | get strokeStyle() { 149 | return getStyle(state_.strokeStyle); 150 | }, 151 | 152 | //@ 153 | //@ #### ctx.strokeStyle = 'color' 154 | //@ Currently only supports color types (e.g. `'rgb()'`, `'rgba()'`, `'blue'`, `'#aabbcc'`, etc) 155 | set strokeStyle(val) { 156 | state_.strokeStyle = setStyle(val) || state_.strokeStyle; 157 | }, 158 | 159 | get font() { 160 | var font = state_.font; 161 | return font.pointSizeF() + 'px ' + font.family(); 162 | }, 163 | 164 | //@ 165 | //@ #### ctx.font = '10px family' 166 | //@ Currently only supports the format above 167 | set font(val) { 168 | var match = val.match(/([\d|\.]+)px\s+([\w|\-]+)/); 169 | if (!match) 170 | return; 171 | 172 | var size = match[1], 173 | family = match[2]; 174 | 175 | var font = new qt.QFont; 176 | font.setFamily(family); 177 | font.setPointSizeF(size); 178 | state_.font = font; 179 | }, 180 | 181 | //@ 182 | //@ #### ctx.fillRect(x, y, w, h) 183 | fillRect: function(x, y, w, h) { 184 | painter_.save(); 185 | // setMatrix() is undocumented in Qt. The syntax is the same as setTransform() 186 | painter_.setMatrix(state_.matrix, false); 187 | painter_.fillRect(x, y, w, h, state_.fillStyle); 188 | painter_.restore(); 189 | 190 | updateWidget(); 191 | }, 192 | 193 | //@ 194 | //@ #### ctx.fillText(text, x, y) 195 | fillText: function(text, x, y) { 196 | // TODO: check if spec supports other fillStyles 197 | var fillStyle = state_.fillStyle instanceof qt.QColor ? 198 | state_.fillStyle : new qt.QColor(0, 0, 0); 199 | 200 | painter_.save(); 201 | var pen = new qt.QPen(state_.fillStyle); 202 | painter_.setPen(pen); 203 | painter_.setFont(state_.font); 204 | // setMatrix() is undocumented in Qt. The syntax is the same as setTransform() 205 | painter_.setMatrix(state_.matrix, false); 206 | painter_.drawText(x, y, text); 207 | painter_.restore(); 208 | 209 | updateWidget(); 210 | }, 211 | 212 | //@ 213 | //@ #### ctx.translate(x, y) 214 | translate: function(x, y) { 215 | state_.matrix.translate(x, y); 216 | }, 217 | 218 | //@ 219 | //@ #### ctx.scale(x, y) 220 | scale: function(x, y) { 221 | state_.matrix.scale(x, y); 222 | }, 223 | 224 | //@ 225 | //@ #### ctx.beginPath() 226 | beginPath: function() { 227 | path_ = new qt.QPainterPath; 228 | }, 229 | 230 | //@ 231 | //@ #### ctx.closePath() 232 | closePath: function() { 233 | path_.closeSubpath(); 234 | }, 235 | 236 | //@ 237 | //@ #### ctx.moveTo(x, y) 238 | moveTo: function(x, y) { 239 | if (!path_) 240 | return; 241 | 242 | path_.moveTo(new qt.QPointF(x, y)); 243 | }, 244 | 245 | //@ 246 | //@ #### ctx.lineTo(x, y) 247 | lineTo: function(x, y) { 248 | if (!path_) 249 | return; 250 | 251 | path_.lineTo(new qt.QPointF(x, y)); 252 | }, 253 | 254 | //@ 255 | //@ #### ctx.stroke() 256 | stroke: function() { 257 | if (!path_) 258 | return; 259 | 260 | if (!(state_.strokeStyle instanceof qt.QColor)) 261 | return; 262 | 263 | var pen = new qt.QPen(state_.strokeStyle); 264 | 265 | painter_.save(); 266 | painter_.setMatrix(state_.matrix, false); 267 | painter_.strokePath(path_, pen); 268 | painter_.restore(); 269 | 270 | updateWidget(); 271 | }, 272 | 273 | //@ 274 | //@ #### ctx.drawImage(image, x, y) 275 | //@ Presently only supports images created via `five.Image()` 276 | drawImage: function(image, x, y) { 277 | var img = image.__image; 278 | if (!(img instanceof qt.QImage)) 279 | return; // not supported 280 | 281 | painter_.save(); 282 | painter_.setMatrix(state_.matrix, false); 283 | painter_.drawImage(x, y, img); 284 | painter_.restore(); 285 | 286 | updateWidget(); 287 | } 288 | }); // prototype 289 | 290 | /////////////////////////////////////////////////////////////////////////// 291 | // Constructor 292 | // 293 | 294 | return obj; 295 | } 296 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | // extend(target_obj, source_obj1 [, source_obj2 ...]) 31 | // shallow extend, e.g.: 32 | // common.extend({a:1}, {b:2}, {c:3}) 33 | // returns {a:1, b:2, c:3} 34 | exports.extend = function(target) { 35 | var sources = [].slice.call(arguments, 1); 36 | sources.forEach(function(source) { 37 | for (key in source) 38 | target[key] = source[key]; 39 | }); 40 | 41 | return target; 42 | } 43 | -------------------------------------------------------------------------------- /lib/core.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | //@ 31 | //@ ## five 32 | //@ 33 | //@ Main object. Typical usage is: 34 | //@ 35 | //@ ``` 36 | //@ var five = require('path-to-node-five-dir'); 37 | //@ ``` 38 | 39 | var qt = require('node-qt'); 40 | 41 | var app = new qt.QApplication(); 42 | var timerHandler, tickStop = false; 43 | 44 | //@ 45 | //@ #### useInterval() 46 | //@ Add event handler to Node's event loop via setTimeout(). This is the default event loop integration. 47 | exports.useInterval = function() { 48 | if (timerHandler) 49 | clearInterval(timerHandler); 50 | tickStop = true; 51 | 52 | timerHandler = setInterval(function(){ 53 | app.processEvents(); 54 | }, 0); 55 | } 56 | 57 | //@ 58 | //@ #### useTick() 59 | //@ Add event handler to Node's event loop via `process.nextTick()`. 60 | //@ This should used in applications that require more instant responsiveness (CPU-intensive!). 61 | exports.useTick = function() { 62 | var registerNextTick = function() { 63 | process.nextTick(function(){ 64 | app.processEvents(); 65 | if (!tickStop) registerNextTick(); 66 | }); 67 | }; 68 | 69 | if (timerHandler) 70 | clearInterval(timerHandler); 71 | tickStop = false; 72 | 73 | registerNextTick(); 74 | } 75 | 76 | //@ 77 | //@ #### stop() 78 | //@ Stop Node-Five's event loop. Applications never exit without a call to this method. 79 | exports.stop = function() { 80 | clearInterval(timerHandler); 81 | } 82 | -------------------------------------------------------------------------------- /lib/five.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var qt = require('node-qt'), 31 | common = require('./common'), 32 | five = require('./core'); 33 | 34 | // Expose key and button codes, e.g. five.Key_Left, five.LeftButton, etc 35 | common.extend(five, qt.Key); 36 | common.extend(five, qt.MouseButton); 37 | 38 | // 39 | // Expose classes 40 | // 41 | five.Window = require('./window'); 42 | five.Canvas = require('./canvas'); 43 | five.Image = require('./image'); 44 | five.AudioContext = require('./audiocontext'); 45 | 46 | // Integrate Qt events with Node's event loop 47 | five.useInterval(); 48 | 49 | module.exports = five; 50 | -------------------------------------------------------------------------------- /lib/image.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | //@ 31 | //@ ## five.Image() 32 | //@ 33 | //@ Constructor for image objects. Intended to mirror `Image()` constructor from browsers. 34 | 35 | var qt = require('node-qt'); 36 | 37 | // 38 | // Class Image() 39 | // 40 | module.exports = function() { 41 | 42 | /////////////////////////////////////////////////////////////////////////// 43 | // Private 44 | // 45 | 46 | var image_, 47 | onload_, 48 | complete_ = false; 49 | 50 | /////////////////////////////////////////////////////////////////////////// 51 | // Public 52 | // 53 | 54 | var obj = Object.create({ 55 | //@ 56 | //@ #### src = 'file_name' 57 | //@ Presently only supports local paths, e.g. `./images/file.png` 58 | set src(val) { 59 | image_ = new qt.QImage(val); 60 | if (!image_.isNull()) { 61 | complete_ = true; 62 | if (onload_) 63 | onload_(); 64 | } else { 65 | complete_ = false; 66 | } 67 | }, 68 | 69 | //@ 70 | //@ #### complete 71 | get complete() { 72 | return complete_; 73 | }, 74 | 75 | //@ 76 | //@ #### onload = callback 77 | set onload(val) { 78 | onload_ = val; 79 | if (complete_) 80 | onload_(); 81 | }, 82 | 83 | // TODO: find a better pattern to avoid leaking internals like image_ 84 | get __image() { 85 | return image_; 86 | } 87 | }); // prototype 88 | 89 | /////////////////////////////////////////////////////////////////////////// 90 | // Constructor 91 | // 92 | 93 | return obj; 94 | } 95 | -------------------------------------------------------------------------------- /lib/window.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | //@ 31 | //@ ## five.Window(width, height) 32 | //@ 33 | //@ Native window constructor with the given `height` and `width`. 34 | 35 | var qt = require('node-qt'); 36 | 37 | // Holds global references to objects that shouldn't get garbage collected during a Node-Five session 38 | // e.g. Windows, painting devices (Pixmaps), etc 39 | var refStore = []; 40 | 41 | // 42 | // Class Window() 43 | // 44 | module.exports = function(width, height) { 45 | 46 | /////////////////////////////////////////////////////////////////////////// 47 | // Private 48 | // 49 | 50 | var widget_, 51 | width_ = width || 640, 52 | height_ = height || 480, 53 | eventHandlers_ = { 54 | mousedown: [], 55 | mouseup: [], 56 | mousemove: [], 57 | keyup: [], 58 | keydown: [] 59 | }; 60 | 61 | /////////////////////////////////////////////////////////////////////////// 62 | // Public 63 | // 64 | 65 | var obj = Object.create({ 66 | //@ 67 | //@ #### width = 640 68 | //@ Gets/sets window width in pixels 69 | set width(val) { 70 | width_ = val; 71 | 72 | if (!widget_) 73 | return; 74 | widget_.resize(width_, height_); 75 | }, 76 | 77 | get width() { 78 | return width_; 79 | }, 80 | 81 | //@ 82 | //@ #### height = 480 83 | //@ Gets/sets window height in pixels 84 | set height(val) { 85 | height_ = val; 86 | 87 | if (!widget_) 88 | return; 89 | widget_.resize(width_, height_); 90 | }, 91 | 92 | get height() { 93 | return height_; 94 | }, 95 | 96 | //@ 97 | //@ #### close() 98 | //@ Closes window. It can't be reopened. 99 | close: function() { 100 | widget_.close(); 101 | }, 102 | 103 | //@ 104 | //@ #### addEventListener(event, callback) 105 | //@ Binds `event` to `callback`. Supported events are listed below. 106 | addEventListener: function(event, callback) { 107 | if (!event || !callback) 108 | return; 109 | 110 | eventHandlers_[event].push(callback); 111 | 112 | if (event === 'mousemove') 113 | widget_.setMouseTracking(true); 114 | }, 115 | 116 | //@ 117 | //@ #### removeEventListener(event, callback) 118 | //@ Removes callback from `event` handler. 119 | removeEventListener: function(event, callback) { 120 | if (!event || !callback) 121 | return; 122 | 123 | var arr = eventHandlers_[event], 124 | pos = arr.indexOf(callback); 125 | 126 | if (pos > -1) 127 | arr.splice(pos, 1); 128 | 129 | // No need for mouse tracking if no more handlers left 130 | if (event === 'mousemove' && arr.length === 0) 131 | widget_.setMouseTracking(false); 132 | }, 133 | 134 | // TODO: find a better window-canvas instantiation pattern to avoid leaking internals like widget 135 | get __widget() { 136 | return widget_; 137 | } 138 | }); 139 | 140 | /////////////////////////////////////////////////////////////////////////// 141 | // Constructor 142 | // 143 | 144 | // Prepare main widget 145 | widget_ = new qt.QWidget(); 146 | widget_.resize(width_, height_); 147 | widget_.show(); 148 | 149 | //@ 150 | //@ #### Supported events for `addEventListener()` 151 | //@ 152 | //@ + `mousedown`: 153 | //@ Callback will be passed `{ clientX, clientY, button }`. `button` values correspond 154 | //@ to the convenience constants `five.LeftButton`, `five.RightButton`, etc. 155 | //@ See http://developer.qt.nokia.com/doc/qt-4.8/qt.html#MouseButton-enum 156 | //@ for a list of supported button codes 157 | widget_.mousePressEvent(function(qtEvent) { 158 | eventHandlers_['mousedown'].forEach(function(handler) { 159 | handler({ 160 | clientX: qtEvent.x(), 161 | clientY: qtEvent.y(), 162 | button: qtEvent.button() 163 | }); 164 | }); 165 | }); 166 | 167 | //@ 168 | //@ + `mouseup`: 169 | //@ Callback will be passed `{ clientX, clientY, button }` as in `mousedown`. 170 | widget_.mouseReleaseEvent(function(qtEvent) { 171 | eventHandlers_['mouseup'].forEach(function(handler) { 172 | handler({ 173 | clientX: qtEvent.x(), 174 | clientY: qtEvent.y(), 175 | button: qtEvent.button() 176 | }); 177 | }); 178 | }); 179 | 180 | //@ 181 | //@ + `mousemove`: 182 | //@ Callback will be passed `{ clientX, clientY }`. 183 | widget_.mouseMoveEvent(function(qtEvent) { 184 | eventHandlers_['mousemove'].forEach(function(handler) { 185 | handler({ 186 | clientX: qtEvent.x(), 187 | clientY: qtEvent.y() 188 | }); 189 | }); 190 | }); 191 | 192 | //@ 193 | //@ + `keydown`: 194 | //@ Callback will be passed `{ key, char }`. 195 | //@ Key values correspond to the convenience constants `five.Key_Esc`, `five.Key_Left`, etc. 196 | //@ See http://developer.qt.nokia.com/doc/qt-4.8/qt.html#Key-enum for the list of supported keys. 197 | //@ `char` is the corresponding Unicode character, if available. 198 | widget_.keyPressEvent(function(qtEvent) { 199 | eventHandlers_['keydown'].forEach(function(handler) { 200 | handler({ 201 | key: qtEvent.key(), 202 | char: qtEvent.text() 203 | }); 204 | }); 205 | }); 206 | 207 | //@ 208 | //@ + `keyup`: 209 | //@ Callback will be passed `{ key, char }`. Details as in `keydown`. 210 | widget_.keyReleaseEvent(function(qtEvent) { 211 | eventHandlers_['keyup'].forEach(function(handler) { 212 | handler({ 213 | key: qtEvent.key(), 214 | char: qtEvent.text() 215 | }); 216 | }); 217 | }); 218 | 219 | refStore.push(obj); 220 | return obj; 221 | } 222 | -------------------------------------------------------------------------------- /make.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('shelljs/make'); 3 | 4 | var root = __dirname; 5 | 6 | // 7 | // Docs 8 | // 9 | target.docs = function() { 10 | cd(root); 11 | 12 | echo('_________________________________________________________________'); 13 | echo('Building docs'); 14 | 15 | var apiDocs = grep('//@', 16 | 'lib/core.js', 17 | 'lib/window.js', 18 | 'lib/canvas.js', 19 | 'lib/canvascontext2d.js', 20 | 'lib/image.js', 21 | 'lib/audiocontext.js', 22 | 'lib/audiobuffersource.js' 23 | ).replace(/ *\/\/\@ */g, ''); 24 | 25 | sed(/# API reference(.|\n)*/, '# API reference\n\n' + apiDocs, 'README.md').to('README.md'); 26 | } 27 | 28 | 29 | // 30 | // Test 31 | // 32 | target.test = function() { 33 | cd(root); 34 | 35 | echo('_________________________________________________________________'); 36 | echo('Running Node-Five tests'); 37 | echo(); 38 | 39 | cd('test'); 40 | rm('-f', 'img-test/*'); 41 | ls('*.js').forEach(function(f) { 42 | echo('Running test file '+f); 43 | exec('node '+f); 44 | }); 45 | } 46 | 47 | 48 | // 49 | // Ref 50 | // 51 | target.ref = function() { 52 | cd(root); 53 | 54 | echo('_________________________________________________________________'); 55 | echo('Node-Five tests: Overwriting reference images'); 56 | rm('-f', 'img-ref/*'); 57 | mv('img-test/*', 'img-ref'); 58 | } 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name": "node-five" 2 | , "version": "0.0.1" 3 | , "author": "Artur Adib" 4 | , "description": "HTML5 APIs for Node.js" 5 | , "homepage": "http://github.com/arturadib/node-five" 6 | , "main": "./lib/five.js" 7 | , "dependencies": { 8 | "shelljs": "0.0.5pre4", 9 | "node-qt": "0.0.2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/screenshot.png -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | img-test/ 2 | 3 | -------------------------------------------------------------------------------- /test/audio.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var assert = require('assert'), 31 | five = require('..'); 32 | 33 | // Constructor 34 | { 35 | var context = new five.AudioContext(); 36 | assert.ok(context); 37 | 38 | var source = context.createBufferSource(); 39 | assert.ok(source); 40 | 41 | // crash test only 42 | source.connect(context.destination); 43 | } 44 | 45 | five.stop(); 46 | -------------------------------------------------------------------------------- /test/canvas.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var assert = require('assert'), 31 | five = require('..'), 32 | test = require('./test'); 33 | 34 | // Constructor: window 35 | { 36 | var window = new five.Window(111, 222), 37 | canvas = new five.Canvas(window); 38 | 39 | assert.equal(canvas.width, 300); // spec default 40 | assert.equal(canvas.height, 150); // spec default 41 | assert.equal(canvas.top, 0); 42 | assert.equal(canvas.left, 0); 43 | assert.equal(typeof canvas.viewWidth, 'undefined'); 44 | assert.equal(typeof canvas.viewHeight, 'undefined'); 45 | } 46 | 47 | // Constructor- headless 48 | { 49 | var canvas = new five.Canvas; 50 | 51 | assert.equal(canvas.width, 300); // spec default 52 | assert.equal(canvas.height, 150); // spec default 53 | } 54 | 55 | // getContext() 56 | { 57 | var canvas = new five.Canvas; 58 | assert.ok(canvas.getContext('2d')); 59 | } 60 | 61 | // Regression tests 62 | { 63 | var canvas = new five.Canvas; 64 | 65 | test.regression('canvas-blank', canvas, function() {}); 66 | } 67 | 68 | five.stop(); 69 | -------------------------------------------------------------------------------- /test/canvascontext2d.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var assert = require('assert'), 31 | five = require('..'), 32 | test = require('./test'); 33 | 34 | function checkDefaults(ctx) { 35 | assert.equal(ctx.fillStyle, '#000000'); 36 | assert.equal(ctx.strokeStyle, '#000000'); 37 | assert.equal(ctx.font, '10px sans-serif'); 38 | } 39 | 40 | // Getters, setters 41 | { 42 | var canvas = new five.Canvas, 43 | ctx = canvas.getContext('2d'); 44 | 45 | checkDefaults(ctx); 46 | 47 | ctx.save(); 48 | 49 | // 50 | // fillStyle, strokeStyle 51 | // 52 | 53 | ctx.fillStyle = ' #030201 '; 54 | assert.equal(ctx.fillStyle, '#030201'); 55 | ctx.strokeStyle = ' #030201 '; 56 | assert.equal(ctx.strokeStyle, '#030201'); 57 | 58 | ctx.fillStyle = ' blue '; 59 | assert.equal(ctx.fillStyle, '#0000ff'); 60 | ctx.strokeStyle = ' blue '; 61 | assert.equal(ctx.strokeStyle, '#0000ff'); 62 | 63 | ctx.fillStyle = ' rgb ( 1,2, 3) '; 64 | assert.equal(ctx.fillStyle, '#010203'); 65 | ctx.strokeStyle = ' rgb ( 1,2, 3) '; 66 | assert.equal(ctx.strokeStyle, '#010203'); 67 | 68 | ctx.fillStyle = ' rgba ( 3,1, 2, 1.0) '; 69 | assert.equal(ctx.fillStyle, '#030102'); 70 | ctx.strokeStyle = ' rgba ( 3,1, 2, 1.0) '; 71 | assert.equal(ctx.strokeStyle, '#030102'); 72 | 73 | ctx.fillStyle = ' rgba ( 2,3, 1, 0.2311111) '; 74 | // FF seems to use 2 decimal points precision 75 | assert.equal(ctx.fillStyle, 'rgba(2, 3, 1, 0.23)'); 76 | ctx.strokeStyle = ' rgba ( 2,3, 1, 0.2311111) '; 77 | // FF seems to use 2 decimal points precision 78 | assert.equal(ctx.strokeStyle, 'rgba(2, 3, 1, 0.23)'); 79 | 80 | // 81 | // Font 82 | // 83 | 84 | ctx.font = '15px helvetica'; 85 | assert.equal(ctx.font, '15px helvetica'); 86 | 87 | ctx.font = ' 15px helvetica '; 88 | assert.equal(ctx.font, '15px helvetica'); 89 | 90 | ctx.font = ' 1px sans-serif '; 91 | assert.equal(ctx.font, '1px sans-serif'); 92 | 93 | ctx.font = '5px arial'; 94 | ctx.font = ' 1pxx sans-serif-- '; // induce parse error 95 | assert.equal(ctx.font, '5px arial'); 96 | 97 | // Defaults 98 | ctx.restore(); 99 | 100 | checkDefaults(ctx); 101 | } 102 | 103 | // Regression tests 104 | { 105 | var canvas = new five.Canvas, 106 | ctx = canvas.getContext('2d'); 107 | 108 | test.regression('context2d-fillRect-black-sq', canvas, function() { 109 | ctx.fillRect(0, 0, 10, 10); 110 | }); 111 | } 112 | 113 | { 114 | var canvas = new five.Canvas, 115 | ctx = canvas.getContext('2d'); 116 | 117 | test.regression('context2d-fillRect-black-sq2', canvas, function() { 118 | ctx.fillStyle = 'red'; 119 | ctx.fillRect(10, 10, 20, 20); 120 | 121 | canvas.width = canvas.width; // reset canvas pixmap, as per spec 122 | 123 | ctx.fillStyle = 'black'; 124 | ctx.fillRect(0, 0, 10, 10); // should be only square 125 | }); 126 | } 127 | 128 | { 129 | var canvas = new five.Canvas, 130 | ctx = canvas.getContext('2d'); 131 | 132 | test.regression('context2d-fillRect-red-sq', canvas, function() { 133 | ctx.fillStyle = 'red'; 134 | ctx.fillRect(0, 0, 10, 10); 135 | }); 136 | } 137 | 138 | { 139 | var canvas = new five.Canvas, 140 | ctx = canvas.getContext('2d'); 141 | 142 | test.regression('context2d-fillRect-blue-sq', canvas, function() { 143 | ctx.fillStyle = '#0000ff'; 144 | ctx.fillRect(0, 0, 10, 10); 145 | }); 146 | } 147 | 148 | { 149 | var canvas = new five.Canvas, 150 | ctx = canvas.getContext('2d'); 151 | 152 | test.regression('context2d-fillText-hello-black', canvas, function() { 153 | ctx.fillText('hello', 20, 20); 154 | }); 155 | } 156 | 157 | { 158 | var canvas = new five.Canvas, 159 | ctx = canvas.getContext('2d'); 160 | 161 | test.regression('context2d-fillText-hello-green', canvas, function() { 162 | ctx.fillStyle = 'green'; 163 | ctx.fillText('hello', 20, 20); 164 | }); 165 | } 166 | 167 | { 168 | var canvas = new five.Canvas, 169 | ctx = canvas.getContext('2d'); 170 | 171 | test.regression('context2d-fillText-hello-big-arial', canvas, function() { 172 | ctx.font = '40px arial'; 173 | ctx.fillText('hello', 20, 50); 174 | }); 175 | } 176 | 177 | { 178 | var canvas = new five.Canvas, 179 | ctx = canvas.getContext('2d'); 180 | 181 | test.regression('context2d-fillText-hello-big-courier', canvas, function() { 182 | ctx.font = '40px courier'; 183 | ctx.fillText('hello', 20, 50); 184 | }); 185 | } 186 | 187 | { 188 | var canvas = new five.Canvas, 189 | ctx = canvas.getContext('2d'); 190 | 191 | test.regression('context2d-fillRect-many-rgb-colored', canvas, function() { 192 | var i, j; 193 | for (i=0;i<6;i++) { 194 | for (j=0;j<6;j++) { 195 | ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + 196 | Math.floor(255-42.5*j) + ',0)'; 197 | ctx.fillRect(j*25,i*25,25,25); 198 | } 199 | } 200 | }); 201 | } 202 | 203 | { 204 | var canvas = new five.Canvas, 205 | ctx = canvas.getContext('2d'); 206 | 207 | test.regression('context2d-fillRect-translate-redonblue', canvas, function() { 208 | ctx.save(); 209 | ctx.fillStyle = 'blue'; 210 | ctx.fillRect(0, 0, 20, 20); 211 | ctx.restore(); 212 | 213 | ctx.save(); 214 | ctx.fillStyle = 'red'; 215 | ctx.translate(10, 10); 216 | ctx.fillRect(0, 0, 20, 20); 217 | ctx.restore(); 218 | }); 219 | } 220 | 221 | { 222 | var canvas = new five.Canvas, 223 | ctx = canvas.getContext('2d'); 224 | 225 | test.regression('context2d-fillRect-scale-redonblue', canvas, function() { 226 | ctx.save(); 227 | ctx.fillStyle = 'blue'; 228 | ctx.fillRect(0, 0, 20, 20); 229 | ctx.restore(); 230 | 231 | ctx.save(); 232 | ctx.fillStyle = 'red'; 233 | ctx.translate(10, 10); 234 | ctx.scale(2, 2); 235 | ctx.fillRect(0, 0, 20, 20); 236 | ctx.restore(); 237 | }); 238 | } 239 | 240 | { 241 | var canvas = new five.Canvas, 242 | ctx = canvas.getContext('2d'); 243 | 244 | test.regression('context2d-fillText-translate-redonblue', canvas, function() { 245 | ctx.save(); 246 | ctx.fillStyle = 'blue'; 247 | ctx.fillText('hello world', 0, 20); 248 | ctx.restore(); 249 | 250 | ctx.save(); 251 | ctx.fillStyle = 'red'; 252 | ctx.translate(10, 10); 253 | ctx.fillText('hello world', 0, 20); 254 | ctx.restore(); 255 | }); 256 | } 257 | 258 | { 259 | var canvas = new five.Canvas, 260 | ctx = canvas.getContext('2d'); 261 | 262 | test.regression('context2d-fillText-scale-redonblue', canvas, function() { 263 | ctx.save(); 264 | ctx.fillStyle = 'blue'; 265 | ctx.fillText('hello world', 0, 20); 266 | ctx.restore(); 267 | 268 | ctx.save(); 269 | ctx.fillStyle = 'red'; 270 | ctx.scale(2, 2); 271 | ctx.fillText('hello world', 0, 20); 272 | ctx.restore(); 273 | }); 274 | } 275 | 276 | { 277 | var canvas = new five.Canvas, 278 | ctx = canvas.getContext('2d'); 279 | 280 | test.regression('context2d-strokePath-lineTo', canvas, function() { 281 | ctx.beginPath(); 282 | ctx.strokeStyle = 'red'; 283 | ctx.moveTo(10, 10); 284 | ctx.lineTo(30, 30); 285 | ctx.stroke(); 286 | }); 287 | } 288 | 289 | { 290 | var canvas = new five.Canvas, 291 | ctx = canvas.getContext('2d'); 292 | 293 | test.regression('context2d-strokePath-lineTo-translate-blueonright', canvas, function() { 294 | ctx.beginPath(); 295 | ctx.strokeStyle = 'red'; 296 | ctx.moveTo(10, 10); 297 | ctx.lineTo(30, 30); 298 | ctx.stroke(); 299 | ctx.closePath(); 300 | 301 | ctx.beginPath(); 302 | ctx.strokeStyle = 'blue'; 303 | ctx.translate(10, 0); 304 | ctx.moveTo(10, 10); 305 | ctx.lineTo(30, 30); 306 | ctx.stroke(); 307 | }); 308 | } 309 | 310 | { 311 | var canvas = new five.Canvas, 312 | ctx = canvas.getContext('2d'); 313 | 314 | test.regression('context2d-drawImage', canvas, function() { 315 | var image = new five.Image(); 316 | image.src = 'resources/image.png'; 317 | assert.equal(image.complete, true); 318 | ctx.drawImage(image, 0, 0); 319 | }); 320 | } 321 | 322 | { 323 | var canvas = new five.Canvas, 324 | ctx = canvas.getContext('2d'); 325 | 326 | test.regression('context2d-drawImage-translate-imageontop', canvas, function() { 327 | ctx.fillStyle = 'blue'; 328 | ctx.fillRect(0, 0, 20, 20); 329 | 330 | var image = new five.Image(); 331 | image.src = 'resources/image.png'; 332 | assert.equal(image.complete, true); 333 | 334 | ctx.translate(10, 10); 335 | ctx.drawImage(image, 0, 0); 336 | }); 337 | } 338 | 339 | { 340 | var canvas = new five.Canvas, 341 | ctx = canvas.getContext('2d'); 342 | 343 | test.regression('context2d-drawImage-scale-mirror', canvas, function() { 344 | var image = new five.Image(); 345 | image.src = 'resources/image.png'; 346 | assert.equal(image.complete, true); 347 | 348 | ctx.drawImage(image, 0, 0); 349 | 350 | ctx.translate(200, 0); 351 | ctx.scale(-1, 1); 352 | ctx.drawImage(image, 0, 0); 353 | }); 354 | } 355 | 356 | { 357 | var canvas = new five.Canvas, 358 | ctx = canvas.getContext('2d'); 359 | 360 | test.regression('context2d-drawImage-save-restore-matrix', canvas, function() { 361 | var image = new five.Image(); 362 | image.src = 'resources/image.png'; 363 | assert.equal(image.complete, true); 364 | 365 | ctx.save(); 366 | ctx.translate(200, 0); 367 | ctx.scale(-1, 1); 368 | ctx.restore(); 369 | 370 | // should draw original picture at 0, 0 371 | ctx.drawImage(image, 0, 0); 372 | }); 373 | } 374 | 375 | five.stop(); 376 | -------------------------------------------------------------------------------- /test/image.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var assert = require('assert'), 31 | five = require('..'), 32 | test = require('./test'); 33 | 34 | // Constructor 35 | { 36 | var image = new five.Image(); 37 | image.src = 'resources/image.png'; 38 | assert.equal(image.complete, true); 39 | } 40 | 41 | // Constructor- bad file 42 | { 43 | var image = new five.Image(); 44 | image.src = 'BAD-FILE'; 45 | assert.equal(image.complete, false); 46 | } 47 | 48 | // .onload() : before .src 49 | { 50 | var called = false; 51 | var image = new five.Image(); 52 | image.onload = function() { called = true; } 53 | image.src = 'resources/image.png'; 54 | assert.equal(called, true); 55 | } 56 | 57 | // .onload() : after .src 58 | { 59 | var called = false; 60 | var image = new five.Image(); 61 | image.src = 'resources/image.png'; 62 | image.onload = function() { called = true; } 63 | assert.equal(called, true); 64 | } 65 | 66 | // .onload() : bad image before .src 67 | { 68 | var called = false; 69 | var image = new five.Image(); 70 | image.onload = function() { called = true; } 71 | image.src = 'BAD-IMAGE'; 72 | assert.equal(called, false); 73 | } 74 | 75 | // .onload() : bad image after .src 76 | { 77 | var called = false; 78 | var image = new five.Image(); 79 | image.src = 'BAD-IMAGE'; 80 | image.onload = function() { called = true; } 81 | assert.equal(called, false); 82 | } 83 | 84 | five.stop(); 85 | -------------------------------------------------------------------------------- /test/img-ref/canvas-blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/canvas-blank.png -------------------------------------------------------------------------------- /test/img-ref/context2d-drawImage-save-restore-matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-drawImage-save-restore-matrix.png -------------------------------------------------------------------------------- /test/img-ref/context2d-drawImage-scale-mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-drawImage-scale-mirror.png -------------------------------------------------------------------------------- /test/img-ref/context2d-drawImage-translate-imageontop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-drawImage-translate-imageontop.png -------------------------------------------------------------------------------- /test/img-ref/context2d-drawImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-drawImage.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-black-sq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-black-sq.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-black-sq2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-black-sq2.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-blue-sq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-blue-sq.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-many-rgb-colored.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-many-rgb-colored.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-red-sq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-red-sq.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-scale-redonblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-scale-redonblue.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillRect-translate-redonblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillRect-translate-redonblue.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillText-hello-big-arial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillText-hello-big-arial.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillText-hello-big-courier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillText-hello-big-courier.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillText-hello-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillText-hello-black.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillText-hello-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillText-hello-green.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillText-scale-redonblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillText-scale-redonblue.png -------------------------------------------------------------------------------- /test/img-ref/context2d-fillText-translate-redonblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-fillText-translate-redonblue.png -------------------------------------------------------------------------------- /test/img-ref/context2d-strokePath-lineTo-translate-blueonright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-strokePath-lineTo-translate-blueonright.png -------------------------------------------------------------------------------- /test/img-ref/context2d-strokePath-lineTo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/img-ref/context2d-strokePath-lineTo.png -------------------------------------------------------------------------------- /test/resources/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturadib/node-five/ea642fcef0d95d3df69fe0b974b80ccb95e25b47/test/resources/image.png -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var fs = require('fs'), 31 | path = require('path'); 32 | 33 | var testDir = __dirname+'/img-test/', 34 | refDir = __dirname+'/img-ref/'; 35 | 36 | if (!path.existsSync(testDir)) { 37 | console.log('! regression warning: img-test/ dir does not exist. creating it...') 38 | fs.mkdirSync(testDir); 39 | } 40 | 41 | function saveImageFromUrl(url, file) { 42 | var buf = new Buffer(url.substr(url.search(',') + 1), 'base64'); 43 | fs.writeFileSync(file, buf); 44 | } 45 | 46 | function readUrlFromImage(file) { 47 | var buf = fs.readFileSync(file); 48 | return 'data:image/png;base64,' + buf.toString('base64'); 49 | } 50 | 51 | exports.regression = function(name, canvas, callback) { 52 | callback(); 53 | 54 | saveImageFromUrl(canvas.toDataURL(), testDir+name+'.png'); 55 | 56 | // Can't compare if refs don't exist 57 | if (!path.existsSync(refDir+name+'.png')) { 58 | console.log('! regression warning: could not find reference file for test:', name) 59 | return; 60 | } 61 | 62 | var testUrl = readUrlFromImage(testDir+name+'.png'); 63 | var refUrl = readUrlFromImage(refDir+name+'.png'); 64 | if (testUrl !== refUrl) { 65 | console.log('!!! regression error in test:', name); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/window.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Mozilla Corporation 2 | // All rights reserved. 3 | // 4 | // Author(s): Artur Adib 5 | // 6 | // You may use this file under the terms of the New BSD license as follows: 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of Mozilla Corporation nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | // ARE DISCLAIMED. IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | var assert = require('assert'), 31 | five = require('..'); 32 | 33 | // Constructor 34 | { 35 | // Defaults 36 | var window = new five.Window; 37 | assert.equal(window.width, 640); 38 | assert.equal(window.height, 480); 39 | 40 | var window2 = new five.Window(100, 200); 41 | assert.equal(window2.width, 100); 42 | assert.equal(window2.height, 200); 43 | 44 | // Sanity check (prototype/private var leaks, etc) 45 | assert.equal(window.width, 640); 46 | assert.equal(window.height, 480); 47 | } 48 | 49 | five.stop(); 50 | --------------------------------------------------------------------------------