1 // ps2.1.js for Perlenspiel 2.1
4 Perlenspiel is a scheme by Professor Moriarty (bmoriarty@wpi.edu).
5 Perlenspiel is Copyright © 2009-12 Worcester Polytechnic Institute.
6 This file is part of Perlenspiel.
8 Perlenspiel is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 Perlenspiel 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 Lesser General Public License for more details.
18 You may have received a copy of the GNU Lesser General Public License
19 along with Perlenspiel. If not, see <http://www.gnu.org/licenses/>.
22 // The following comments are for JSLint
24 /*global document, window, Audio, Image */
26 // Global namespace variable
33 DEFAULT
: -1, // use default value
34 CURRENT
: -2, // use current value
35 ALL
: -3, // Use all rows or columns
36 ERROR
: "*ERROR*", // generic error return value
37 CANVAS_SIZE
: 480, // max width/height of canvas
38 GRID_MAX
: 32, // max x/y dimensions of grid
39 GRID_DEFAULT_WIDTH
: 8,
40 GRID_DEFAULT_HEIGHT
: 8,
41 DEFAULT_BEAD_COLOR
: 0x000000,
42 DEFAULT_BEAD_RED
: 0x00,
43 DEFAULT_BEAD_GREEN
: 0x00,
44 DEFAULT_BEAD_BLUE
: 0x00,
45 DEFAULT_BG_COLOR
: 0xFFFFFF,
47 DEFAULT_BG_GREEN
: 0xFF,
48 DEFAULT_BG_BLUE
: 0xFF,
49 DEFAULT_BORDER_COLOR
: 0x808080,
50 DEFAULT_BORDER_RED
: 0x80,
51 DEFAULT_BORDER_GREEN
: 0x80,
52 DEFAULT_BORDER_BLUE
: 0x80,
53 DEFAULT_BORDER_WIDTH
: 1,
55 DEFAULT_GLYPH_COLOR
: 0xFFFFFF,
56 DEFAULT_GLYPH_RED
: 0xFF,
57 DEFAULT_GLYPH_GREEN
: 0xFF,
58 DEFAULT_GLYPH_BLUE
: 0xFF,
59 DEFAULT_FLASH_COLOR
: 0xFFFFFF,
60 DEFAULT_FLASH_RED
: 0xFF,
61 DEFAULT_FLASH_GREEN
: 0xFF,
62 DEFAULT_FLASH_BLUE
: 0xFF,
63 DEFAULT_ALPHA
: 100, // must be between 0 and 100
64 DEFAULT_VOLUME
: 1.0, // must be between 0 and 1.0
66 DEFAULT_FPS
: 10, // frame rate in milliseconds (1/100 sec)
67 REDSHIFT
: 256 * 256, // used to decode rgb
68 FLASH_STEP
: 10, // percent for each flash
69 STATUS_FLASH_STEP
: 5, // percent for each step
70 FLASH_INTERVAL
: 5, // number of ticks per flash step
71 DEFAULT_TEXT_COLOR
: 0x000000,
75 COLOR_BLACK
: 0x000000,
76 COLOR_WHITE
: 0xFFFFFF,
77 COLOR_GRAY_LIGHT
: 0xC0C0C0,
79 COLOR_GRAY_DARK
: 0x404040,
81 COLOR_ORANGE
: 0xFF8000,
82 COLOR_YELLOW
: 0xFFFF00,
83 COLOR_GREEN
: 0x00FF00,
85 COLOR_INDIGO
: 0x4000FF,
86 COLOR_VIOLET
: 0x8000FF,
87 COLOR_MAGENTA
: 0xFF00FF,
90 // Key and mouse wheel constants
119 Grid
: null, // main grid
120 DebugWindow
: null, // debugger window
121 ImageCanvas
: null, // offscreen canvas for image manipulation
123 // Coordinates of current and previous beads, -1 if none
130 // Delay and clock settings
138 Status
: "Perlenspiel",
139 StatusHue
: 0, // target hue
143 StatusPhase
: 0, // 100: done fading
147 // Improved typeof that distinguishes arrays
149 PS
.TypeOf = function (value
)
155 if ( s
=== "object" )
159 if ( value
instanceof Array
)
172 // Get the canvas context
174 PS
.Context = function ()
180 cv
= document
.getElementById("screen");
181 if ( cv
&& cv
.getContext
)
183 ctx
= cv
.getContext("2d");
189 // Takes a multiplexed rgb value and a function name
190 // Returns floored rgb value, or -1 if invalid
192 PS
.ValidRGB = function ( rgb
, fn
)
196 if ( typeof rgb
!== "number" )
198 PS
.Oops( fn
+ "rgb parameter not a number" );
201 rgb
= Math
.floor(rgb
);
204 PS
.Oops( fn
+ "rgb parameter negative" );
207 if ( rgb
> 0xFFFFFF )
209 PS
.Oops( fn
+ "rgb parameter out of range" );
215 // Construct a color string with optional alpha
217 PS
.RGBString = function (r
, g
, b
, a
)
222 if ( a
=== undefined )
224 str
= "rgb(" + r
+ "," + g
+ "," + b
+ ")";
228 str
= "rgba(" + r
+ "," + g
+ "," + b
+ "," + a
+ ")";
233 // Takes a multiplexed rgb value and creates an object with
234 // separate r, g and b values, or null if error
236 PS
.UnmakeRGB = function ( rgb
)
239 var fn
, red
, green
, blue
, rval
, gval
;
241 fn
= "[PS.DecodeRGB] ";
243 if ( typeof rgb
!== "number" )
245 PS
.Oops(fn
+ "RGB parameter not a number");
248 rgb
= Math
.floor(rgb
);
251 PS
.Oops(fn
+ "RGB parameter negative");
254 if ( rgb
> 0xFFFFFF )
256 PS
.Oops(fn
+ "RGB parameter out of range");
260 red
= rgb
/ PS
.REDSHIFT
;
261 red
= Math
.floor(red
);
262 rval
= red
* PS
.REDSHIFT
;
264 green
= (rgb
- rval
) / 256;
265 green
= Math
.floor(green
);
268 blue
= rgb
- rval
- gval
;
270 return { r
: red
, g
: green
, b
: blue
};
274 // Returns a color that is x% between c1 and c2
276 PS
.Dissolve = function ( c1
, c2
, x
)
284 delta
= ( x
* delta
) / 100;
285 delta
= Math
.floor(delta
);
286 return ( c1
- delta
);
291 delta
= ( x
* delta
) / 100;
292 delta
= Math
.floor(delta
);
293 return ( c1
+ delta
);
299 PS
.InitBead = function (xpos
, ypos
, size
, bgcolor
)
307 bead
.right
= xpos
+ size
;
309 bead
.bottom
= ypos
+ size
;
313 bead
.visible
= true; // bead visible?
317 bead
.dirty
= false; // bead color touched?
321 bead
.red
= PS
.DEFAULT_BEAD_RED
;
322 bead
.green
= PS
.DEFAULT_BEAD_GREEN
;
323 bead
.blue
= PS
.DEFAULT_BEAD_BLUE
;
324 bead
.color
= PS
.RGBString (bead
.red
, bead
.green
, bead
.blue
); // default color
325 bead
.colorNow
= bead
.color
; // actual color while drawing
327 // pre-calculated alpha colors
329 bead
.alpha
= PS
.DEFAULT_ALPHA
;
330 bead
.alphaRed
= PS
.DEFAULT_BEAD_RED
;
331 bead
.alphaGreen
= PS
.DEFAULT_BEAD_GREEN
;
332 bead
.alphaBlue
= PS
.DEFAULT_BEAD_BLUE
;
336 bead
.glyph
= 0; // glyph code (zero if none)
337 bead
.glyphStr
= ""; // actual string to print
338 bead
.glyphRed
= PS
.DEFAULT_GLYPH_RED
;
339 bead
.glyphGreen
= PS
.DEFAULT_GLYPH_GREEN
;
340 bead
.glyphBlue
= PS
.DEFAULT_GLYPH_BLUE
;
341 bead
.glyphColor
= PS
.RGBString (PS
.DEFAULT_GLYPH_RED
, PS
.DEFAULT_GLYPH_GREEN
, PS
.DEFAULT_GLYPH_BLUE
);
345 bead
.flash
= true; // flashing enabled?
346 bead
.flashPhase
= 0; // phase of flash animation
347 bead
.flashRed
= PS
.DEFAULT_FLASH_RED
;
348 bead
.flashGreen
= PS
.DEFAULT_FLASH_GREEN
;
349 bead
.flashBlue
= PS
.DEFAULT_FLASH_BLUE
;
350 bead
.flashColor
= PS
.RGBString (PS
.DEFAULT_FLASH_RED
, PS
.DEFAULT_FLASH_GREEN
, PS
.DEFAULT_FLASH_BLUE
);
354 bead
.borderWidth
= PS
.DEFAULT_BORDER_WIDTH
; // border width; 0 if none
355 bead
.borderRed
= PS
.DEFAULT_BORDER_RED
;
356 bead
.borderGreen
= PS
.DEFAULT_BORDER_GREEN
;
357 bead
.borderBlue
= PS
.DEFAULT_BORDER_BLUE
;
358 bead
.borderAlpha
= PS
.DEFAULT_ALPHA
; // border alpha
359 bead
.borderColor
= PS
.RGBString (PS
.DEFAULT_BORDER_RED
, PS
.DEFAULT_BORDER_GREEN
, PS
.DEFAULT_BORDER_BLUE
);
361 // data, sound, exec params
363 bead
.data
= 0; // data value
365 bead
.audio
= null; // sound (null = none)
366 bead
.volume
= PS
.DEFAULT_VOLUME
; // volume
367 bead
.loop
= PS
.DEFAULT_LOOP
; // loop flag
369 bead
.exec
= null; // on-click function (null = none)
371 // give each bead its own offscreen canvas and context
373 bead
.off
= document
.createElement("canvas");
374 bead
.off
.width
= size
;
375 bead
.off
.height
= size
;
376 bead
.off
.backgroundColor
= bgcolor
;
377 bead
.offContext
= bead
.off
.getContext("2d");
379 // set up font info for offscreen context
381 bead
.offContext
.font
= Math
.floor(size
/ 2) + "pt sans-serif";
382 bead
.offContext
.textAlign
= "center";
383 bead
.offContext
.textBaseline
= "middle";
388 // Draws bead [bead] in (optional) context [ctx]
390 PS
.DrawBead = function (bead
, ctx
)
393 var offctx
, left
, top
, size
, width
;
395 // get destination context if not provided
397 if ( ctx
=== undefined )
406 offctx
= bead
.offContext
; // the offscreen canvas context
408 // draw border if needed
410 width
= bead
.borderWidth
;
413 offctx
.fillStyle
= bead
.borderColor
;
414 offctx
.fillRect(0, 0, size
, size
);
416 // adjust position and size of bead rect
420 size
-= (width
+ width
);
423 // draw bead body if dirty (has had color explicitly set)
427 offctx
.fillStyle
= bead
.colorNow
;
430 // otherwise fill with background color
434 offctx
.fillStyle
= PS
.Grid
.bgColor
;
437 offctx
.fillRect(left
, top
, size
, size
);
439 if ( bead
.glyph
> 0 )
441 offctx
.fillStyle
= bead
.glyphColor
;
442 offctx
.fillText (bead
.glyphStr
, PS
.Grid
.glyphX
, PS
.Grid
.glyphY
);
445 // blit offscreen canvas to main canvas
447 ctx
.drawImage(bead
.off
, bead
.left
, bead
.top
);
450 // Erase bead [bead] in (optional) context [ctx]
452 PS
.EraseBead = function (bead
, ctx
)
455 var size
, left
, top
, width
;
457 // get destination context if not provided
459 if ( ctx
=== undefined )
468 // draw border if needed
470 width
= bead
.borderWidth
;
473 ctx
.fillStyle
= bead
.borderColor
;
474 ctx
.fillRect(left
, top
, size
, size
);
476 // adjust position and size of bead rect
480 size
-= (width
+ width
);
483 ctx
.fillStyle
= PS
.Grid
.bgColor
;
484 ctx
.fillRect(left
, top
, size
, size
);
488 // Call with x/y dimensions of grid
489 // Returns initialized grid object or null if error
491 PS
.InitGrid = function (x
, y
)
494 var grid
, i
, j
, size
, xpos
, ypos
;
498 grid
.x
= x
; // x dimensions of grid
499 grid
.y
= y
; // y dimensions of grid
500 grid
.count
= x
* y
; // number of beads in grid
502 // calc size of beads, position/dimensions of centered grid on canvas
506 grid
.beadSize
= size
= Math
.floor(PS
.CANVAS_SIZE
/ x
);
507 grid
.width
= size
* x
;
508 grid
.height
= size
* y
;
513 grid
.beadSize
= size
= Math
.floor(PS
.CANVAS_SIZE
/ y
);
514 grid
.width
= size
* x
;
515 grid
.height
= size
* y
;
516 grid
.left
= Math
.floor( (PS
.CANVAS_SIZE
- grid
.width
) / 2 );
521 grid
.right
= grid
.left
+ grid
.width
;
522 grid
.bottom
= grid
.top
+ grid
.height
;
524 grid
.bgRed
= PS
.DEFAULT_BG_RED
;
525 grid
.bgGreen
= PS
.DEFAULT_BG_GREEN
;
526 grid
.bgBlue
= PS
.DEFAULT_BG_BLUE
;
527 grid
.bgColor
= PS
.RGBString (grid
.bgRed
, grid
.bgGreen
, grid
.bgBlue
);
529 grid
.borderRed
= PS
.DEFAULT_BORDER_RED
;
530 grid
.borderGreen
= PS
.DEFAULT_BORDER_GREEN
;
531 grid
.borderBlue
= PS
.DEFAULT_BORDER_BLUE
;
532 grid
.borderColor
= PS
.RGBString (grid
.borderRed
, grid
.borderGreen
, grid
.borderBlue
);
534 // grid.borderWidth = PS.DEFAULT_BORDER_WIDTH;
535 grid
.borderMax
= PS
.BORDER_WIDTH_MAX
; // for now; should be calculated
537 grid
.pointing
= -1; // bead cursor is pointing at (-1 if none)
539 grid
.flash
= true; // flash globally enabled?
540 grid
.flashList
= []; // array of currently flashing beads
542 grid
.glyphX
= Math
.floor(size
/ 2);
543 grid
.glyphY
= Math
.floor((size
/ 7) * 4);
549 for ( j
= 0; j
< y
; j
+= 1 )
552 for ( i
= 0; i
< x
; i
+= 1 )
554 grid
.beads
.push( PS
.InitBead(xpos
, ypos
, size
, grid
.bgColor
) );
563 PS
.DrawGrid = function ()
566 var ctx
, beads
, cnt
, i
, bead
;
569 beads
= PS
.Grid
.beads
;
572 for ( i
= 0; i
< cnt
; i
+= 1 )
577 PS
.DrawBead(bead
, ctx
);
581 PS
.EraseBead(bead
, ctx
);
586 // Returns true if x parameter is valid, else false
588 PS
.CheckX = function ( x
, fn
)
592 if ( typeof x
!== "number" )
594 PS
.Oops(fn
+ "x parameter not a number");
600 PS
.Oops(fn
+ "x parameter negative");
603 if ( x
>= PS
.Grid
.x
)
605 PS
.Oops(fn
+ "x parameter exceeds grid width");
612 // Returns true if y parameter is valid, else false
614 PS
.CheckY = function ( y
, fn
)
618 if ( typeof y
!== "number" )
620 PS
.Oops(fn
+ "y parameter not a number");
626 PS
.Oops(fn
+ "y parameter negative");
629 if ( y
>= PS
.Grid
.y
)
631 PS
.Oops(fn
+ "y parameter exceeds grid height");
639 // Takes x/y coords of bead and function name
640 // Returns the bead object at (x, y), or null if error
642 PS
.GetBead = function (x
, y
, fn
)
647 if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) )
652 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
654 return PS
.Grid
.beads
[i
];
659 PS
.GridSize = function (w
, h
)
662 var fn
, i
, cnt
, beads
, cv
;
664 fn
= "[PS.GridSize] ";
666 if ( typeof w
!== "number" )
668 PS
.Oops(fn
+ "Width param not a number");
671 if ( typeof h
!== "number" )
673 PS
.Oops(fn
+ "Height param not a number");
678 if ( w
=== PS
.DEFAULT
)
680 w
= PS
.GRID_DEFAULT_WIDTH
;
684 PS
.Oops(fn
+ "width parameter < 1");
687 else if ( w
> PS
.GRID_MAX
)
689 PS
.Oops(fn
+ "width parameter > " + PS
.GRID_MAX
);
694 if ( h
=== PS
.DEFAULT
)
696 h
= PS
.GRID_DEFAULT_HEIGHT
;
700 PS
.Oops(fn
+ "height parameter < 1");
703 else if ( h
> PS
.GRID_MAX
)
705 PS
.Oops(fn
+ "height parameter > " + PS
.GRID_MAX
);
709 // If a grid already exists, null out its arrays and then itself
713 beads
= PS
.Grid
.beads
;
717 for ( i
= 0; i
< cnt
; i
+= 1 )
723 PS
.Grid
.beads
= null;
724 PS
.Grid
.flashList
= null;
728 PS
.Grid
= PS
.InitGrid(w
, h
);
730 // Reset mouse coordinates
741 cv
= document
.getElementById("screen");
744 cv
.height
= PS
.Grid
.height
; // setting height erases canvas
750 PS
.GridBGColor = function ( rgb
)
753 var fn
, current
, colors
, e
;
755 fn
= "[PS.GridBGColor] ";
756 current
= (PS
.Grid
.bgRed
* PS
.REDSHIFT
) + (PS
.Grid
.bgGreen
* 256) + PS
.Grid
.bgBlue
;
758 // if param or PS.CURRENT, just return current color
760 if ( (rgb
=== undefined) || (rgb
=== PS
.CURRENT
) )
765 if ( rgb
=== PS
.DEFAULT
)
767 rgb
= PS
.DEFAULT_BG_COLOR
;
771 rgb
= PS
.ValidRGB( rgb
, fn
);
778 colors
= PS
.UnmakeRGB(rgb
);
781 PS
.Grid
.bgRed
= colors
.r
;
782 PS
.Grid
.bgGreen
= colors
.g
;
783 PS
.Grid
.bgBlue
= colors
.b
;
784 PS
.Grid
.bgColor
= PS
.RGBString(colors
.r
, colors
.g
, colors
.b
);
786 // Reset browser background
789 e
.style
.backgroundColor
= PS
.Grid
.bgColor
;
791 // reset status line background
793 e
= document
.getElementById("status");
796 e
.style
.backgroundColor
= PS
.Grid
.bgColor
;
801 e
= document
.getElementById("screen");
804 e
.width
= PS
.CANVAS_SIZE
; // setting width erases
812 // PS.MakeRGB (r, g, b)
813 // Takes three colors and returns multiplexed rgb value, or 0 (black) if error
815 PS
.MakeRGB = function (r
, g
, b
)
820 fn
= "[PS.MakeRGB] ";
822 if ( typeof r
!== "number" )
824 PS
.Oops(fn
+ "R parameter not a number");
837 if ( typeof g
!== "number" )
839 PS
.Oops(fn
+ "G parameter not a number");
852 if ( typeof b
!== "number" )
854 PS
.Oops(fn
+ "B parameter not a number");
867 rgb
= (r
* PS
.REDSHIFT
) + (g
* 256) + b
;
874 // PS.BeadShow(x, y, flag)
875 // Returns a bead's display status and optionally changes it
876 // [x, y] are grid position
877 // Optional [flag] must be 1/true or 0/false
879 PS
.DoBeadShow = function (x
, y
, flag
)
884 // Assume x/y params are already verified
886 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
887 bead
= PS
.Grid
.beads
[i
];
889 if ( (flag
=== undefined) || (flag
=== PS
.CURRENT
) || (flag
=== bead
.visible
) )
897 if ( PS
.Grid
.flash
&& bead
.flash
)
903 bead
.colorNow
= bead
.color
;
915 PS
.BeadShow = function (x
, y
, flag
)
920 fn
= "[PS.BeadShow] ";
922 // normalize flag value to t/f if defined
924 if ( (flag
!== undefined) && (flag
!== PS
.CURRENT
) )
926 if ( flag
=== PS
.DEFAULT
)
942 if ( y
=== PS
.ALL
) // do entire grid
944 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
946 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
948 flag
= PS
.DoBeadShow( i
, j
, flag
);
952 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
958 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
960 flag
= PS
.DoBeadShow( i
, y
, flag
);
964 else if ( y
=== PS
.ALL
)
966 if ( !PS
.CheckX( x
, fn
) ) // verify x param
970 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
972 flag
= PS
.DoBeadShow( x
, j
, flag
);
975 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
981 flag
= PS
.DoBeadShow( x
, y
, flag
); // do one bead
987 // PS.BeadColor (x, y, rgb)
988 // Returns and optionally sets a bead's color
989 // [x, y] are grid position
990 // Optional [rgb] must be a multiplexed rgb value (0xRRGGBB)
992 PS
.DoBeadColor = function ( x
, y
, rgb
, r
, g
, b
)
997 // Assume x/y params are already verified
999 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1000 bead
= PS
.Grid
.beads
[i
];
1002 if ( (rgb
=== undefined) || (rgb
=== PS
.CURRENT
) ) // if no rgb or PS.CURRENT, return current color
1004 return (bead
.red
* PS
.REDSHIFT
) + (bead
.green
* 256) + bead
.blue
;
1007 bead
.dirty
= true; // mark this bead as explicitly colored
1012 if ( bead
.alpha
< PS
.DEFAULT_ALPHA
) // Calc new color based on alpha
1014 bead
.alphaRed
= PS
.Dissolve( PS
.Grid
.bgRed
, r
, bead
.alpha
);
1015 bead
.alphaGreen
= PS
.Dissolve( PS
.Grid
.bgGreen
, g
, bead
.alpha
);
1016 bead
.alphaBlue
= PS
.Dissolve( PS
.Grid
.bgBlue
, b
, bead
.alpha
);
1017 bead
.color
= PS
.RGBString( bead
.alphaRed
, bead
.alphaGreen
, bead
.alphaBlue
);
1022 bead
.alphaGreen
= g
;
1024 bead
.color
= PS
.RGBString( r
, g
, b
);
1029 if ( PS
.Grid
.flash
&& bead
.flash
)
1031 PS
.FlashStart(x
, y
);
1035 bead
.colorNow
= bead
.color
;
1043 PS
.BeadColor = function (x
, y
, rgb
)
1046 var fn
, colors
, r
, g
, b
, i
, j
;
1048 fn
= "[PS.BeadColor] ";
1050 // if no rgb specified, just return current color
1052 if ( rgb
=== PS
.DEFAULT
)
1054 rgb
= PS
.DEFAULT_BEAD_COLOR
;
1055 r
= PS
.DEFAULT_BG_RED
;
1056 g
= PS
.DEFAULT_BG_GREEN
;
1057 b
= PS
.DEFAULT_BG_BLUE
;
1059 else if ( (rgb
!== undefined) && (rgb
!== PS
.CURRENT
) )
1061 rgb
= PS
.ValidRGB( rgb
, fn
);
1066 colors
= PS
.UnmakeRGB( rgb
);
1074 if ( y
=== PS
.ALL
) // do entire grid
1076 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1078 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1080 rgb
= PS
.DoBeadColor( i
, j
, rgb
, r
, g
, b
);
1084 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1090 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1092 rgb
= PS
.DoBeadColor( i
, y
, rgb
, r
, g
, b
);
1096 else if ( y
=== PS
.ALL
)
1098 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1102 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1104 rgb
= PS
.DoBeadColor( x
, j
, rgb
, r
, g
, b
);
1107 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1113 rgb
= PS
.DoBeadColor( x
, y
, rgb
, r
, g
, b
); // do one bead
1119 // PS.BeadAlpha(x, y, a)
1120 // Returns and optionally sets a bead's alpha
1121 // [x, y] are grid position
1122 // Optional [a] must be a number between 0.0 and 1.0
1124 PS
.DoBeadAlpha = function ( x
, y
, a
)
1129 // Assume x/y params are already verified
1131 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1132 bead
= PS
.Grid
.beads
[i
];
1134 if ( (a
=== undefined) || (a
=== PS
.CURRENT
) || (a
=== bead
.alpha
) )
1139 // Calc new color between background and base
1143 if ( bead
.alpha
< PS
.DEFAULT_ALPHA
) // Calc new color based on alpha
1145 bead
.alphaRed
= PS
.Dissolve( PS
.Grid
.bgRed
, bead
.red
, a
);
1146 bead
.alphaGreen
= PS
.Dissolve( PS
.Grid
.bgGreen
, bead
.green
, a
);
1147 bead
.alphaBlue
= PS
.Dissolve( PS
.Grid
.bgBlue
, bead
.blue
, a
);
1148 bead
.color
= PS
.RGBString( bead
.alphaRed
, bead
.alphaGreen
, bead
.alphaBlue
);
1152 bead
.alphaRed
= bead
.red
;
1153 bead
.alphaGreen
= bead
.green
;
1154 bead
.alphaBlue
= bead
.blue
;
1155 bead
.color
= PS
.RGBString( bead
.red
, bead
.green
, bead
.blue
);
1159 if ( PS
.Grid
.flash
&& bead
.flash
)
1161 PS
.FlashStart(x
, y
);
1165 bead
.colorNow
= bead
.color
;
1173 PS
.BeadAlpha = function (x
, y
, a
)
1178 fn
= "[PS.BeadAlpha] ";
1180 if ( a
!== undefined )
1182 if ( typeof a
!== "number" )
1184 PS
.Oops(fn
+ "alpha param is not a number");
1191 if ( a
=== PS
.DEFAULT
)
1193 a
= PS
.DEFAULT_ALPHA
;
1195 else if ( a
!== PS
.CURRENT
)
1201 else if ( a
> PS
.DEFAULT_ALPHA
)
1203 a
= PS
.DEFAULT_ALPHA
;
1210 if ( y
=== PS
.ALL
) // do entire grid
1212 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1214 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1216 a
= PS
.DoBeadAlpha( i
, j
, a
);
1220 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1226 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1228 a
= PS
.DoBeadAlpha( i
, y
, a
);
1232 else if ( y
=== PS
.ALL
)
1234 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1238 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1240 a
= PS
.DoBeadAlpha( x
, j
, a
);
1243 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1249 a
= PS
.DoBeadAlpha( x
, y
, a
); // do one bead
1255 // PS.BeadBorderWidth (x, y, width)
1256 // Returns and optionally sets a bead's border width
1257 // [x, y] are grid position
1258 // Optional [width] must be a number
1260 PS
.DoBeadBorderWidth = function (x
, y
, width
)
1265 // Assume x/y params are already verified
1267 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1268 bead
= PS
.Grid
.beads
[i
];
1270 if ( (width
=== undefined) || (width
=== PS
.CURRENT
) ) // if no width or PS.CURRENT, return current width
1272 return bead
.borderWidth
;
1275 bead
.borderWidth
= width
;
1285 PS
.BeadBorderWidth = function (x
, y
, width
)
1290 fn
= "[PS.BeadBorderWidth] ";
1292 if ( width
=== PS
.DEFAULT
)
1294 width
= PS
.DEFAULT_BORDER_WIDTH
;
1296 else if ( (width
!== undefined) && (width
!== PS
.CURRENT
) )
1298 if ( typeof width
!== "number" )
1300 PS
.Oops(fn
+ "width param is not a number");
1303 width
= Math
.floor(width
);
1308 else if ( width
> PS
.BORDER_WIDTH_MAX
)
1310 width
= PS
.BORDER_WIDTH_MAX
;
1316 if ( y
=== PS
.ALL
) // do entire grid
1318 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1320 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1322 width
= PS
.DoBeadBorderWidth( i
, j
, width
);
1326 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1332 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1334 width
= PS
.DoBeadBorderWidth( i
, y
, width
);
1338 else if ( y
=== PS
.ALL
)
1340 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1344 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1346 width
= PS
.DoBeadBorderWidth( x
, j
, width
);
1349 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1355 width
= PS
.DoBeadBorderWidth( x
, y
, width
); // do one bead
1361 // PS.BeadBorderColor (x, y, rgb)
1362 // Returns and optionally sets a bead's border color
1363 // [x, y] are grid position
1364 // Optional [rgb] must be a multiplexed rgb value (0xRRGGBB)
1366 PS
.DoBeadBorderColor = function (x
, y
, rgb
, r
, g
, b
)
1371 // Assume x/y params are already verified
1373 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1374 bead
= PS
.Grid
.beads
[i
];
1376 if ( (rgb
=== undefined) || (rgb
=== PS
.CURRENT
) ) // if no rgb or PS.CURRENT, return current color
1378 return (bead
.borderRed
* PS
.REDSHIFT
) + (bead
.borderGreen
* 256) + bead
.borderBlue
;
1382 bead
.borderGreen
= g
;
1383 bead
.borderBlue
= b
;
1384 if ( bead
.borderAlpha
< PS
.DEFAULT_ALPHA
)
1386 r
= PS
.Dissolve( PS
.Grid
.bgRed
, r
, bead
.borderAlpha
);
1387 g
= PS
.Dissolve( PS
.Grid
.bgGreen
, g
, bead
.borderAlpha
);
1388 b
= PS
.Dissolve( PS
.Grid
.bgBlue
, b
, bead
.borderAlpha
);
1390 bead
.borderColor
= PS
.RGBString( r
, g
, b
);
1400 PS
.BeadBorderColor = function (x
, y
, rgb
)
1403 var fn
, colors
, r
, g
, b
, i
, j
;
1405 fn
= "[PS.BeadBorderColor] ";
1407 if ( rgb
=== PS
.DEFAULT
)
1409 rgb
= PS
.DEFAULT_BORDER_COLOR
;
1410 r
= PS
.DEFAULT_BORDER_RED
;
1411 g
= PS
.DEFAULT_BORDER_GREEN
;
1412 b
= PS
.DEFAULT_BORDER_BLUE
;
1414 else if ( (rgb
!== undefined) && (rgb
!== PS
.CURRENT
) )
1416 rgb
= PS
.ValidRGB( rgb
, fn
);
1421 colors
= PS
.UnmakeRGB( rgb
);
1429 if ( y
=== PS
.ALL
) // do entire grid
1431 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1433 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1435 rgb
= PS
.DoBeadBorderColor( i
, j
, rgb
, r
, g
, b
);
1439 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1445 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1447 rgb
= PS
.DoBeadBorderColor( i
, y
, rgb
, r
, g
, b
);
1451 else if ( y
=== PS
.ALL
)
1453 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1457 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1459 rgb
= PS
.DoBeadBorderColor( x
, j
, rgb
, r
, g
, b
);
1462 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1468 rgb
= PS
.DoBeadBorderColor( x
, y
, rgb
, r
, g
, b
); // do one bead
1474 // PS.BeadBorderAlpha(x, y, a)
1475 // Returns a bead's border alpha and optionally changes it
1476 // [x, y] are grid position
1477 // Optional [a] must be a number between 0.0 and 1.0
1479 PS
.DoBeadBorderAlpha = function (x
, y
, a
)
1482 var i
, bead
, r
, g
, b
;
1484 // Assume x/y params are already verified
1486 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1487 bead
= PS
.Grid
.beads
[i
];
1489 if ( (a
=== undefined) || (a
=== PS
.CURRENT
) || (a
=== bead
.borderAlpha
) )
1491 return bead
.borderAlpha
;
1494 bead
.borderAlpha
= a
;
1495 if ( a
< PS
.DEFAULT_ALPHA
)
1497 r
= PS
.Dissolve( PS
.Grid
.bgRed
, bead
.borderRed
, a
);
1498 g
= PS
.Dissolve( PS
.Grid
.bgGreen
, bead
.borderGreen
, a
);
1499 b
= PS
.Dissolve( PS
.Grid
.bgBlue
, bead
.borderBlue
, a
);
1500 bead
.borderColor
= PS
.RGBString( r
, g
, b
);
1504 bead
.borderColor
= PS
.RGBString( bead
.borderRed
, bead
.borderGreen
, bead
.borderBlue
);
1513 PS
.BeadBorderAlpha = function (x
, y
, a
)
1518 fn
= "[PS.BeadBorderAlpha] ";
1520 if ( a
!== undefined )
1522 if ( typeof a
!== "number" )
1524 PS
.Oops(fn
+ "alpha param is not a number");
1531 if ( a
=== PS
.DEFAULT
)
1533 a
= PS
.DEFAULT_ALPHA
;
1535 else if ( a
!== PS
.CURRENT
)
1541 else if ( a
> PS
.DEFAULT_ALPHA
)
1543 a
= PS
.DEFAULT_ALPHA
;
1550 if ( y
=== PS
.ALL
) // do entire grid
1552 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1554 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1556 a
= PS
.DoBeadBorderAlpha( i
, j
, a
);
1560 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1566 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1568 a
= PS
.DoBeadBorderAlpha( i
, y
, a
);
1572 else if ( y
=== PS
.ALL
)
1574 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1578 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1580 a
= PS
.DoBeadBorderAlpha( x
, j
, a
);
1583 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1589 a
= PS
.DoBeadBorderAlpha( x
, y
, a
); // do one bead
1595 // PS.BeadGlyph(x, y, g)
1596 // Returns a bead's glyph and optionally changes it
1597 // [x, y] are grid position
1598 // Optional [g] must be either a string or a number
1600 PS
.DoBeadGlyph = function (x
, y
, g
)
1605 // Assume x/y params are already verified
1607 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1608 bead
= PS
.Grid
.beads
[i
];
1610 if ( (g
=== undefined) || (g
=== PS
.CURRENT
) || (g
=== bead
.glyph
) )
1616 bead
.glyphStr
= String
.fromCharCode(g
);
1619 if ( PS
.Grid
.flash
&& bead
.flash
)
1621 PS
.FlashStart(x
, y
);
1625 bead
.colorNow
= bead
.color
;
1633 PS
.BeadGlyph = function (x
, y
, g
)
1638 fn
= "[PS.BeadGlyph] ";
1640 // if no glyph specified, just return current border status
1643 if ( type
!== "undefined" )
1645 if ( type
=== "string" )
1649 PS
.Oops(fn
+ "glyph param is empty string");
1652 g
= g
.charCodeAt(0); // use only first character
1654 else if ( type
=== "number" )
1657 if ( g
=== PS
.DEFAULT
)
1661 else if ( g
!== PS
.CURRENT
)
1671 PS
.Oops(fn
+ "glyph param not a string or number");
1678 if ( y
=== PS
.ALL
) // do entire grid
1680 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1682 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1684 g
= PS
.DoBeadGlyph( i
, j
, g
);
1688 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1694 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1696 g
= PS
.DoBeadGlyph( i
, y
, g
);
1700 else if ( y
=== PS
.ALL
)
1702 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1706 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1708 g
= PS
.DoBeadGlyph( x
, j
, g
);
1711 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1717 g
= PS
.DoBeadGlyph( x
, y
, g
); // do one bead
1723 // PS.BeadGlyphColor (x, y, rgb)
1724 // Returns and optionally sets a bead's glyph color
1725 // [x, y] are grid position
1726 // Optional [rgb] must be a multiplexed rgb value (0xRRGGBB)
1728 PS
.DoBeadGlyphColor = function (x
, y
, rgb
, r
, g
, b
)
1733 // Assume x/y params are already verified
1735 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1736 bead
= PS
.Grid
.beads
[i
];
1738 if ( (rgb
=== undefined) || (rgb
=== PS
.CURRENT
) ) // if no rgb or PS.CURRENT, return current color
1740 return (bead
.glyphRed
* PS
.REDSHIFT
) + (bead
.glyphGreen
* 256) + bead
.glyphBlue
;
1744 bead
.glyphGreen
= g
;
1746 if ( bead
.alpha
< PS
.DEFAULT_ALPHA
) // Calc new color based on alpha
1748 r
= PS
.Dissolve( PS
.Grid
.bgRed
, r
, bead
.alpha
);
1749 g
= PS
.Dissolve( PS
.Grid
.bgGreen
, g
, bead
.alpha
);
1750 b
= PS
.Dissolve( PS
.Grid
.bgBlue
, b
, bead
.alpha
);
1752 bead
.glyphColor
= PS
.RGBString( r
, g
, b
);
1754 if ( bead
.visible
&& (bead
.glyph
> 0) )
1761 PS
.BeadGlyphColor = function (x
, y
, rgb
)
1764 var fn
, colors
, r
, g
, b
, i
, j
;
1766 fn
= "[PS.BeadGlyphColor] ";
1768 if ( rgb
=== PS
.DEFAULT
)
1770 rgb
= PS
.DEFAULT_GLYPH_COLOR
;
1771 r
= PS
.DEFAULT_GLYPH_RED
;
1772 g
= PS
.DEFAULT_GLYPH_GREEN
;
1773 b
= PS
.DEFAULT_GLYPH_BLUE
;
1775 else if ( (rgb
!== undefined) && (rgb
!== PS
.CURRENT
) )
1777 rgb
= PS
.ValidRGB( rgb
, fn
);
1782 colors
= PS
.UnmakeRGB( rgb
);
1790 if ( y
=== PS
.ALL
) // do entire grid
1792 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1794 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1796 rgb
= PS
.DoBeadGlyphColor( i
, j
, rgb
, r
, g
, b
);
1800 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1806 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1808 rgb
= PS
.DoBeadGlyphColor( i
, y
, rgb
, r
, g
, b
);
1812 else if ( y
=== PS
.ALL
)
1814 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1818 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1820 rgb
= PS
.DoBeadGlyphColor( x
, j
, rgb
, r
, g
, b
);
1823 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1829 rgb
= PS
.DoBeadGlyphColor( x
, y
, rgb
, r
, g
, b
); // do one bead
1835 // PS.BeadFlash(x, y, flag)
1836 // Returns a bead's flash status and optionally changes it
1837 // [x, y] are grid position
1838 // Optional [flag] must be 1/true or 0/false
1840 PS
.DoBeadFlash = function (x
, y
, flag
)
1845 // Assume x/y params are already verified
1847 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1848 bead
= PS
.Grid
.beads
[i
];
1850 if ( (flag
=== undefined) || (flag
=== PS
.CURRENT
) )
1859 PS
.BeadFlash = function (x
, y
, flag
)
1864 fn
= "[PS.BeadFlash] ";
1866 // normalize flag value to t/f if defined
1868 if ( (flag
!== undefined) && (flag
!== PS
.CURRENT
) )
1870 if ( flag
=== PS
.DEFAULT
)
1886 if ( y
=== PS
.ALL
) // do entire grid
1888 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1890 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1892 flag
= PS
.DoBeadFlash( i
, j
, flag
);
1896 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
1902 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
1904 flag
= PS
.DoBeadFlash( i
, y
, flag
);
1908 else if ( y
=== PS
.ALL
)
1910 if ( !PS
.CheckX( x
, fn
) ) // verify x param
1914 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
1916 flag
= PS
.DoBeadFlash( x
, j
, flag
);
1919 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
1925 flag
= PS
.DoBeadFlash( x
, y
, flag
); // do one bead
1931 // PS.BeadFlashColor (x, y, rgb)
1932 // Returns and optionally sets a bead's flash color
1933 // [x, y] are grid position
1934 // Optional [rgb] must be a multiplexed rgb value (0xRRGGBB)
1936 PS
.DoBeadFlashColor = function (x
, y
, rgb
, r
, g
, b
)
1941 // Assume x/y params are already verified
1943 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
1944 bead
= PS
.Grid
.beads
[i
];
1946 if ( (rgb
=== undefined) || (rgb
=== PS
.CURRENT
) ) // if no rgb or PS.CURRENT, return current color
1948 return (bead
.flashRed
* PS
.REDSHIFT
) + (bead
.flashGreen
* 256) + bead
.flashBlue
;
1952 bead
.flashGreen
= g
;
1954 bead
.flashColor
= PS
.RGBString(r
, g
, b
);
1959 PS
.BeadFlashColor = function (x
, y
, rgb
)
1962 var fn
, r
, g
, b
, colors
, i
, j
;
1964 fn
= "[PS.BeadFlashColor] ";
1966 if ( rgb
=== PS
.DEFAULT
)
1968 rgb
= PS
.DEFAULT_FLASH_COLOR
;
1969 r
= PS
.DEFAULT_FLASH_RED
;
1970 g
= PS
.DEFAULT_FLASH_GREEN
;
1971 b
= PS
.DEFAULT_FLASH_BLUE
;
1973 else if ( (rgb
!== undefined) && (rgb
!== PS
.CURRENT
) )
1975 rgb
= PS
.ValidRGB( rgb
, fn
);
1980 colors
= PS
.UnmakeRGB( rgb
);
1988 if ( y
=== PS
.ALL
) // do entire grid
1990 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
1992 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
1994 rgb
= PS
.DoBeadFlashColor( i
, j
, rgb
, r
, g
, b
);
1998 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
2004 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
2006 rgb
= PS
.DoBeadFlashColor( i
, y
, rgb
, r
, g
, b
);
2010 else if ( y
=== PS
.ALL
)
2012 if ( !PS
.CheckX( x
, fn
) ) // verify x param
2016 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
2018 rgb
= PS
.DoBeadFlashColor( x
, j
, rgb
, r
, g
, b
);
2021 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
2027 rgb
= PS
.DoBeadFlashColor( x
, y
, rgb
, r
, g
, b
); // do one bead
2033 // PS.BeadData(x, y, data)
2034 // Returns a bead's data and optionally changes it
2035 // [x, y] are grid position
2036 // Optional [data] can be any data type
2038 PS
.DoBeadData = function (x
, y
, data
)
2043 // Assume x/y params are already verified
2045 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
2046 bead
= PS
.Grid
.beads
[i
];
2048 if ( data
!== undefined )
2056 PS
.BeadData = function (x
, y
, data
)
2061 fn
= "[PS.BeadData] ";
2065 if ( y
=== PS
.ALL
) // do entire grid
2067 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
2069 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
2071 data
= PS
.DoBeadData( i
, j
, data
);
2075 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
2081 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
2083 data
= PS
.DoBeadData( i
, y
, data
);
2087 else if ( y
=== PS
.ALL
)
2089 if ( !PS
.CheckX( x
, fn
) ) // verify x param
2093 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
2095 data
= PS
.DoBeadData( x
, j
, data
);
2098 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
2104 data
= PS
.DoBeadData( x
, y
, data
); // do one bead
2110 // PS.BeadAudio(x, y, audio, volume)
2111 // Returns a bead's audio file and optionally changes it (and its volume)
2112 // [x, y] are grid position
2113 // Optional [audio] must be a string
2114 // Optional [volume] should be between 0 and 100 inclusive
2116 PS
.DoBeadAudio = function (x
, y
, audio
, volume
)
2121 // Assume x/y params are already verified
2123 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
2124 bead
= PS
.Grid
.beads
[i
];
2126 if ( (audio
!== undefined) && (audio
!== PS
.CURRENT
) )
2131 if ( (volume
!== undefined) && (volume
!== PS
.CURRENT
) )
2133 bead
.volume
= volume
;
2139 PS
.BeadAudio = function (x
, y
, audio
, volume
)
2144 fn
= "[PS.BeadAudio] ";
2146 // check audio file param
2148 if ( (audio
!== undefined) && (audio
!== PS
.CURRENT
) )
2150 if ( audio
=== PS
.DEFAULT
)
2154 else if ( typeof audio
!== "string" )
2156 PS
.Oops(fn
+ "audio param is not a string");
2159 else if ( audio
.length
< 1 )
2165 // check volume param
2167 if ( (volume
!== undefined) && (volume
!== PS
.CURRENT
) )
2169 if ( volume
=== PS
.DEFAULT
)
2171 volume
= PS
.DEFAULT_VOLUME
;
2173 else if ( typeof volume
!== "number" )
2175 PS
.Oops(fn
+ "volume param is not a number");
2184 else if ( volume
> PS
.DEFAULT_VOLUME
)
2186 volume
= PS
.DEFAULT_VOLUME
;
2193 if ( y
=== PS
.ALL
) // do entire grid
2195 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
2197 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
2199 audio
= PS
.DoBeadAudio( i
, j
, audio
, volume
);
2203 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
2209 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
2211 audio
= PS
.DoBeadAudio( i
, y
, audio
, volume
);
2215 else if ( y
=== PS
.ALL
)
2217 if ( !PS
.CheckX( x
, fn
) ) // verify x param
2221 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
2223 audio
= PS
.DoBeadAudio( x
, j
, audio
, volume
);
2226 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
2232 audio
= PS
.DoBeadAudio( x
, y
, audio
, volume
); // do one bead
2238 // PS.BeadFunction(x, y, func)
2239 // Returns a bead's exec function and optionally changes it
2240 // [x, y] are grid position
2241 // Optional [func] must be a JavaScript function
2243 PS
.DoBeadFunction = function (x
, y
, exec
)
2248 // Assume x/y params are already verified
2250 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
2251 bead
= PS
.Grid
.beads
[i
];
2253 if ( (exec
!== undefined) && (exec
!== PS
.CURRENT
) )
2261 PS
.BeadFunction = function (x
, y
, exec
)
2266 fn
= "[PS.BeadFunction] ";
2268 if ( (exec
!== undefined) || (exec
!== PS
.CURRENT
) )
2270 if ( exec
=== PS
.DEFAULT
)
2274 else if ( typeof exec
!== "function" )
2276 PS
.Oops(fn
+ "exec param not a valid function");
2283 if ( y
=== PS
.ALL
) // do entire grid
2285 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
2287 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
2289 exec
= PS
.DoBeadFunction( i
, j
, exec
);
2293 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
2299 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
2301 exec
= PS
.DoBeadFunction( i
, y
, exec
);
2305 else if ( y
=== PS
.ALL
)
2307 if ( !PS
.CheckX( x
, fn
) ) // verify x param
2311 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
2313 exec
= PS
.DoBeadFunction( x
, j
, exec
);
2316 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
2322 exec
= PS
.DoBeadFunction( x
, y
, exec
); // do one bead
2328 // PS.BeadTouch(x, y, mask)
2329 // Simulates effect of clicking on a bead
2330 // [x, y] are grid position
2332 PS
.DoBeadTouch = function (x
, y
)
2337 // Assume x/y params are already verified
2339 i
= x
+ (y
* PS
.Grid
.x
); // get index of bead
2340 bead
= PS
.Grid
.beads
[i
];
2344 if ( typeof bead
.audio
=== "string" )
2346 PS
.AudioPlay(bead
.audio
, bead
.volume
);
2351 if ( typeof bead
.exec
=== "function" )
2353 bead
.exec(x
, y
, bead
.data
);
2358 PS
.Click(x
, y
, bead
.data
);
2361 PS
.BeadTouch = function (x
, y
)
2366 fn
= "[PS.BeadTouch] ";
2370 if ( y
=== PS
.ALL
) // do entire grid
2372 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 )
2374 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 )
2376 PS
.DoBeadTouch( i
, j
);
2380 else if ( !PS
.CheckY( y
, fn
) ) // verify y param
2386 for ( i
= 0; i
< PS
.Grid
.x
; i
+= 1 ) // do entire row
2388 PS
.DoBeadTouch( i
, y
);
2392 else if ( y
=== PS
.ALL
)
2394 if ( !PS
.CheckX( x
, fn
) ) // verify x param
2398 for ( j
= 0; j
< PS
.Grid
.y
; j
+= 1 ) // do entire column
2400 PS
.DoBeadTouch( x
, j
);
2403 else if ( !PS
.CheckX( x
, fn
) || !PS
.CheckY( y
, fn
) ) // verify both params
2409 PS
.DoBeadTouch( x
, y
); // do one bead
2415 PS
.Status
= "Perlenspiel";
2418 PS
.StatusText = function (str
)
2423 fn
= "[PS.StatusText] ";
2426 if ( type
!== "undefined" )
2428 if ( type
!== "string" )
2430 PS
.Oops(fn
+ "Parameter is not a string");
2434 e
= document
.getElementById("status");
2437 if ( PS
.StatusFading
) // start the fade
2439 e
.style
.color
= PS
.Grid
.bgColor
;
2450 PS
.StatusColor = function (rgb
)
2455 fn
= "[PS.StatusText] ";
2457 if ( (rgb
!== undefined) && (rgb
!== PS
.CURRENT
) )
2459 rgb
= PS
.ValidRGB( rgb
, fn
);
2464 if ( rgb
=== PS
.DEFAULT
)
2466 rgb
= PS
.DEFAULT_TEXT_COLOR
;
2468 colors
= PS
.UnmakeRGB(rgb
);
2469 PS
.StatusRed
= colors
.r
;
2470 PS
.StatusGreen
= colors
.g
;
2471 PS
.StatusBlue
= colors
.b
;
2472 PS
.StatusHue
= PS
.RGBString(colors
.r
, colors
.g
, colors
.b
);
2473 PS
.StatusPhase
= 100; // stops fades in progress
2475 e
= document
.getElementById("status");
2478 e
.style
.color
= PS
.StatusHue
;
2480 e
= document
.getElementById("footer");
2483 e
.style
.color
= PS
.StatusHue
;
2487 return PS
.StatusHue
;
2490 // Turn status line fading on and off
2492 PS
.StatusFade = function (flag
)
2497 fn
= "[PS.StatusFade] ";
2499 if ( (flag
!== undefined) && (flag
!== PS
.CURRENT
) )
2501 if ( flag
|| (flag
=== PS
.DEFAULT
) )
2508 PS
.StatusPhase
= 100;
2509 e
= document
.getElementById("status");
2512 e
.style
.color
= PS
.StatusHue
;
2515 PS
.StatusFading
= flag
;
2518 return PS
.StatusFading
;
2523 // Open debugger if not already open
2525 PS
.DebugOpen = function ()
2530 if ( !PS
.DebugWindow
)
2532 div
= document
.getElementById("debug");
2533 div
.style
.display
= "inline";
2537 e
= document
.getElementById("monitor");
2543 PS
.DebugWindow
= true;
2547 // Close debugger if not already closed
2549 PS
.DebugClose = function ()
2554 if ( PS
.DebugWindow
)
2556 e
= document
.getElementById("debug");
2557 e
.style
.display
= "none";
2558 PS
.DebugWindow
= false;
2562 // Add line to debugger (does not include CR)
2564 PS
.Debug = function (str
)
2569 if ( typeof str
!== "string" )
2576 e
= document
.getElementById("monitor");
2579 e
.value
+= str
; // add it
2580 e
.scrollTop
= e
.scrollHeight
; // keep it scrolled down
2584 // Clear footer and debugger
2586 PS
.DebugClear = function ()
2591 e
= document
.getElementById("footer");
2594 e
.style
.color
="#000000"; // change to black
2595 e
.innerHTML
= "Version 2.0.0";
2598 if ( PS
.DebugWindow
)
2600 e
= document
.getElementById("monitor");
2608 // Send error message to footer and debugger if open (includes CR)
2610 PS
.Oops = function (str
)
2615 if ( typeof str
!== "string" )
2620 e
= document
.getElementById("footer");
2626 // Also display on debugger if open
2628 // if ( PS.DebugWindow )
2630 // e = document.getElementById("monitor");
2633 // e.value += ("ERROR: " + str + "\n");
2634 // e.scrollTop = e.scrollHeight; // keep it scrolled down
2638 PS
.Debug( "ERROR: " + str
+ "\n" );
2640 PS
.AudioPlay("fx_uhoh");
2643 // Set up user clock
2645 PS
.Clock = function ( ticks
)
2652 if ( ticks
!== undefined )
2654 if ( typeof ticks
!== "number" )
2656 PS
.Oops(fn
+ "ticks parameter not a number");
2659 ticks
= Math
.floor(ticks
);
2664 else if ( typeof PS
.Tick
!== "function" )
2666 PS
.Oops(fn
+ "PS.Tick function undefined");
2671 PS
.UserClock
= ticks
;
2675 return PS
.UserClock
;
2678 // General system timer
2680 PS
.Timer = function ()
2683 var phase
, hue
, r
, g
, b
, e
;
2685 // Handle bead flashing and status text fading
2688 if ( PS
.FlashDelay
>= PS
.FLASH_INTERVAL
)
2693 // Handle status text fading
2695 if ( PS
.StatusFading
&& (PS
.StatusPhase
< 100) )
2697 phase
= PS
.StatusPhase
+ PS
.STATUS_FLASH_STEP
;
2706 r
= PS
.Dissolve( PS
.Grid
.bgRed
, PS
.StatusRed
, phase
);
2707 g
= PS
.Dissolve( PS
.Grid
.bgGreen
, PS
.StatusGreen
, phase
);
2708 b
= PS
.Dissolve( PS
.Grid
.bgBlue
, PS
.StatusBlue
, phase
);
2709 hue
= PS
.RGBString(r
, g
, b
);
2711 PS
.StatusPhase
= phase
;
2712 e
= document
.getElementById("status");
2715 e
.style
.color
= hue
;
2720 // Handle user clock
2722 if ( PS
.UserClock
> 0 )
2725 if ( PS
.UserDelay
>= PS
.UserClock
)
2732 PS
.Tick(); // call user function
2736 PS
.Oops("PS.Tick() failed [" + err
.message
+ "]" );
2737 PS
.UserClock
= 0; // stop the timer
2744 // PS.StartFlash(bead)
2745 // Initiates flashing of bead
2747 PS
.FlashStart = function (x
, y
)
2750 var which
, bead
, i
, len
;
2752 which
= x
+ (y
* PS
.Grid
.x
); // index of bead
2754 bead
= PS
.Grid
.beads
[which
];
2756 bead
.flashPhase
= 0; // init flash step
2760 bead
.colorNow
= bead
.flashColor
;
2763 // if this bead is already in flash queue, exit
2765 len
= PS
.Grid
.flashList
.length
;
2766 for ( i
= 0; i
< len
; i
+= 1 )
2768 if ( PS
.Grid
.flashList
[i
] === which
)
2774 // else add this bead to queue
2776 PS
.Grid
.flashList
.push(which
);
2779 // PS.NextFlash(bead)
2780 // Flash all beads in queue
2782 PS
.FlashNext = function ()
2785 var ctx
, len
, i
, which
, bead
, phase
, r
, g
, b
;
2788 len
= PS
.Grid
.flashList
.length
;
2792 which
= PS
.Grid
.flashList
[i
];
2793 bead
= PS
.Grid
.beads
[which
];
2794 phase
= bead
.flashPhase
+ PS
.FLASH_STEP
;
2796 // If flash is done, set normal color and remove bead from queue
2800 bead
.colorNow
= bead
.color
;
2801 bead
.flashPhase
= 0;
2802 PS
.Grid
.flashList
.splice(i
, 1);
2807 bead
.flashPhase
= phase
;
2808 r
= PS
.Dissolve( bead
.flashRed
, bead
.alphaRed
, phase
);
2809 g
= PS
.Dissolve( bead
.flashGreen
, bead
.alphaGreen
, phase
);
2810 b
= PS
.Dissolve( bead
.flashBlue
, bead
.alphaBlue
, phase
);
2811 bead
.colorNow
= PS
.RGBString(r
, g
, b
);
2814 PS
.DrawBead(bead
, ctx
);
2818 // System initialization
2820 // Records the x/y of mouse over grid, -1 if not over grid
2822 PS
.MouseXY = function (event
)
2825 var canvas
, x
, y
, beads
, bead
, row
, col
, i
;
2829 canvas
= document
.getElementById("screen");
2831 if ( event
.x
&& event
.y
)
2836 else // Firefox method to get the position
2838 x
= event
.clientX
+ document
.body
.scrollLeft
+ document
.documentElement
.scrollLeft
;
2839 y
= event
.clientY
+ document
.body
.scrollTop
+ document
.documentElement
.scrollTop
;
2842 x
-= canvas
.offsetLeft
;
2843 y
-= canvas
.offsetTop
;
2847 if ( (x
>= PS
.Grid
.left
) && (x
< PS
.Grid
.right
) && (y
>= PS
.Grid
.top
) && (y
< PS
.Grid
.bottom
) )
2849 // Which bead are we over?
2851 beads
= PS
.Grid
.beads
;
2852 i
= 0; // init index
2853 for ( row
= 0; row
< PS
.Grid
.y
; row
+= 1 )
2855 bead
= beads
[i
]; // get the first bead in this row
2857 // Is mouse over this row?
2859 if ( (y
>= bead
.top
) && (y
< bead
.bottom
) )
2863 for ( col
= 0; col
< PS
.Grid
.x
; col
+= 1 )
2866 if ( (x
>= bead
.left
) && (x
< bead
.right
) )
2877 i
+= PS
.Grid
.x
; // try next row
2887 // Called when mouse is clicked over canvas
2889 PS
.MouseDown = function (event
)
2895 if ( PS
.MouseX
>= 0 )
2897 bead
= PS
.Grid
.beads
[PS
.MouseX
+ (PS
.MouseY
* PS
.Grid
.x
)];
2899 // play audio if assigned to bead
2903 PS
.AudioPlay(bead
.audio
, bead
.volume
);
2906 // Call function if assigned to bead
2908 if ( typeof bead
.exec
=== "function" )
2912 bead
.exec(PS
.MouseX
, PS
.MouseY
, bead
.data
);
2916 PS
.Oops("Bead " + PS
.MouseX
+ ", " + PS
.MouseY
+ " function failed [" + err1
.message
+ "]" );
2920 if ( PS
.Click
) // only if function exists
2924 PS
.Click(PS
.MouseX
, PS
.MouseY
, bead
.data
);
2928 PS
.Oops("PS.Click() failed [" + err2
.message
+ "]" );
2934 // Called when mouse is released over canvas
2936 PS
.MouseUp = function (event
)
2941 if ( PS
.Grid
&& PS
.Release
) // only if grid and function exist
2944 if ( PS
.MouseX
>= 0 )
2946 bead
= PS
.Grid
.beads
[PS
.MouseX
+ (PS
.MouseY
* PS
.Grid
.x
)];
2949 PS
.Release(PS
.MouseX
, PS
.MouseY
, bead
.data
);
2953 PS
.Oops("PS.Release() failed [" + err
.message
+ "]" );
2959 // Called when mouse moves over canvas
2961 PS
.MouseMove = function (event
)
2968 if ( PS
.MouseX
>= 0 )
2970 bead
= PS
.Grid
.beads
[PS
.MouseX
+ (PS
.MouseY
* PS
.Grid
.x
)];
2971 if ( (PS
.MouseX
!== PS
.LastX
) || (PS
.MouseY
!== PS
.LastY
) )
2973 if ( PS
.Leave
) // only if function exists
2975 if ( PS
.LastX
>= 0 )
2977 last
= PS
.Grid
.beads
[PS
.LastX
+ (PS
.LastY
* PS
.Grid
.x
)];
2980 PS
.Leave(PS
.LastX
, PS
.LastY
, last
.data
);
2984 PS
.Oops("PS.Leave() failed [" + err1
.message
+ "]" );
2988 if ( PS
.Enter
) // only if function exists
2992 PS
.Enter(PS
.MouseX
, PS
.MouseY
, bead
.data
);
2996 PS
.Oops("PS.Enter() failed [" + err2
.message
+ "]" );
2999 PS
.LastX
= PS
.MouseX
;
3000 PS
.LastY
= PS
.MouseY
;
3003 else if ( PS
.LastX
>= 0 )
3005 if ( PS
.Leave
) // only if function exists
3007 last
= PS
.Grid
.beads
[PS
.LastX
+ (PS
.LastY
* PS
.Grid
.x
)];
3010 PS
.Leave(PS
.LastX
, PS
.LastY
, last
.data
);
3014 PS
.Oops("PS.Leave() failed [" + err3
.message
+ "]" );
3022 // Called when mouse leaves canvas
3024 PS
.MouseOut = function (event
)
3030 if ( PS
.Grid
&& PS
.Leave
) // only if grid and function exist
3032 if ( PS
.LastBead
>= 0 )
3034 last
= PS
.Grid
.beads
[PS
.LastBead
];
3037 PS
.Leave(last
.x
, last
.y
, last
.data
);
3041 PS
.Oops("PS.Leave() failed [" + err
.message
+ "]" );
3048 PS
.KeyFilter = function (key
, shift
)
3052 // convert lower-case alpha to upper-case if shift key is down
3054 if ( (key
>= 65) && (key
<= 90) )
3063 // Convert weird keycodes to ASCII
3092 // Translate shifted keys
3169 // Called when a key is pressed
3171 PS
.SysKeyDown = function (event
)
3176 if ( PS
.KeyDown
) // only if function exists
3178 if ( event
.which
=== null )
3180 key
= event
.keyCode
; // IE
3184 key
= event
.which
; // Others
3186 key
= PS
.KeyFilter(key
, event
.shiftKey
);
3189 PS
.KeyDown(key
, event
.shiftKey
, event
.ctrlKey
);
3193 PS
.Oops("PS.KeyDown() failed [" + err
.message
+ "]" );
3199 // Called when a key is released
3201 PS
.SysKeyUp = function (event
)
3206 if ( PS
.KeyUp
) // only if function exists
3208 if ( event
.which
=== null )
3210 key
= event
.keyCode
; // IE
3214 key
= event
.which
; // Others
3216 key
= PS
.KeyFilter(key
, event
.shiftKey
);
3219 PS
.KeyUp(key
, event
.shiftKey
, event
.ctrlKey
);
3223 PS
.Oops("PS.KeyUp() failed [" + err
.message
+ "]" );
3229 // Called when mouse wheel is moved
3231 PS
.SysWheel = function (event
)
3236 if ( PS
.Wheel
) // only if function exists
3244 event
= window
.event
;
3249 if ( event
.wheelDelta
)
3251 delta
= event
.wheelDelta
/ 120;
3258 // Firefox and Chrome?
3260 else if ( event
.detail
)
3262 delta
= -( event
.detail
/ 3 );
3265 if ( event
.preventDefault
)
3267 event
.preventDefault();
3272 if ( delta
>= PS
.FORWARD
)
3278 delta
= PS
.BACKWARD
;
3281 // Send delta to user
3289 PS
.Oops("PS.Wheel() failed [" + err
.message
+ "]" );
3293 event
.returnValue
= false;
3298 PS
.Sys = function ()
3305 // Init audio support, preload error sound
3308 PS
.AudioLoad("fx_uhoh");
3310 // Make sure all required game functions exist
3312 if ( typeof PS
.Init
!== "function" )
3315 PS
.Oops(fn
+ "WARNING: PS.Init function undefined");
3318 if ( typeof PS
.Click
!== "function" )
3321 PS
.Oops(fn
+ "WARNING: PS.Click function undefined");
3324 if ( typeof PS
.Release
!== "function" )
3327 PS
.Oops(fn
+ "WARNING: PS.Release function undefined");
3330 if ( typeof PS
.Enter
!== "function" )
3333 PS
.Oops(fn
+ "WARNING: PS.Enter function undefined");
3336 if ( typeof PS
.Leave
!== "function" )
3339 PS
.Oops(fn
+ "WARNING: PS.Leave function undefined");
3342 if ( typeof PS
.KeyDown
!== "function" )
3345 PS
.Oops(fn
+ "WARNING: PS.KeyDown function undefined");
3348 if ( typeof PS
.KeyUp
!== "function" )
3351 PS
.Oops(fn
+ "WARNING: PS.KeyUp function undefined");
3354 if ( typeof PS
.Wheel
!== "function" )
3357 PS
.Oops(fn
+ "WARNING: PS.Wheel function undefined");
3360 // Set up mouse/keyboard events
3362 e
= document
.getElementById("screen");
3365 e
.addEventListener("mousedown", PS
.MouseDown
, false);
3366 e
.addEventListener("mouseup", PS
.MouseUp
, false);
3367 e
.addEventListener("mouseout", PS
.MouseOut
, false);
3368 e
.addEventListener("mousemove", PS
.MouseMove
, false);
3371 // Set up mouse wheel events
3373 if ( window
.addEventListener
)
3375 window
.addEventListener('DOMMouseScroll', PS
.SysWheel
, false); // for Firefox
3376 window
.addEventListener('mousewheel', PS
.SysWheel
, false); // for others
3380 window
.onmousewheel
= document
.onmousewheel
= PS
.SysWheel
; // for IE, maybe
3383 // Setup offscreen canvas for image manipulation
3385 PS
.ImageCanvas
= document
.createElement("canvas");
3386 PS
.ImageCanvas
.width
= PS
.GRID_MAX
;
3387 PS
.ImageCanvas
.height
= PS
.GRID_MAX
;
3391 window
.setInterval (PS
.Timer
, PS
.DEFAULT_FPS
);
3393 // Print version number
3395 e
= document
.getElementById("footer");
3398 e
.innerHTML
= "Version " + PS
.VERSION
;
3405 PS
.Init(); // call user initializer
3409 PS
.Oops("PS.Init() failed [" + err
.message
+ "]" );
3414 PS
.GridSize(PS
.GRID_DEFAULT_WIDTH
, PS
.GRID_DEFAULT_HEIGHT
);
3420 PS
.Random = function (val
)
3425 fn
= "[PS.Random] ";
3426 if ( typeof val
!== "number" )
3428 PS
.Oops(fn
+ "Parameter is not a number");
3431 val
= Math
.floor(val
);
3437 val
= Math
.random() * val
;
3438 val
= Math
.floor(val
) + 1;
3444 "c1", "db1", "d1", "eb1", "e1", "f1", "gb1", "g1", "ab1", "a1", "bb1", "b1",
3445 "c2", "db2", "d2", "eb2", "e2", "f2", "gb2", "g2", "ab2", "a2", "bb2", "b2",
3446 "c3", "db3", "d3", "eb3", "e3", "f3", "gb3", "g3", "ab3", "a3", "bb3", "b3",
3447 "c4", "db4", "d4", "eb4", "e4", "f4", "gb4", "g4", "ab4", "a4", "bb4", "b4",
3448 "c5", "db5", "d5", "eb5", "e5", "f5", "gb5", "g5", "ab5", "a5", "bb5", "b5",
3449 "c6", "db6", "d6", "eb6", "e6", "f6", "gb6", "g6", "ab6", "a6", "bb6", "b6",
3450 "c7", "db7", "d7", "eb7", "e7", "f7", "gb7", "g7", "ab7", "a7", "bb7", "b7",
3454 PS
.Piano = function ( val
, flag
)
3461 if ( typeof val
!== "number" )
3463 PS
.Oops(fn
+ "Parameter is not a number");
3466 val
= Math
.floor(val
);
3471 else if ( val
> 88 )
3476 str
= "piano_" + PS
.PianoFiles
[val
- 1];
3486 "c3", "db3", "d3", "eb3", "e3", "f3", "gb3", "g3", "ab3", "a3", "bb3", "b3",
3487 "c4", "db4", "d4", "eb4", "e4", "f4", "gb4", "g4", "ab4", "a4", "bb4", "b4",
3488 "c5", "db5", "d5", "eb5", "e5", "f5", "gb5", "g5", "ab5", "a5", "bb5", "b5",
3489 "c6", "db6", "d6", "eb6", "e6", "f6", "gb6", "g6", "ab6", "a6", "bb6", "b6",
3490 "c7", "db7", "d7", "eb7", "e7", "f7"
3493 PS
.Harpsichord = function ( val
, flag
)
3498 fn
= "[PS.Harpsichord] ";
3500 if ( typeof val
!== "number" )
3502 PS
.Oops(fn
+ "Parameter is not a number");
3505 val
= Math
.floor(val
);
3510 else if ( val
> 57 )
3515 str
= "hchord_" + PS
.HchordFiles
[val
- 1];
3525 "c5", "db5", "d5", "eb5", "e5", "f5", "gb5", "g5", "ab5", "a5", "bb5", "b5",
3526 "c6", "db6", "d6", "eb6", "e6", "f6", "gb6", "g6", "ab6", "a6", "bb6", "b6",
3527 "c7", "db7", "d7", "eb7", "e7", "f7", "gb7", "g7", "ab7", "a7", "bb7", "b7"
3530 PS
.Xylophone = function ( val
)
3535 fn
= "[PS.Xylophone] ";
3537 if ( typeof val
!== "number" )
3539 PS
.Oops(fn
+ "Parameter is not a number");
3542 val
= Math
.floor(val
);
3547 else if ( val
> 39 )
3552 str
= "xylo_" + PS
.XyloFiles
[val
- 1];
3558 PS
.AUDIO_PATH_DEFAULT
= "http://users.wpi.edu/~bmoriarty/ps/audio/"; // case sensitive!
3559 PS
.AUDIO_PATH
= PS
.AUDIO_PATH_DEFAULT
;
3560 PS
.AUDIO_MAX_CHANNELS
= 32;
3561 PS
.AudioChannels
= [];
3563 PS
.AudioInit = function ()
3568 for ( i
= 0; i
< PS
.AUDIO_MAX_CHANNELS
; i
+= 1 )
3570 PS
.AudioChannels
[i
] = {};
3571 PS
.AudioChannels
[i
].audio
= new Audio();
3572 PS
.AudioChannels
[i
].done
= -1;
3573 PS
.AudioChannels
[i
].id
= "";
3577 PS
.AudioError = function (obj
)
3586 str
= "MEDIA_ERR_ABORTED";
3589 str
= "MEDIA_ERR_NETWORK";
3592 str
= "MEDIA_ERR_DECODE";
3595 str
= "MEDIA_ERR_SRC_NOT_SUPPORTED";
3602 PS
.Oops("[Audio Error: " + str
+ "]\n");
3605 PS
.AudioPath = function (path
)
3610 fn
= "[PS.AudioPath] ";
3612 if ( path
=== PS
.DEFAULT
)
3614 PS
.AUDIO_PATH
= PS
.AUDIO_PATH_DEFAULT
;
3616 else if ( typeof path
!== "string" )
3618 PS
.Oops(fn
+ "path parameter is not a string");
3623 PS
.AUDIO_PATH
= path
;
3626 return PS
.AUDIO_PATH
;
3629 PS
.AudioLoad = function (id
, path
)
3634 fn
= "[PS.AudioLoad] ";
3636 if ( typeof id
!== "string" )
3638 PS
.Oops(fn
+ "id parameter is not a string");
3641 if ( id
.length
< 1 )
3643 PS
.Oops(fn
+ "id parameter is an empty string");
3647 if ( path
=== undefined )
3649 path
= PS
.AUDIO_PATH
;
3651 else if ( path
=== PS
.DEFAULT
)
3653 path
= PS
.AUDIO_PATH_DEFAULT
;
3655 else if ( typeof path
!== "string" )
3657 PS
.Oops(fn
+ "path parameter is not a string");
3661 // Already got this? Clone it
3663 snd
= document
.getElementById(id
);
3669 path
= path
+ id
+ ".wav";
3671 snd
= document
.createElement("audio");
3672 snd
.setAttribute("src", path
);
3673 snd
.setAttribute("id", id
);
3674 snd
.setAttribute("preload", "auto");
3675 snd
.setAttribute("onerror", "PS.AudioError(this)");
3677 // src = document.createElement("source");
3678 // src.setAttribute("src", path + ".ogg");
3679 // src.setAttribute("type", "audio/ogg");
3680 // snd.appendChild(src);
3682 // src = document.createElement("source");
3683 // src.setAttribute("src", path + ".mp3");
3684 // src.setAttribute("type", "audio/mpeg");
3685 // src.setAttribute("onerror", "PS.AudioError()");
3686 // snd.appendChild(src);
3688 // src = document.createElement("source");
3689 // src.setAttribute("src", path + ".wav");
3690 // src.setAttribute("type", "audio/x-wav");
3691 // snd.appendChild(src);
3693 document
.body
.appendChild(snd
);
3699 // Returns a channel number
3701 PS
.AudioPlay = function (id
, volume
, func
, path
)
3704 var fn
, i
, snd
, d
, t
, channel
;
3706 fn
= "[PS.AudioPlay] ";
3708 if ( (volume
=== undefined) || (volume
=== PS
.DEFAULT
) )
3710 volume
= PS
.DEFAULT_VOLUME
;
3712 else if ( typeof volume
!== "number" )
3714 PS
.Oops(fn
+ "volume parameter is not a number");
3717 else if ( volume
< 0 )
3721 else if ( volume
> PS
.DEFAULT_VOLUME
)
3723 volume
= PS
.DEFAULT_VOLUME
;
3726 if ( (func
!== undefined) && (typeof func
!== "function") )
3728 PS
.Oops(fn
+ "func parameter is not a function");
3732 snd
= PS
.AudioLoad(id
, path
);
3733 if ( snd
!== PS
.ERROR
)
3735 snd
.volume
= volume
;
3736 if ( func
!== undefined )
3738 snd
.addEventListener("ended", func
);
3740 for ( i
= 0; i
< PS
.AUDIO_MAX_CHANNELS
; i
+= 1 )
3744 channel
= PS
.AudioChannels
[i
];
3745 if ( channel
.done
< t
)
3747 channel
.done
= t
+ ( snd
.duration
* 1000 );
3748 channel
.audio
= snd
;
3750 snd
.load(); // WHY???
3752 return i
+ 1; // channel id
3757 return PS
.ERROR
; // error
3760 // Stops playback of channel number
3762 PS
.AudioStop = function (c
)
3765 var fn
, d
, t
, channel
;
3767 fn
= "[PS.AudioStop] ";
3769 if ( typeof c
!== "number" )
3771 PS
.Oops(fn
+ "Parameter is not a number");
3775 if ( (c
< 1) || (c
> PS
.AUDIO_MAX_CHANNELS
) )
3777 PS
.Oops(fn
+ "Invalid channel id");
3781 channel
= PS
.AudioChannels
[c
- 1];
3785 channel
.done
= t
; // mark as done playing
3786 channel
.audio
.pause();
3787 channel
.audio
.currentTime
= 0;
3792 // Pauses/unpauses playback of channel number
3794 PS
.AudioPause = function (c
)
3797 var fn
, channel
, audio
;
3799 fn
= "[PS.AudioPause] ";
3801 if ( typeof c
!== "number" )
3803 PS
.Oops(fn
+ "Parameter is not a number");
3807 if ( (c
< 1) || (c
> PS
.AUDIO_MAX_CHANNELS
) )
3809 PS
.Oops(fn
+ "Invalid channel id");
3813 channel
= PS
.AudioChannels
[c
- 1];
3814 audio
= channel
.audio
;
3829 PS
.ImageLoad = function ( file
, func
)
3834 fn
= "[PS.ImageLoad] ";
3836 if ( typeof file
!== "string" )
3838 PS
.Oops(fn
+ "Parameter 1 is not a string");
3841 if ( file
.length
< 1 )
3843 PS
.Oops(fn
+ "Parameter 1 is an empty string");
3846 if ( (func
!== undefined) && (typeof func
!== "function") )
3848 PS
.Oops(fn
+ "Parameter 2 is not a function");
3854 if ( func
!== undefined )
3863 PS
.Oops(fn
+ "failed [" + err
.message
+ "]");
3868 // Extract an imageData table from an image file
3869 // optional [alpha] determines if alpha data if included
3871 PS
.ImageData = function ( img
, alpha
)
3874 var fn
, w
, h
, ctx
, imgData
, imgData2
, i
, j
, len
, d1
, d2
;
3876 fn
= "[PS.ImageMap] ";
3877 if ( (alpha
=== undefined) || !alpha
)
3887 if ( (typeof w
!== "number") || (w
< 0) )
3889 PS
.Oops(fn
+ "Invalid width parameter");
3893 if ( w
> PS
.GRID_MAX
)
3899 if ( (typeof h
!== "number") || (h
< 0) )
3901 PS
.Oops(fn
+ "Invalid height parameter");
3905 if ( h
> PS
.GRID_MAX
)
3910 // draw the image onto the offscreen canvas
3914 ctx
= PS
.ImageCanvas
.getContext("2d");
3915 ctx
.drawImage(img
, 0, 0);
3919 PS
.Oops(fn
+ "failed @ 1 [" + err
.message
+ "]");
3923 // fetch the data and return it
3927 imgData
= ctx
.getImageData(0, 0, w
, h
);
3931 PS
.Oops(fn
+ "failed @ 2 [" + err2
.message
+ "]");
3935 // imgData is read-only for some reason
3936 // so make a copy of it
3939 imgData2
.width
= imgData
.width
;
3940 imgData2
.height
= imgData
.height
;
3944 d2
= []; // new data array
3948 // if alpha data is not wanted, remove it
3952 d2
.length
= (len
/ 4) * 3;
3962 i
+= 2; // skip alpha
3965 imgData2
.pixelSize
= 3;
3985 imgData2
.pixelSize
= 4;
3992 PS
.ImageBlit = function ( imgdata
, xpos
, ypos
)
3995 var fn
, size
, bytes
, w
, h
, pixsize
, ptr
, drawx
, drawy
, i
, j
, r
, g
, b
, a
, k
, bead
;
3997 fn
= "[PS.ImageBlit] ";
3999 // verify data format
4001 if ( typeof imgdata
!== "object" )
4003 PS
.Oops(fn
+ "parameter 1 is not a table");
4007 bytes
= imgdata
.data
; // check this?
4010 if ( typeof w
!== "number" )
4012 PS
.Oops(fn
+ "imgdata.width is not a number");
4018 PS
.Oops(fn
+ "imgdata.width < 1");
4023 if ( typeof h
!== "number" )
4025 PS
.Oops(fn
+ "imgdata.height is not a number");
4031 PS
.Oops(fn
+ "imgdata.height < 1");
4035 pixsize
= imgdata
.pixelSize
;
4036 if ( (pixsize
!== 3) && (pixsize
!== 4) )
4038 PS
.Oops(fn
+ "invalid pixelSize");
4042 size
= w
* h
* pixsize
;
4043 if ( bytes
.length
!== size
)
4045 PS
.Oops(fn
+ "invalid data length [" + imgdata
.data
.length
+ "]");
4049 if ( (xpos
=== undefined) || (xpos
=== PS
.DEFAULT
) )
4053 else if ( typeof xpos
!== "number" )
4055 PS
.Oops(fn
+ "parameter 2 is not a number");
4060 xpos
= Math
.floor(xpos
);
4062 // exit if drawing off grid
4064 if ( (xpos
>= PS
.Grid
.x
) || ((xpos
+ w
) < 1) )
4070 if ( (ypos
=== undefined) || (ypos
=== PS
.DEFAULT
) )
4074 else if ( typeof ypos
!== "number" )
4076 PS
.Oops(fn
+ "parameter 3 is not a number");
4081 ypos
= Math
.floor(ypos
);
4083 // exit if drawing off grid
4085 if ( (ypos
>= PS
.Grid
.y
) || ((ypos
+ h
) < 1) )
4093 for ( j
= 0; j
< h
; j
+= 1 )
4096 if ( drawy
>= PS
.Grid
.y
) // exit if off bottom of grid
4100 if ( drawy
>= 0 ) // is this row visible?
4102 for ( i
= 0; i
< w
; i
+= 1 )
4104 if ( (drawx
>= 0) && (drawx
< PS
.Grid
.x
) )
4107 if ( typeof r
!== "number" )
4109 PS
.Oops(fn
+ "non-numeric red at position " + ptr
);
4123 if ( typeof g
!== "number" )
4125 PS
.Oops(fn
+ "non-numeric green at position " + ptr
);
4139 if ( typeof b
!== "number" )
4141 PS
.Oops(fn
+ "non-numeric blue at position " + ptr
);
4154 if ( pixsize
=== 4 )
4157 if ( typeof a
!== "number" )
4159 PS
.Oops(fn
+ "non-numeric alpha at position " + ptr
);
4162 a
= Math
.floor(a
/ 2.55); // convert 0-255 range to 0-100
4167 else if ( a
> PS
.DEFAULT_ALPHA
)
4169 a
= PS
.DEFAULT_ALPHA
;
4174 a
= PS
.DEFAULT_ALPHA
;
4177 k
= drawx
+ (drawy
* PS
.Grid
.x
); // get index of bead
4178 bead
= PS
.Grid
.beads
[k
];
4180 bead
.dirty
= true; // mark this bead as explicitly colored
4181 if ( a
< PS
.DEFAULT_ALPHA
) // Calc new color based on alpha-adjusted color of existing bead
4183 bead
.alphaRed
= PS
.Dissolve( PS
.Grid
.bgRed
, bead
.alphaRed
, a
);
4184 bead
.alphaGreen
= PS
.Dissolve( PS
.Grid
.bgGreen
, bead
.alphaGreen
, a
);
4185 bead
.alphaBlue
= PS
.Dissolve( PS
.Grid
.bgBlue
, bead
.alphaBlue
, a
);
4186 bead
.color
= PS
.RGBString( bead
.alphaRed
, bead
.alphaGreen
, bead
.alphaBlue
);
4191 bead
.alphaGreen
= g
;
4193 bead
.color
= PS
.RGBString( r
, g
, b
);
4196 // Do NOT change alpha value of existing bead!
4204 bead
.flashPhase
= 100; // stops flash in progress
4205 bead
.colorNow
= bead
.color
;
4215 ptr
+= (w
* pixsize
); // skip this row