From 65a79b053aef639c8fd8adeffa2e8912700b73f4 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 3 Nov 2021 16:54:53 +0000 Subject: [PATCH] Bangle.js2: Add Unistroke object, and 'Bangle.stroke' event --- ChangeLog | 1 + boards/BANGLEJS2.py | 1 + libs/banglejs/jswrap_bangle.c | 144 +++--- libs/misc/jswrap_unistroke.c | 50 ++ libs/misc/jswrap_unistroke.h | 21 + libs/misc/unistroke.c | 285 +++++++----- libs/misc/unistroke.h | 13 +- src/jsutils.c | 30 ++ src/jsutils.h | 13 + tests/test_language_FAIL.js | 827 ---------------------------------- 10 files changed, 377 insertions(+), 1008 deletions(-) create mode 100644 libs/misc/jswrap_unistroke.c create mode 100644 libs/misc/jswrap_unistroke.h delete mode 100644 tests/test_language_FAIL.js diff --git a/ChangeLog b/ChangeLog index 2797bdf38..3ea6b3411 100644 --- a/ChangeLog +++ b/ChangeLog @@ -76,6 +76,7 @@ Fix break scoping error in nested for loops (fix #2084) Fix memory leak when rejecting pre-resolved promise (test_promise11.js) Fix parser errors in arrow functions (fix #2067) + Bangle.js2: Add Unistroke object, and 'Bangle.stroke' event 2v10 : Bangle.js: Improved HRM calculations - swapped autocorrelation for bandpass filter Bangle.js: Significantly improved step counting algorithm using bandpass filter (fix #1846) diff --git a/boards/BANGLEJS2.py b/boards/BANGLEJS2.py index 7c0be1a83..d74661587 100644 --- a/boards/BANGLEJS2.py +++ b/boards/BANGLEJS2.py @@ -64,6 +64,7 @@ info = { 'SOURCES += libs/misc/heartrate.c', 'SOURCES += libs/misc/hrm_vc31.c', 'SOURCES += libs/misc/unistroke.c', + 'WRAPPERSOURCES += libs/misc/jswrap_unistroke.c', 'DEFINES += -DESPR_BANGLE_UNISTROKE=1', 'SOURCES += libs/banglejs/banglejs2_storage_default.c', 'DEFINES += -DESPR_STORAGE_INITIAL_CONTENTS=1', # diff --git a/libs/banglejs/jswrap_bangle.c b/libs/banglejs/jswrap_bangle.c index 5e5933636..dc82e1cc5 100644 --- a/libs/banglejs/jswrap_bangle.c +++ b/libs/banglejs/jswrap_bangle.c @@ -347,6 +347,37 @@ Emitted when the touchscreen is pressed } Emitted when the touchscreen is dragged or released */ +/*JSON{ + "type" : "event", + "class" : "Bangle", + "name" : "stroke", + "params" : [["event","JsVar","Object of form `{xy:Uint8Array([x1,y1,x2,y2...])}` containing touch coordinates"]], + "ifdef" : "BANGLEJS2" +} +Emitted when the touchscreen is dragged for a large enough distance to count as a gesture. + +If Bangle.strokes is defined and populated with data from `Unistroke.new`, the `event` argument will also +contain a `stroke` field containing the most closely matching stroke name. + +For example: + +``` +Bangle.strokes = { + up : Unistroke.new(new Uint8Array([57, 151, ... 158, 137])), + alpha : Unistroke.new(new Uint8Array([161, 55, ... 159, 161])), +}; +Bangle.on('stroke',o=>{ + print(o.stroke); + g.clear(1).drawPoly(o.xy); +}); +// Might print something like +{ + "xy": new Uint8Array([149, 50, ... 107, 136]), + "stroke": "alpha" +} +``` +*/ + #define ACCEL_HISTORY_LEN 50 ///< Number of samples of accelerometer history @@ -467,6 +498,14 @@ unsigned char touchX, touchY; ///< current touch event coordinates unsigned char lastTouchX, lastTouchY; ///< last touch event coordinates - updated when JSBT_DRAG is fired bool touchPts, lastTouchPts; ///< whether a fnger is currently touching or not unsigned char touchType; ///< variable to differentiate press, long press, double press +typedef enum { + TG_SWIPE_NONE, + TG_SWIPE_LEFT, + TG_SWIPE_RIGHT, + TG_SWIPE_UP, + TG_SWIPE_DOWN, +} TouchGestureType; +TouchGestureType touchGesture; /// is JSBT_SWIPE is set, what happened? #endif #ifdef PRESSURE_I2C @@ -700,23 +739,22 @@ typedef enum { JSBT_HRM_DATA = 1<<16, ///< Heart rate data is ready for analysis JSBT_CHARGE_EVENT = 1<<17, ///< we need to fire a charging event JSBT_STEP_EVENT = 1<<18, ///< we've detected a step via the pedometer - JSBT_SWIPE_LEFT = 1<<19, ///< swiped left over touchscreen - JSBT_SWIPE_RIGHT = 1<<20, ///< swiped right over touchscreen - JSBT_SWIPE_UP = 1<<21, ///< swiped left over touchscreen (Bangle 2 only) - JSBT_SWIPE_DOWN = 1<<22, ///< swiped right over touchscreen (Bangle 2 only) - JSBT_SWIPE_MASK = JSBT_SWIPE_LEFT | JSBT_SWIPE_RIGHT | JSBT_SWIPE_UP | JSBT_SWIPE_DOWN, - JSBT_TOUCH_LEFT = 1<<23, ///< touch lhs of touchscreen - JSBT_TOUCH_RIGHT = 1<<24, ///< touch rhs of touchscreen + JSBT_SWIPE = 1<<19, ///< swiped over touchscreen, info in touchGesture + JSBT_TOUCH_LEFT = 1<<20, ///< touch lhs of touchscreen + JSBT_TOUCH_RIGHT = 1<<21, ///< touch rhs of touchscreen JSBT_TOUCH_MASK = JSBT_TOUCH_LEFT | JSBT_TOUCH_RIGHT, #ifdef TOUCH_DEVICE - JSBT_DRAG = 1<<25, + JSBT_DRAG = 1<<22, #endif - JSBT_TWIST_EVENT = 1<<26, ///< Watch was twisted - JSBT_FACE_UP = 1<<27, ///< Watch was turned face up/down (faceUp holds the actual state) - JSBT_ACCEL_INTERVAL_DEFAULT = 1<<28, ///< reschedule accelerometer poll handler to default speed - JSBT_ACCEL_INTERVAL_POWERSAVE = 1<<29, ///< reschedule accelerometer poll handler to powersave speed - JSBT_HRM_INSTANT_DATA = 1<<30, ///< Instant heart rate data - JSBT_HEALTH = 1<<31, ///< New 'health' event +#if ESPR_BANGLE_UNISTROKE + JSBT_STROKE = 1<<23, // a gesture has been made on the touchscreen +#endif + JSBT_TWIST_EVENT = 1<<24, ///< Watch was twisted + JSBT_FACE_UP = 1<<25, ///< Watch was turned face up/down (faceUp holds the actual state) + JSBT_ACCEL_INTERVAL_DEFAULT = 1<<26, ///< reschedule accelerometer poll handler to default speed + JSBT_ACCEL_INTERVAL_POWERSAVE = 1<<27, ///< reschedule accelerometer poll handler to powersave speed + JSBT_HRM_INSTANT_DATA = 1<<28, ///< Instant heart rate data + JSBT_HEALTH = 1<<29, ///< New 'health' event } JsBangleTasks; JsBangleTasks bangleTasks; @@ -800,34 +838,6 @@ void lcd_flip(JsVar *parent, bool all) { #endif } -static char clipi8(int x) { - if (x<-128) return -128; - if (x>127) return 127; - return (char)x; -} - -static int twosComplement(int val, unsigned char bits) { - if (val & ((unsigned int)1 << (bits - 1))) - val -= (unsigned int)1 << bits; - return val; -} - -// quick integer square root -// https://stackoverflow.com/questions/31117497/fastest-integer-square-root-in-the-least-amount-of-instructions -static unsigned short int int_sqrt32(unsigned int x) { - unsigned short int res=0; - unsigned short int add= 0x8000; - int i; - for(i=0;i<16;i++) { - unsigned short int temp=res | add; - unsigned int g2=temp*temp; - if (x>=g2) - res=temp; - add>>=1; - } - return res; -} - /// Clear the given health state back to defaults static void healthStateClear(HealthState *health) { memset(health, 0, sizeof(HealthState)); @@ -1376,12 +1386,14 @@ bool btnTouchHandler() { if ((touchLastState2==TS_RIGHT && touchLastState==TS_BOTH && state==TS_LEFT) || (touchLastState==TS_RIGHT && state==1)) { touchStatus |= TS_SWIPED; - bangleTasks |= JSBT_SWIPE_LEFT; + touchGesture = TG_SWIPE_LEFT; + bangleTasks |= JSBT_SWIPE; } if ((touchLastState2==TS_LEFT && touchLastState==TS_BOTH && state==TS_RIGHT) || (touchLastState==TS_LEFT && state==TS_RIGHT)) { touchStatus |= TS_SWIPED; - bangleTasks |= JSBT_SWIPE_RIGHT; + touchGesture = TG_SWIPE_RIGHT; + bangleTasks |= JSBT_SWIPE; } if (!state) { if (touchLastState && !(touchStatus&TS_SWIPED)) { @@ -1428,6 +1440,9 @@ void touchHandlerInternal(int tx, int ty, int pts, int gesture) { // ignore if locked if (bangleFlags & JSBF_LOCKED) return; + int dx = tx-touchX; + int dy = ty-touchY; + touchX = tx; touchY = ty; touchPts = pts; @@ -1436,17 +1451,21 @@ void touchHandlerInternal(int tx, int ty, int pts, int gesture) { switch (gesture) { // gesture case 0:break; // no gesture case 1: // slide down - bangleTasks |= JSBT_SWIPE_DOWN; - break; + touchGesture = TG_SWIPE_DOWN; + bangleTasks |= JSBT_SWIPE; + break; case 2: // slide up - bangleTasks |= JSBT_SWIPE_UP; - break; + touchGesture = TG_SWIPE_UP; + bangleTasks |= JSBT_SWIPE; + break; case 3: // slide left - bangleTasks |= JSBT_SWIPE_LEFT; - break; + touchGesture = TG_SWIPE_LEFT; + bangleTasks |= JSBT_SWIPE; + break; case 4: // slide right - bangleTasks |= JSBT_SWIPE_RIGHT; - break; + touchGesture = TG_SWIPE_RIGHT; + bangleTasks |= JSBT_SWIPE; + break; case 5: // single click if (touchX<80) bangleTasks |= JSBT_TOUCH_LEFT; else bangleTasks |= JSBT_TOUCH_RIGHT; @@ -1470,8 +1489,11 @@ void touchHandlerInternal(int tx, int ty, int pts, int gesture) { // ensure we don't sleep if touchscreen is being used inactivityTimer = 0; #if ESPR_BANGLE_UNISTROKE - unistroke_touch(touchX, touchY, touchPts); + if (unistroke_touch(touchX, touchY, dx, dy, touchPts)) { + bangleTasks |= JSBT_STROKE; + } #endif + jshHadEvent(); } lastGesture = gesture; @@ -3500,11 +3522,12 @@ bool jswrap_banglejs_idle() { inactivityTimer = 0; } } - if (bangleTasks & JSBT_SWIPE_MASK) { + if (bangleTasks & JSBT_SWIPE) { JsVar *o[2] = { - jsvNewFromInteger((bangleTasks & JSBT_SWIPE_LEFT)?-1:((bangleTasks & JSBT_SWIPE_RIGHT)?1:0)), - jsvNewFromInteger((bangleTasks & JSBT_SWIPE_UP)?-1:((bangleTasks & JSBT_SWIPE_DOWN)?1:0)), + jsvNewFromInteger((touchGesture==TG_SWIPE_LEFT)?-1:((touchGesture==TG_SWIPE_RIGHT)?1:0)), + jsvNewFromInteger((touchGesture==TG_SWIPE_UP)?-1:((touchGesture==TG_SWIPE_DOWN)?1:0)), }; + touchGesture = TG_SWIPE_NONE; jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"swipe", o, 2); jsvUnLockMany(2,o); } @@ -3542,6 +3565,15 @@ bool jswrap_banglejs_idle() { lastTouchY = touchY; lastTouchPts = touchPts; } +#endif +#if ESPR_BANGLE_UNISTROKE + if (bangleTasks & JSBT_STROKE) { + JsVar *o = unistroke_getEventVar(); + if (o) { + jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"stroke", &o, 1); + jsvUnLock(o); + } + } #endif jsvUnLock(bangle); bangleTasks = JSBT_NONE; diff --git a/libs/misc/jswrap_unistroke.c b/libs/misc/jswrap_unistroke.c new file mode 100644 index 000000000..859f65c6e --- /dev/null +++ b/libs/misc/jswrap_unistroke.c @@ -0,0 +1,50 @@ +/* + * This file is part of Espruino, a JavaScript interpreter for Microcontrollers + * + * Copyright (C) 2013 Gordon Williams + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * ---------------------------------------------------------------------------- + * Unistroke handling functionality + * ---------------------------------------------------------------------------- + */ +#include "jswrap_unistroke.h" +#include "unistroke.h" + +/*JSON{ + "type" : "staticmethod", + "class" : "Unistroke", + "name" : "new", + "ifdef" : "BANGLEJS2", + "generate" : "jswrap_unistroke_new", + "params" : [ + ["xy","JsVar","An array of interleaved XY coordinates"] + ], + "return" : ["JsVar","A string of data representing this unistroke"] +} +Create a new Unistroke based on XY coordinates +*/ +JsVar *jswrap_unistroke_new(JsVar *xy) { + return unistroke_convert(xy); +} + +/*JSON{ + "type" : "staticmethod", + "class" : "Unistroke", + "name" : "recognise", + "ifdef" : "BANGLEJS2", + "generate" : "jswrap_unistroke_recognise", + "params" : [ + ["strokes","JsVar","An object of named strokes : `{arrow:..., circle:...}`"], + ["xy","JsVar","An array of interleaved XY coordinates"] + ], + "return" : ["JsVar","The key name of the matched stroke"] +} +Recognise based on an object of named strokes, and a list of XY coordinates +*/ +JsVar *jswrap_unistroke_recognise(JsVar *strokes, JsVar *xy) { + return unistroke_recognise_xy(strokes, xy); +} diff --git a/libs/misc/jswrap_unistroke.h b/libs/misc/jswrap_unistroke.h new file mode 100644 index 000000000..118458a13 --- /dev/null +++ b/libs/misc/jswrap_unistroke.h @@ -0,0 +1,21 @@ +/* + * This file is part of Espruino, a JavaScript interpreter for Microcontrollers + * + * Copyright (C) 2013 Gordon Williams + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * ---------------------------------------------------------------------------- + * Unistroke handling functionality + * ---------------------------------------------------------------------------- + */ +#include "jsutils.h" +#include "jsvar.h" + +/// Create a new Unistroke based on XY coordinates +JsVar *jswrap_unistroke_new(JsVar *xy); + +/// Recognise based on an object of named strokes, and a list of XY coordinates +JsVar *jswrap_unistroke_recognise(JsVar *strokes, JsVar *xy); diff --git a/libs/misc/unistroke.c b/libs/misc/unistroke.c index 43dbf4b49..1128612d7 100644 --- a/libs/misc/unistroke.c +++ b/libs/misc/unistroke.c @@ -67,16 +67,32 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ +/* TODO for Bangle.js + + * Move 'points' to an array of int8 + * + + */ + #include #include #include #include #include "jsinteractive.h" -#define NUMPOINTS 64 +#define NUMPOINTS 32 #define SQUARESIZE 176 #define PI 3.141592f #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) + +// DollarRecognizer constants +// + +const float Diagonal = sqrtf(SQUARESIZE * SQUARESIZE + SQUARESIZE * SQUARESIZE); +const float AngleRange = 45.0f * PI / 180.0f; +const float AnglePrecision = 2.0f * PI / 180.0f; +const float Phi = 0.5f * (-1.0f + sqrtf(5.0f)); // Golden Ratio + // // Point class // @@ -87,7 +103,6 @@ typedef struct { float X,Y,width,height; } Rectangle; typedef struct { - char name[8]; Point points[NUMPOINTS]; } Unistroke; @@ -107,125 +122,60 @@ float PathLength(Point *points, int pointsLen); float Distance(Point p1, Point p2); float Deg2Rad(float d); -/*Unistroke newUnistroke(char *name, Point* points) { -}*/ -// -// Unistroke class: a unistroke template -// -/*function Unistroke(name, points) // constructor -{ - this.Name = name; - this.Points = Resample(points, NUMPOINTS); - var radians = IndicativeAngle(this.Points); - this.Points = RotateBy(this.Points, -radians); - this.Points = ScaleTo(this.Points, SQUARESIZE); - this.Points = TranslateTo(this.Points, Origin); - this.Vector = Vectorize(this.Points); // for Protractor -}*/ -// -// Result class -// -/*void newResult(name, score, ms) { // constructor - this.Name = name; - this.Score = score; - this.Time = ms; -}*/ -// -// DollarRecognizer constants -// - -const Point Origin = {0,0}; -const float Diagonal = sqrtf(SQUARESIZE * SQUARESIZE + SQUARESIZE * SQUARESIZE); -const float AngleRange = 45.0 * PI / 180.0f; -const float AnglePrecision = 2.0 * PI / 180.0f; -const float Phi = 0.5 * (-1.0 + sqrtf(5.0)); // Golden Ratio -// -// DollarRecognizer class -// -void DollarRecognizer() // constructor -{ - // - // one built-in unistroke per gesture type - // - /*this.Unistrokes = new Array(NumUnistrokes); - this.Unistrokes[0] = newUnistroke("triangle", new Array(new Point(137,139),new Point(135,141),new Point(133,144),new Point(132,146),new Point(130,149),new Point(128,151),new Point(126,155),new Point(123,160),new Point(120,166),new Point(116,171),new Point(112,177),new Point(107,183),new Point(102,188),new Point(100,191),new Point(95,195),new Point(90,199),new Point(86,203),new Point(82,206),new Point(80,209),new Point(75,213),new Point(73,213),new Point(70,216),new Point(67,219),new Point(64,221),new Point(61,223),new Point(60,225),new Point(62,226),new Point(65,225),new Point(67,226),new Point(74,226),new Point(77,227),new Point(85,229),new Point(91,230),new Point(99,231),new Point(108,232),new Point(116,233),new Point(125,233),new Point(134,234),new Point(145,233),new Point(153,232),new Point(160,233),new Point(170,234),new Point(177,235),new Point(179,236),new Point(186,237),new Point(193,238),new Point(198,239),new Point(200,237),new Point(202,239),new Point(204,238),new Point(206,234),new Point(205,230),new Point(202,222),new Point(197,216),new Point(192,207),new Point(186,198),new Point(179,189),new Point(174,183),new Point(170,178),new Point(164,171),new Point(161,168),new Point(154,160),new Point(148,155),new Point(143,150),new Point(138,148),new Point(136,148))); - this.Unistrokes[1] = newUnistroke("x", new Array(new Point(87,142),new Point(89,145),new Point(91,148),new Point(93,151),new Point(96,155),new Point(98,157),new Point(100,160),new Point(102,162),new Point(106,167),new Point(108,169),new Point(110,171),new Point(115,177),new Point(119,183),new Point(123,189),new Point(127,193),new Point(129,196),new Point(133,200),new Point(137,206),new Point(140,209),new Point(143,212),new Point(146,215),new Point(151,220),new Point(153,222),new Point(155,223),new Point(157,225),new Point(158,223),new Point(157,218),new Point(155,211),new Point(154,208),new Point(152,200),new Point(150,189),new Point(148,179),new Point(147,170),new Point(147,158),new Point(147,148),new Point(147,141),new Point(147,136),new Point(144,135),new Point(142,137),new Point(140,139),new Point(135,145),new Point(131,152),new Point(124,163),new Point(116,177),new Point(108,191),new Point(100,206),new Point(94,217),new Point(91,222),new Point(89,225),new Point(87,226),new Point(87,224))); - this.Unistrokes[2] = newUnistroke("rectangle", new Array(new Point(78,149),new Point(78,153),new Point(78,157),new Point(78,160),new Point(79,162),new Point(79,164),new Point(79,167),new Point(79,169),new Point(79,173),new Point(79,178),new Point(79,183),new Point(80,189),new Point(80,193),new Point(80,198),new Point(80,202),new Point(81,208),new Point(81,210),new Point(81,216),new Point(82,222),new Point(82,224),new Point(82,227),new Point(83,229),new Point(83,231),new Point(85,230),new Point(88,232),new Point(90,233),new Point(92,232),new Point(94,233),new Point(99,232),new Point(102,233),new Point(106,233),new Point(109,234),new Point(117,235),new Point(123,236),new Point(126,236),new Point(135,237),new Point(142,238),new Point(145,238),new Point(152,238),new Point(154,239),new Point(165,238),new Point(174,237),new Point(179,236),new Point(186,235),new Point(191,235),new Point(195,233),new Point(197,233),new Point(200,233),new Point(201,235),new Point(201,233),new Point(199,231),new Point(198,226),new Point(198,220),new Point(196,207),new Point(195,195),new Point(195,181),new Point(195,173),new Point(195,163),new Point(194,155),new Point(192,145),new Point(192,143),new Point(192,138),new Point(191,135),new Point(191,133),new Point(191,130),new Point(190,128),new Point(188,129),new Point(186,129),new Point(181,132),new Point(173,131),new Point(162,131),new Point(151,132),new Point(149,132),new Point(138,132),new Point(136,132),new Point(122,131),new Point(120,131),new Point(109,130),new Point(107,130),new Point(90,132),new Point(81,133),new Point(76,133))); - this.Unistrokes[3] = newUnistroke("circle", new Array(new Point(127,141),new Point(124,140),new Point(120,139),new Point(118,139),new Point(116,139),new Point(111,140),new Point(109,141),new Point(104,144),new Point(100,147),new Point(96,152),new Point(93,157),new Point(90,163),new Point(87,169),new Point(85,175),new Point(83,181),new Point(82,190),new Point(82,195),new Point(83,200),new Point(84,205),new Point(88,213),new Point(91,216),new Point(96,219),new Point(103,222),new Point(108,224),new Point(111,224),new Point(120,224),new Point(133,223),new Point(142,222),new Point(152,218),new Point(160,214),new Point(167,210),new Point(173,204),new Point(178,198),new Point(179,196),new Point(182,188),new Point(182,177),new Point(178,167),new Point(170,150),new Point(163,138),new Point(152,130),new Point(143,129),new Point(140,131),new Point(129,136),new Point(126,139))); - this.Unistrokes[4] = newUnistroke("check", new Array(new Point(91,185),new Point(93,185),new Point(95,185),new Point(97,185),new Point(100,188),new Point(102,189),new Point(104,190),new Point(106,193),new Point(108,195),new Point(110,198),new Point(112,201),new Point(114,204),new Point(115,207),new Point(117,210),new Point(118,212),new Point(120,214),new Point(121,217),new Point(122,219),new Point(123,222),new Point(124,224),new Point(126,226),new Point(127,229),new Point(129,231),new Point(130,233),new Point(129,231),new Point(129,228),new Point(129,226),new Point(129,224),new Point(129,221),new Point(129,218),new Point(129,212),new Point(129,208),new Point(130,198),new Point(132,189),new Point(134,182),new Point(137,173),new Point(143,164),new Point(147,157),new Point(151,151),new Point(155,144),new Point(161,137),new Point(165,131),new Point(171,122),new Point(174,118),new Point(176,114),new Point(177,112),new Point(177,114),new Point(175,116),new Point(173,118))); - this.Unistrokes[5] = newUnistroke("caret", new Array(new Point(79,245),new Point(79,242),new Point(79,239),new Point(80,237),new Point(80,234),new Point(81,232),new Point(82,230),new Point(84,224),new Point(86,220),new Point(86,218),new Point(87,216),new Point(88,213),new Point(90,207),new Point(91,202),new Point(92,200),new Point(93,194),new Point(94,192),new Point(96,189),new Point(97,186),new Point(100,179),new Point(102,173),new Point(105,165),new Point(107,160),new Point(109,158),new Point(112,151),new Point(115,144),new Point(117,139),new Point(119,136),new Point(119,134),new Point(120,132),new Point(121,129),new Point(122,127),new Point(124,125),new Point(126,124),new Point(129,125),new Point(131,127),new Point(132,130),new Point(136,139),new Point(141,154),new Point(145,166),new Point(151,182),new Point(156,193),new Point(157,196),new Point(161,209),new Point(162,211),new Point(167,223),new Point(169,229),new Point(170,231),new Point(173,237),new Point(176,242),new Point(177,244),new Point(179,250),new Point(181,255),new Point(182,257))); - this.Unistrokes[6] = newUnistroke("zig-zag", new Array(new Point(307,216),new Point(333,186),new Point(356,215),new Point(375,186),new Point(399,216),new Point(418,186))); - this.Unistrokes[7] = newUnistroke("arrow", new Array(new Point(68,222),new Point(70,220),new Point(73,218),new Point(75,217),new Point(77,215),new Point(80,213),new Point(82,212),new Point(84,210),new Point(87,209),new Point(89,208),new Point(92,206),new Point(95,204),new Point(101,201),new Point(106,198),new Point(112,194),new Point(118,191),new Point(124,187),new Point(127,186),new Point(132,183),new Point(138,181),new Point(141,180),new Point(146,178),new Point(154,173),new Point(159,171),new Point(161,170),new Point(166,167),new Point(168,167),new Point(171,166),new Point(174,164),new Point(177,162),new Point(180,160),new Point(182,158),new Point(183,156),new Point(181,154),new Point(178,153),new Point(171,153),new Point(164,153),new Point(160,153),new Point(150,154),new Point(147,155),new Point(141,157),new Point(137,158),new Point(135,158),new Point(137,158),new Point(140,157),new Point(143,156),new Point(151,154),new Point(160,152),new Point(170,149),new Point(179,147),new Point(185,145),new Point(192,144),new Point(196,144),new Point(198,144),new Point(200,144),new Point(201,147),new Point(199,149),new Point(194,157),new Point(191,160),new Point(186,167),new Point(180,176),new Point(177,179),new Point(171,187),new Point(169,189),new Point(165,194),new Point(164,196))); - this.Unistrokes[8] = newUnistroke("left square bracket", new Array(new Point(140,124),new Point(138,123),new Point(135,122),new Point(133,123),new Point(130,123),new Point(128,124),new Point(125,125),new Point(122,124),new Point(120,124),new Point(118,124),new Point(116,125),new Point(113,125),new Point(111,125),new Point(108,124),new Point(106,125),new Point(104,125),new Point(102,124),new Point(100,123),new Point(98,123),new Point(95,124),new Point(93,123),new Point(90,124),new Point(88,124),new Point(85,125),new Point(83,126),new Point(81,127),new Point(81,129),new Point(82,131),new Point(82,134),new Point(83,138),new Point(84,141),new Point(84,144),new Point(85,148),new Point(85,151),new Point(86,156),new Point(86,160),new Point(86,164),new Point(86,168),new Point(87,171),new Point(87,175),new Point(87,179),new Point(87,182),new Point(87,186),new Point(88,188),new Point(88,195),new Point(88,198),new Point(88,201),new Point(88,207),new Point(89,211),new Point(89,213),new Point(89,217),new Point(89,222),new Point(88,225),new Point(88,229),new Point(88,231),new Point(88,233),new Point(88,235),new Point(89,237),new Point(89,240),new Point(89,242),new Point(91,241),new Point(94,241),new Point(96,240),new Point(98,239),new Point(105,240),new Point(109,240),new Point(113,239),new Point(116,240),new Point(121,239),new Point(130,240),new Point(136,237),new Point(139,237),new Point(144,238),new Point(151,237),new Point(157,236),new Point(159,237))); - this.Unistrokes[9] = newUnistroke("right square bracket", new Array(new Point(112,138),new Point(112,136),new Point(115,136),new Point(118,137),new Point(120,136),new Point(123,136),new Point(125,136),new Point(128,136),new Point(131,136),new Point(134,135),new Point(137,135),new Point(140,134),new Point(143,133),new Point(145,132),new Point(147,132),new Point(149,132),new Point(152,132),new Point(153,134),new Point(154,137),new Point(155,141),new Point(156,144),new Point(157,152),new Point(158,161),new Point(160,170),new Point(162,182),new Point(164,192),new Point(166,200),new Point(167,209),new Point(168,214),new Point(168,216),new Point(169,221),new Point(169,223),new Point(169,228),new Point(169,231),new Point(166,233),new Point(164,234),new Point(161,235),new Point(155,236),new Point(147,235),new Point(140,233),new Point(131,233),new Point(124,233),new Point(117,235),new Point(114,238),new Point(112,238))); - this.Unistrokes[10] = newUnistroke("v", new Array(new Point(89,164),new Point(90,162),new Point(92,162),new Point(94,164),new Point(95,166),new Point(96,169),new Point(97,171),new Point(99,175),new Point(101,178),new Point(103,182),new Point(106,189),new Point(108,194),new Point(111,199),new Point(114,204),new Point(117,209),new Point(119,214),new Point(122,218),new Point(124,222),new Point(126,225),new Point(128,228),new Point(130,229),new Point(133,233),new Point(134,236),new Point(136,239),new Point(138,240),new Point(139,242),new Point(140,244),new Point(142,242),new Point(142,240),new Point(142,237),new Point(143,235),new Point(143,233),new Point(145,229),new Point(146,226),new Point(148,217),new Point(149,208),new Point(149,205),new Point(151,196),new Point(151,193),new Point(153,182),new Point(155,172),new Point(157,165),new Point(159,160),new Point(162,155),new Point(164,150),new Point(165,148),new Point(166,146))); - this.Unistrokes[11] = newUnistroke("delete", new Array(new Point(123,129),new Point(123,131),new Point(124,133),new Point(125,136),new Point(127,140),new Point(129,142),new Point(133,148),new Point(137,154),new Point(143,158),new Point(145,161),new Point(148,164),new Point(153,170),new Point(158,176),new Point(160,178),new Point(164,183),new Point(168,188),new Point(171,191),new Point(175,196),new Point(178,200),new Point(180,202),new Point(181,205),new Point(184,208),new Point(186,210),new Point(187,213),new Point(188,215),new Point(186,212),new Point(183,211),new Point(177,208),new Point(169,206),new Point(162,205),new Point(154,207),new Point(145,209),new Point(137,210),new Point(129,214),new Point(122,217),new Point(118,218),new Point(111,221),new Point(109,222),new Point(110,219),new Point(112,217),new Point(118,209),new Point(120,207),new Point(128,196),new Point(135,187),new Point(138,183),new Point(148,167),new Point(157,153),new Point(163,145),new Point(165,142),new Point(172,133),new Point(177,127),new Point(179,127),new Point(180,125))); - this.Unistrokes[12] = newUnistroke("left curly brace", new Array(new Point(150,116),new Point(147,117),new Point(145,116),new Point(142,116),new Point(139,117),new Point(136,117),new Point(133,118),new Point(129,121),new Point(126,122),new Point(123,123),new Point(120,125),new Point(118,127),new Point(115,128),new Point(113,129),new Point(112,131),new Point(113,134),new Point(115,134),new Point(117,135),new Point(120,135),new Point(123,137),new Point(126,138),new Point(129,140),new Point(135,143),new Point(137,144),new Point(139,147),new Point(141,149),new Point(140,152),new Point(139,155),new Point(134,159),new Point(131,161),new Point(124,166),new Point(121,166),new Point(117,166),new Point(114,167),new Point(112,166),new Point(114,164),new Point(116,163),new Point(118,163),new Point(120,162),new Point(122,163),new Point(125,164),new Point(127,165),new Point(129,166),new Point(130,168),new Point(129,171),new Point(127,175),new Point(125,179),new Point(123,184),new Point(121,190),new Point(120,194),new Point(119,199),new Point(120,202),new Point(123,207),new Point(127,211),new Point(133,215),new Point(142,219),new Point(148,220),new Point(151,221))); - this.Unistrokes[13] = newUnistroke("right curly brace", new Array(new Point(117,132),new Point(115,132),new Point(115,129),new Point(117,129),new Point(119,128),new Point(122,127),new Point(125,127),new Point(127,127),new Point(130,127),new Point(133,129),new Point(136,129),new Point(138,130),new Point(140,131),new Point(143,134),new Point(144,136),new Point(145,139),new Point(145,142),new Point(145,145),new Point(145,147),new Point(145,149),new Point(144,152),new Point(142,157),new Point(141,160),new Point(139,163),new Point(137,166),new Point(135,167),new Point(133,169),new Point(131,172),new Point(128,173),new Point(126,176),new Point(125,178),new Point(125,180),new Point(125,182),new Point(126,184),new Point(128,187),new Point(130,187),new Point(132,188),new Point(135,189),new Point(140,189),new Point(145,189),new Point(150,187),new Point(155,186),new Point(157,185),new Point(159,184),new Point(156,185),new Point(154,185),new Point(149,185),new Point(145,187),new Point(141,188),new Point(136,191),new Point(134,191),new Point(131,192),new Point(129,193),new Point(129,195),new Point(129,197),new Point(131,200),new Point(133,202),new Point(136,206),new Point(139,211),new Point(142,215),new Point(145,220),new Point(147,225),new Point(148,231),new Point(147,239),new Point(144,244),new Point(139,248),new Point(134,250),new Point(126,253),new Point(119,253),new Point(115,253))); - this.Unistrokes[14] = newUnistroke("star", new Array(new Point(75,250),new Point(75,247),new Point(77,244),new Point(78,242),new Point(79,239),new Point(80,237),new Point(82,234),new Point(82,232),new Point(84,229),new Point(85,225),new Point(87,222),new Point(88,219),new Point(89,216),new Point(91,212),new Point(92,208),new Point(94,204),new Point(95,201),new Point(96,196),new Point(97,194),new Point(98,191),new Point(100,185),new Point(102,178),new Point(104,173),new Point(104,171),new Point(105,164),new Point(106,158),new Point(107,156),new Point(107,152),new Point(108,145),new Point(109,141),new Point(110,139),new Point(112,133),new Point(113,131),new Point(116,127),new Point(117,125),new Point(119,122),new Point(121,121),new Point(123,120),new Point(125,122),new Point(125,125),new Point(127,130),new Point(128,133),new Point(131,143),new Point(136,153),new Point(140,163),new Point(144,172),new Point(145,175),new Point(151,189),new Point(156,201),new Point(161,213),new Point(166,225),new Point(169,233),new Point(171,236),new Point(174,243),new Point(177,247),new Point(178,249),new Point(179,251),new Point(180,253),new Point(180,255),new Point(179,257),new Point(177,257),new Point(174,255),new Point(169,250),new Point(164,247),new Point(160,245),new Point(149,238),new Point(138,230),new Point(127,221),new Point(124,220),new Point(112,212),new Point(110,210),new Point(96,201),new Point(84,195),new Point(74,190),new Point(64,182),new Point(55,175),new Point(51,172),new Point(49,170),new Point(51,169),new Point(56,169),new Point(66,169),new Point(78,168),new Point(92,166),new Point(107,164),new Point(123,161),new Point(140,162),new Point(156,162),new Point(171,160),new Point(173,160),new Point(186,160),new Point(195,160),new Point(198,161),new Point(203,163),new Point(208,163),new Point(206,164),new Point(200,167),new Point(187,172),new Point(174,179),new Point(172,181),new Point(153,192),new Point(137,201),new Point(123,211),new Point(112,220),new Point(99,229),new Point(90,237),new Point(80,244),new Point(73,250),new Point(69,254),new Point(69,252))); - this.Unistrokes[15] = newUnistroke("pigtail", new Array(new Point(81,219),new Point(84,218),new Point(86,220),new Point(88,220),new Point(90,220),new Point(92,219),new Point(95,220),new Point(97,219),new Point(99,220),new Point(102,218),new Point(105,217),new Point(107,216),new Point(110,216),new Point(113,214),new Point(116,212),new Point(118,210),new Point(121,208),new Point(124,205),new Point(126,202),new Point(129,199),new Point(132,196),new Point(136,191),new Point(139,187),new Point(142,182),new Point(144,179),new Point(146,174),new Point(148,170),new Point(149,168),new Point(151,162),new Point(152,160),new Point(152,157),new Point(152,155),new Point(152,151),new Point(152,149),new Point(152,146),new Point(149,142),new Point(148,139),new Point(145,137),new Point(141,135),new Point(139,135),new Point(134,136),new Point(130,140),new Point(128,142),new Point(126,145),new Point(122,150),new Point(119,158),new Point(117,163),new Point(115,170),new Point(114,175),new Point(117,184),new Point(120,190),new Point(125,199),new Point(129,203),new Point(133,208),new Point(138,213),new Point(145,215),new Point(155,218),new Point(164,219),new Point(166,219),new Point(177,219),new Point(182,218),new Point(192,216),new Point(196,213),new Point(199,212),new Point(201,211))); - -} */ - // - // The $1 Gesture Recognizer API begins here -- 3 methods: Recognize(), AddGesture(), and DeleteUserGestures() - // - /* -void Recognize(Point *points, int pointsLen, bool useProtractor) { - var candidate = new Unistroke("", points); - - int u = -1; - float b = FLT_MAX; - for (var i = 0; i < this.Unistrokes.length; i++) { // for each unistroke template - float d; - if (useProtractor) - d = OptimalCosineDistance(this.Unistrokes[i].Vector, candidate.Vector); // Protractor - else - d = DistanceAtBestAngle(candidate.Points, this.Unistrokes[i], -AngleRange, +AngleRange, AnglePrecision); // Golden Section Search (original $1) - if (d < b) { - b = d; // best (least) distance - u = i; // unistroke index - } +/*void dumpPts(const char *n, Point *points, int pointCount) { + jsiConsolePrintf("%s\n",n); + for (int i=0;i= I) - { + if ((D + d) >= I) { float qx = points[i-1].X + ((I - D) / d) * (points[i].X - points[i-1].X); float qy = points[i-1].Y + ((I - D) / d) * (points[i].Y - points[i-1].Y); Point q = {qx, qy}; dst[dstLen++] = q; // append new point 'q' - points[i]=q; // insert 'q' at position i in points s.t. 'q' will be the next i + points[i-1]=q;i--; // insert 'q' at position i in points s.t. 'q' will be the next i D = 0.0; - } - else D += d; + } else D += d; } if (dstLen == n - 1) {// sometimes we fall a rounding-error short of adding the last point, so add it if so Point q = {points[pointsLen- 1].X, points[pointsLen - 1].Y }; @@ -235,13 +185,13 @@ void Resample(Point *dst, int n, Point *points, int pointsLen) // points is dama float IndicativeAngle(Point *points, int pointsLen) { Point c = Centroid(points, pointsLen); - return atan2(c.Y - points[0].Y, c.X - points[0].X); + return (float)atan2(c.Y - points[0].Y, c.X - points[0].X); } void RotateBy(Point *dst, Point *points, int pointsLen, float radians) // rotates points around centroid { Point c = Centroid(points, pointsLen); - float fcos = cos(radians); - float fsin = sin(radians); + float fcos = (float)cos(radians); + float fsin = (float)sin(radians); for (int i = 0; i < pointsLen; i++) { dst[i].X = (points[i].X - c.X) * fcos - (points[i].Y - c.Y) * fsin + c.X; dst[i].Y = (points[i].X - c.X) * fsin + (points[i].Y - c.Y) * fcos + c.Y; @@ -283,14 +233,14 @@ float OptimalCosineDistance(float *v1, float *v2, int vLen) // for Protractor a += v1[i] * v2[i] + v1[i+1] * v2[i+1]; b += v1[i] * v2[i+1] - v1[i+1] * v2[i]; } - float angle = atan(b / a); - return acos(a * cos(angle) + b * sin(angle)); + float angle = (float)atan(b / a); + return (float)acos(a * cos(angle) + b * sin(angle)); } float DistanceAtBestAngle(Point *points, int pointsLen, Point *T, float a, float b, float threshold) { - float x1 = Phi * a + (1.0 - Phi) * b; + float x1 = Phi * a + (1.0f - Phi) * b; float f1 = DistanceAtAngle(points, pointsLen, T, x1); - float x2 = (1.0 - Phi) * a + Phi * b; + float x2 = (1.0f - Phi) * a + Phi * b; float f2 = DistanceAtAngle(points, pointsLen, T, x2); while (fabs(b - a) > threshold) { @@ -298,13 +248,13 @@ float DistanceAtBestAngle(Point *points, int pointsLen, Point *T, float a, float b = x2; x2 = x1; f2 = f1; - x1 = Phi * a + (1.0 - Phi) * b; + x1 = Phi * a + (1.0f - Phi) * b; f1 = DistanceAtAngle(points, pointsLen, T, x1); } else { a = x1; x1 = x2; f1 = f2; - x2 = (1.0 - Phi) * a + Phi * b; + x2 = (1.0f - Phi) * a + Phi * b; f2 = DistanceAtAngle(points, pointsLen, T, x2); } } @@ -349,7 +299,7 @@ float PathDistance(Point *pts1, Point *pts2, int pointsLen) } float PathLength(Point *points, int pointsLen) { - float d = 0.0; + float d = 0.0f; for (int i = 1; i < pointsLen; i++) d += Distance(points[i - 1], points[i]); return d; @@ -358,7 +308,7 @@ float Distance(Point p1, Point p2) { float dx = p2.X - p1.X; float dy = p2.Y - p1.Y; - return sqrtf(dx * dx + dy * dy); + return sqrtf((dx*dx) + (dy*dy)); } float Deg2Rad(float d) { return d * PI / 180.0f; @@ -369,34 +319,39 @@ float Deg2Rad(float d) { Point touchHistory[NUMPOINTS]; uint8_t touchHistoryLen; // how many points in touchHistory Point touchSum; // sum of points so far +bool touchLastPressed; // Is a finger on the screen? If not, next press we should start from scratch uint8_t touchCnt; // how many touches are in touchSum uint8_t touchDivisor; // how many touch points do we sum to get the next point? - +int touchDistance; // distance dragged /// initialise stroke detection void unistroke_init() { touchHistoryLen = 0; touchDivisor = 1; + touchLastPressed = false; } -/// Called when a touch event occurs -void unistroke_touch(int x, int y, int pts) { +#ifdef LCD_WIDTH +/// Called when a touch event occurs, returns 'true' if an event should be created (by calling unistroke_getEventVar) +bool unistroke_touch(int x, int y, int dx, int dy, int pts) { if (!pts) { // touch released - - /*jsiConsolePrintf("["); - for (int i=0;i LCD_WIDTH/2; // fire off an event IF the distance is large amount + } + // now finger is definitely pressed + if (!touchLastPressed) { + // reset everything if this is the first time touchHistoryLen = 0; touchDivisor = 1; touchSum.X = 0; touchSum.Y = 0; touchCnt = 0; - return; + touchDistance = 0; + touchLastPressed = true; } + // append to touch distance + touchDistance += int_sqrt32((dx*dx)+(dy*dy)); // store the sum of touch points touchSum.X += x; @@ -423,6 +378,90 @@ void unistroke_touch(int x, int y, int pts) { touchSum.Y = 0; touchCnt = 0; } + return false; +} +#endif + +/// Convert an array containing XY values to a unistroke var +JsVar *unistroke_convert(JsVar *xy) { + uint8_t points8[NUMPOINTS*2]; + unsigned int bytes = jsvIterateCallbackToBytes(xy, points8, sizeof(points8)); + int pointCount = bytes/2; + Unistroke uni = newUnistroke8(points8, pointCount); + return jsvNewStringOfLength(sizeof(uni), (char *)&uni); +} + +/// recognise a single stroke +float unistroke_recognise_one(JsVar *strokeVar, Unistroke *candidate) { + float d = FLT_MAX; + JSV_GET_AS_CHAR_ARRAY(strokePtr, strokeLen, strokeVar) + if (strokePtr && strokeLen==sizeof(Unistroke)) { + Unistroke *uni = (Unistroke*)strokePtr; + /*if (useProtractor) + d = OptimalCosineDistance(this.Unistrokes[i].Vector, candidate.Vector); // Protractor + else*/ + d = DistanceAtBestAngle(candidate->points, NUMPOINTS, uni->points, -AngleRange, +AngleRange, AnglePrecision); // Golden Section Search (original $1) + } + jsvUnLock(strokeVar); + return d; +} + +/// Given an object containing values created with unistroke_convert, compare against a unistroke +JsVar *unistroke_recognise(JsVar *strokes, Unistroke *candidate) { + JsVar *u = 0; + float b = FLT_MAX; + JsvObjectIterator it; + jsvObjectIteratorNew(&it, strokes); + while (jsvObjectIteratorHasValue(&it)) { // for each unistroke template + JsVar *strokeVar = jsvObjectIteratorGetValue(&it); + float d = unistroke_recognise_one(strokeVar, candidate); + if (d < b) { + b = d; // best (least) distance + jsvUnLock(u); + u = jsvObjectIteratorGetKey(&it); // unistroke index + } + jsvUnLock(strokeVar); + jsvObjectIteratorNext(&it); + } + // CERTAINTY = useProtractor ? (1.0 - b) : (1.0 - b / (0.5 * Diagonal)) + return u ? jsvAsStringAndUnLock(u) : 0; +} + +/// Given an object containing values created with unistroke_convert, compare against an array containing XY values +JsVar *unistroke_recognise_xy(JsVar *strokes, JsVar *xy) { + uint8_t points8[NUMPOINTS*2]; + unsigned int bytes = jsvIterateCallbackToBytes(xy, points8, sizeof(points8)); + int pointCount = bytes/2; + Unistroke uni = newUnistroke8(points8, pointCount); + return unistroke_recognise(strokes, &uni); +} + +JsVar *unistroke_getEventVar() { + if (!touchHistoryLen) return 0; + JsVar *o = jsvNewObject(); + if (!o) return 0; + JsVar *a = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, touchHistoryLen*2); + if (a) { + JsVar *s = jsvGetArrayBufferBackingString(a,NULL); + size_t n=0; + for (int i=0;i>16); } + +/// Clip X between -128 and 127 +char clipi8(int x) { + if (x<-128) return -128; + if (x>127) return 127; + return (char)x; +} + +/// Convert the given value to a signed integer assuming it has the given number of bits +int twosComplement(int val, unsigned char bits) { + if (val & ((unsigned int)1 << (bits - 1))) + val -= (unsigned int)1 << bits; + return val; +} + +// quick integer square root +// https://stackoverflow.com/questions/31117497/fastest-integer-square-root-in-the-least-amount-of-instructions +unsigned short int int_sqrt32(unsigned int x) { + unsigned short int res=0; + unsigned short int add= 0x8000; + int i; + for(i=0;i<16;i++) { + unsigned short int temp=res | add; + unsigned int g2=temp*temp; + if (x>=g2) + res=temp; + add>>=1; + } + return res; +} diff --git a/src/jsutils.h b/src/jsutils.h index 9452d9649..1ac700937 100755 --- a/src/jsutils.h +++ b/src/jsutils.h @@ -144,8 +144,12 @@ typedef uint32_t JsfWord; //typedef unsigned char bool; #endif +#ifndef DBL_MIN #define DBL_MIN 2.2250738585072014e-308 +#endif +#ifndef DBL_MAX #define DBL_MAX 1.7976931348623157e+308 +#endif /* Number of JS Variables allowed and JS Variable reference format. @@ -575,6 +579,15 @@ int rand(); /// a rand() replacement that doesn't need malloc (!!!) void srand(unsigned int seed); +/// Clip X between -128 and 127 +char clipi8(int x); + +/// Convert the given value to a signed integer assuming it has the given number of bits +int twosComplement(int val, unsigned char bits); + +/// quick integer square root +unsigned short int int_sqrt32(unsigned int x); + /** get the amount of free stack we have, in bytes */ size_t jsuGetFreeStack(); diff --git a/tests/test_language_FAIL.js b/tests/test_language_FAIL.js deleted file mode 100644 index 41ec69e17..000000000 --- a/tests/test_language_FAIL.js +++ /dev/null @@ -1,827 +0,0 @@ -// From https://gist.github.com/randunel/8983226 -// It's very unlikely this will ever pass completely, but it's good to have -// IMO it should be split into separate tests - -var tests = 0; -var testsPass = 0; - -// From V8 and SpiderMonkey -function shouldBe(item, value) { - tests++; - if (eval(item) !== eval(value)) { - console.log('ERROR:', item, 'should be', value, 'but found', '' + eval(item)); - } else testsPass++; -} - -function shouldBeUndefined(item) { - tests++; - if (eval(item) !== undefined) { - console.log('ERROR:', item, 'should be undefined but found', '' + eval(item)); - } else testsPass++; -} - -function shouldBeTrue(item) { - tests++; - if (eval(item) !== true) { - console.log('ERROR:', item, 'should be true but found', '' + eval(item)); - } else testsPass++; -} - -function shouldBeFalse(item) { - tests++; - if (eval(item) !== false) { - console.log('ERROR:', item, 'should be false but found', '' + eval(item)); - } else testsPass++; -} - -function shouldBeOfType(msg, val, type) { - tests++; - if (typeof(val) !== type) - console.log('ERROR:', msg + ": value has type " + typeof(val) + " , not:" + type); - else testsPass++; -} - -function shouldBeVal(msg, val, expected) { - tests++; - if (val !== expected) - console.log('ERROR:', msg + ": value is " + val + " , not:" + expected); - else testsPass++; -} - -shouldBe("Array().length", "0"); -shouldBe("(new Array()).length", "0"); -shouldBe("(new Array(3)).length", "3"); -shouldBe("(new Array(11, 22)).length", "2"); -shouldBe("(new Array(11, 22))[0]", "11"); -shouldBe("Array(11, 22)[1]", "22"); -shouldBeUndefined("(new Array(11, 22))[3]"); -shouldBe("String(new Array(11, 22))", "'11,22'"); -shouldBe("var a = []; a[0] = 33; a[0]", "33"); -shouldBe("var a = []; a[0] = 33; a.length", "1"); - -shouldBe("Array().toString()", "''"); -shouldBe("Array(3).toString()", "',,'"); -shouldBe("Array(11, 22).toString()", "'11,22'"); - -shouldBe("[1,2,3,4].slice(1, 3).toString()", "'2,3'"); -shouldBe("[1,2,3,4].slice(-3, -1).toString()", "'2,3'"); -shouldBe("[1,2].slice(-9, 0).length", "0"); -shouldBe("[1,2].slice(1).toString()", "'2'"); -shouldBe("[1,2].slice().toString()", "'1,2'"); - -shouldBe("(new Array('a')).length", "1"); -shouldBe("(new Array('a'))[0]", "'a'"); -shouldBeUndefined("(new Array('a'))[1]"); - -shouldBe("Array('a').length", "1"); -shouldBe("Array('a')[0]", "'a'"); - -shouldBe("String(Array())", "''"); -shouldBe("String(Array('a','b'))", "'a,b'"); - -shouldBe("[].length", "0"); -shouldBe("['a'].length", "1"); -shouldBe("['a'][0]", "'a'"); -shouldBe("['a',undefined,'c'][1]", "undefined"); -shouldBe("1 in ['a',undefined,'c']", "true"); - -var arrayWithDeletion = ['a','b','c']; -delete arrayWithDeletion[1]; -shouldBe("1 in arrayWithDeletion", "false"); - -function forInSum(_a) { - var s = ''; - for (var i in _a) - s += _a[i]; - return s; -} - -shouldBe("forInSum([])", "''"); -shouldBe("forInSum(Array())", "''"); -shouldBe("forInSum(Array('a'))", "'a'"); - -var a0 = []; -shouldBe("forInSum(a0)", "''"); - -var a1 = [ 'a' ]; -shouldBe("forInSum(a1)", "'a'"); - -shouldBe("String(['a', 'b', 'c'].splice(1, 2, 'x', 'y'))", "'b,c'"); - -var arr = new Array('a','b','c'); -var propnames = []; -for (var i in arr) - propnames.push(i); -shouldBe("propnames.length","3"); -shouldBe("propnames[0]","'0'"); -shouldBe("propnames[1]","'1'"); -shouldBe("propnames[2]","'2'"); - - - -var h = "a\xefc"; -var u = "a\u1234c"; -var z = "\x00"; - -shouldBe("h.charCodeAt(1)", "239"); -shouldBe("u.charCodeAt(1)", "4660"); - -shouldBeTrue("isNaN(NaN)"); -shouldBeTrue("isNaN('NaN')"); -shouldBeFalse("isNaN('1')"); - -// all should return NaN because 1st char is non-number -shouldBe('isNaN(parseInt("Hello", 8))', "true"); -shouldBe('isNaN(parseInt("FFF", 10))', "true"); -shouldBe('isNaN(parseInt(".5", 10))', "true"); - -shouldBeTrue("isNaN(parseInt())"); -shouldBeTrue("isNaN(parseInt(''))"); -shouldBeTrue("isNaN(parseInt(' '))"); -shouldBeTrue("isNaN(parseInt('a'))"); -shouldBe("parseInt(1)", "1"); -shouldBe("parseInt(1234567890123456)", "1234567890123456"); -shouldBe("parseInt(1.2)", "1"); -shouldBe("parseInt(' 2.3')", "2"); -shouldBe("parseInt('0x10')", "16"); -shouldBe("parseInt('11', 0)", "11"); -shouldBe("parseInt('F', 16)", "15"); - -shouldBeTrue("isNaN(parseInt('10', 40))"); -shouldBe("parseInt('3x')", "3"); -shouldBe("parseInt('3 x')", "3"); -shouldBeTrue("isNaN(parseInt('Infinity'))"); - -// all should return 15 -shouldBe('parseInt("15")', "15"); -shouldBe('parseInt("015")', "15"); // ES5 prohibits parseInt from handling octal, see annex E. -shouldBe('parseInt("0xf")', "15"); -shouldBe('parseInt("15", 0)', "15"); -shouldBe('parseInt("15", 10)', "15"); -shouldBe('parseInt("F", 16)', "15"); -shouldBe('parseInt("17", 8)', "15"); -shouldBe('parseInt("15.99", 10)', "15"); -shouldBe('parseInt("FXX123", 16)', "15"); -shouldBe('parseInt("1111", 2)', "15"); -shouldBe('parseInt("15*3", 10)', "15"); - -// this should be 0 -shouldBe('parseInt("0x7", 10)', "0"); -shouldBe('parseInt("1x7", 10)', "1"); - -shouldBeTrue("isNaN(parseFloat())"); -shouldBeTrue("isNaN(parseFloat(''))"); -shouldBeTrue("isNaN(parseFloat(' '))"); -shouldBeTrue("isNaN(parseFloat('a'))"); -shouldBe("parseFloat(1)", "1"); -shouldBe("parseFloat(' 2.3')", "2.3"); -shouldBe("parseFloat('3.1 x', 3)", "3.1"); -shouldBe("parseFloat('3.1x', 3)", "3.1"); -shouldBeFalse("delete NaN"); -shouldBeFalse("delete Infinity"); -shouldBeFalse("delete undefined"); - - -shouldBe("isNaN(Number('a'))", "true"); -shouldBe("isNaN(new Number('a'))", "true"); - -shouldBe("isNaN(Number.NaN)", "true"); -shouldBe("Number.NEGATIVE_INFINITY", "-Infinity"); -shouldBe("Number.POSITIVE_INFINITY", "Infinity"); - -shouldBe("(1).toString()", "'1'"); -shouldBe("typeof (1).toString()", "'string'"); -shouldBe("(10).toString(16)", "'a'"); -shouldBe("(8.5).toString(16)", "'8.8'"); -shouldBe("(-8.5).toString(16)", "'-8.8'"); -shouldBe("Number.POSITIVE_INFINITY.toString(16)", "'Infinity'"); -shouldBe("Number.NEGATIVE_INFINITY.toString(16)", "'-Infinity'"); -shouldBe("Number.MAX_VALUE.toString(2).length", "23"); -shouldBe("typeof 1", "'number'"); - - - - -shouldBe("typeof (new Object())", "'object'"); -shouldBe("var o = new Object(); o.x = 11; o.x;", "11"); - -shouldBe("typeof (new Object())", "'object'"); - -shouldBe("String(new Object())", "'[object Object]'"); -shouldBe("(new Object()).toString()", "'[object Object]'"); - - - - -function Square(x) { - this.x = x; -} - -new Square(0); // create prototype - -function Square_area() { return this.x * this.x; } -Square.prototype.area = Square_area; -var s = new Square(3); -shouldBe("s.area()", "9"); - -function Item(name) { - this.name = name; -} - -function Book(name, author){ - this.base = Item; // set Item constructor as method of Book object - this.base(name); // set the value of name property - this.author = author; -} -Book.prototype = new Item(); -var b = new Book("a book", "Fred"); // create object instance -//edebug(e"b.name")); -shouldBe("b.name", "'a book'"); -shouldBe("b.author", "'Fred'"); // outpus "Fred" - -shouldBe("delete Array.prototype", "false"); - -shouldBe("var i = 1; i", "1"); -shouldBe("j = k = 2", "2"); -shouldBeUndefined("var i; i"); - -// compound assignments -shouldBe("var i = 1; i <<= 2", "4"); -shouldBe("var i = 8; i >>= 1", "4"); -shouldBe("var i = 1; i >>= 2", "0"); -shouldBe("var i = -8; i >>= 24", "-1"); -shouldBe("var i = 8; i >>>= 2", "2"); -shouldBe("var i = -8; i >>>= 24", "255"); - -var i = 1; - -function foo() { - i = 2; - return; - i = 3; -} - -shouldBe("foo(), i", "2"); - -// value completions take precedence -var val = eval("11; { }"); -shouldBe("val", "11"); -val = eval("12; ;"); -shouldBe("val", "12"); -val = eval("13; if(false);"); -shouldBe("val", "13"); -val = eval("14; function f() {}"); -shouldBe("val", "14"); -val = eval("15; var v = 0"); -shouldBe("val", "15"); - -shouldBe("true ? 1 : 2", "1"); -shouldBe("false ? 1 : 2", "2"); -shouldBe("'abc' ? 1 : 2", "1"); -shouldBe("null ? 1 : 2", "2"); -shouldBe("undefined ? 1 : 2", "2"); -var asd = 1; -if ( undefined ) - asd = 2; -shouldBe("/*var asd=1;if (undefined) asd = 2;*/ asd", "1"); - - -shouldBe("a = 1; delete a;", "true"); -shouldBe("delete nonexistant;", "true"); -shouldBe("delete NaN", "false"); - - -f = "global"; - -function test() { - shouldBeOfType("Function declaration takes effect at entry", f, "function"); - - for (var i = 0; i < 3; ++i) { - if (i == 0) - shouldBeOfType("Decl not yet overwritten", f, 'function'); - else - shouldBeOfType("Decl already overwritten", f, 'number'); - - f = 3; - shouldBeVal("After assign ("+i+")", f, 3); - - function f() {}; - shouldBeVal("function decls have no execution content", f, 3); - - f = 5; - - shouldBeVal("After assign #2 ("+i+")", f, 5); - } -} - -test(); - - -var count = 0; -do { - count++; -} while (count < 10); -shouldBe("count", "10"); - -count = 0; -for (var i = 0; i < 10; i++) { - if (i == 5) - break; - count++; -} -shouldBe("count", "5"); - -count = 0; -for (i = 0; i < 10; i++) { - count++; -} -shouldBe("count", "10"); - -obj = new Object(); -obj.a = 11; -obj.b = 22; - -properties = ""; -for ( prop in obj ) { - properties += (prop + "=" + obj[prop] + ";"); -} - -shouldBe("properties", "'a=11;b=22;'"); - -obj.y = 33; -obj.x = 44; -properties = ""; -for ( prop in obj ) - properties += prop; - -arr = new Array; -arr[0] = 100; -arr[1] = 101; -list = ""; -for ( var j in arr ) { - list += "[" + j + "]=" + arr[j] + ";"; -} -shouldBe("list","'[0]=100;[1]=101;'"); - -list = ""; -for (var a = [1,2,3], length = a.length, i = 0; i < length; i++) { - list += a[i]; -} -shouldBe("list", "'123'"); - - -var negativeZero = Math.atan2(-1, Infinity); // ### any nicer way? - -function isNegativeZero(n) { - return n == 0 && 1 / n < 0; -} - -// self tests -shouldBeTrue("isNegativeZero(negativeZero)"); -shouldBeFalse("isNegativeZero(0)"); - -// Constants -shouldBe("String()+Math.E", "'2.718281828459045'"); -shouldBe("String()+Math.LN2", "'0.6931471805599453'"); -shouldBe("String()+Math.LN10", "'2.302585092994046'"); -shouldBe("String()+Math.LOG2E", "'1.4426950408889634'"); -shouldBe("String()+Math.LOG10E", "'0.4342944819032518'"); -shouldBe("String()+Math.PI", "'3.141592653589793'"); -shouldBe("String()+Math.SQRT1_2", "'0.7071067811865476'"); -shouldBe("String()+Math.SQRT2", "'1.4142135623730951'"); - -shouldBe("String()+Number.NaN", "'NaN'"); -shouldBe("String()+Number.NEGATIVE_INFINITY", "'-Infinity'"); -shouldBe("String()+Number.POSITIVE_INFINITY", "'Infinity'"); - - -shouldBe("Math.abs(-5)", "5"); -shouldBe("Math.acos(0)", "Math.PI/2"); -shouldBe("Math.acos(1)", "0"); -shouldBe("Math.ceil(1.1)", "2"); -shouldBe("String()+Math.sqrt(2)", "String()+Math.SQRT2"); -shouldBe("Math.ceil(1.6)", "2"); -shouldBe("Math.round(0)", "0"); -shouldBeFalse("isNegativeZero(Math.round(0))"); -shouldBeTrue("isNegativeZero(Math.round(negativeZero))"); -shouldBe("Math.round(0.2)", "0"); -shouldBeTrue("isNegativeZero(Math.round(-0.2))"); -shouldBeTrue("isNegativeZero(Math.round(-0.5))"); -shouldBe("Math.round(1.1)", "1"); -shouldBe("Math.round(1.6)", "2"); -shouldBe("Math.round(-3.5)", "-3"); -shouldBe("Math.round(-3.6)", "-4"); -shouldBeTrue("isNaN(Math.round())"); -shouldBeTrue("isNaN(Math.round(NaN))"); -shouldBe("Math.round(-Infinity)", "-Infinity"); -shouldBe("Math.round(Infinity)", "Infinity"); -shouldBe("Math.round(99999999999999999999.99)", "100000000000000000000"); -shouldBe("Math.round(-99999999999999999999.99)", "-100000000000000000000"); - -shouldBe("Math.log(Math.E*Math.E)", "2"); -shouldBeTrue("isNaN(Math.log(NaN))"); -shouldBeTrue("isNaN(Math.log(-1))"); -shouldBe("Math.log(1)", "0"); - - -var obj = {}; -obj.a = 1; -obj.b = 2; -list=""; -for ( var i in obj ) { list += i + ','; } -shouldBe("list","'a,b,'"); - -list=""; -for ( var i in Math ) { list += i + ','; } -shouldBe("list","''"); - -Math.myprop=true; // adding a custom property to the math object (why not?) -list=""; -for ( var i in Math ) { list += i + ','; } -shouldBe("list","'myprop,'"); - - -shouldBeTrue("!undefined"); -shouldBeTrue("!null"); -shouldBeTrue("!!true"); -shouldBeTrue("!false"); -shouldBeTrue("!!1"); -shouldBeTrue("!0"); -shouldBeTrue("!!'a'"); -shouldBeTrue("!''"); -// unary plus -shouldBe("+9", "9"); -shouldBe("var i = 10; +i", "10"); - -// negation -shouldBe("-11", "-11"); -shouldBe("var i = 12; -i", "-12"); - -// increment -shouldBe("var i = 0; ++i;", "1"); -shouldBe("var i = 0; ++i; i", "1"); -shouldBe("var i = 0; i++;", "0"); -shouldBe("var i = 0; i++; i", "1"); -shouldBe("var i = true; i++", "1"); -shouldBe("var i = true; i++; i", "2"); - -// decrement -shouldBe("var i = 0; --i;", "-1"); -shouldBe("var i = 0; --i; i", "-1"); -shouldBe("var i = 0; i--;", "0"); -shouldBe("var i = 0; i--; i", "-1"); -shouldBe("var i = true; i--", "1"); -shouldBe("var i = true; i--; i", "0"); - - -var one = 1; -var two = 2; -var twentyFour = 24; - -shouldBe("1 << two", "4"); -shouldBe("8 >> one", "4"); -shouldBe("1 >> two", "0"); -shouldBe("-8 >> twentyFour", "-1"); -shouldBe("8 >>> two", "2"); -shouldBe("-8 >>> twentyFour", "255"); -shouldBe("(-2200000000 >> one) << one", "2094967296"); -shouldBe("Infinity >> one", "0"); -shouldBe("Infinity << one", "0"); -shouldBe("Infinity >>> one", "0"); -shouldBe("NaN >> one", "0"); -shouldBe("NaN << one", "0"); -shouldBe("NaN >>> one", "0"); -shouldBe("888.1 >> one", "444"); -shouldBe("888.1 << one", "1776"); -shouldBe("888.1 >>> one", "444"); -shouldBe("888.9 >> one", "444"); -shouldBe("888.9 << one", "1776"); -shouldBe("888.9 >>> one", "444"); -shouldBe("Math.pow(2, 32) >> one", "0"); -shouldBe("Math.pow(2, 32) << one", "0"); -shouldBe("Math.pow(2, 32) >>> one", "0"); - -// addition -shouldBe("1+2", "3"); -shouldBe("'a'+'b'", "'ab'"); -shouldBe("'a'+2", "'a2'"); -shouldBe("'2'+'-1'", "'2-1'"); -shouldBe("true+'a'", "'truea'"); -shouldBe("'a' + null", "'anull'"); -shouldBe("true+1", "2"); -shouldBe("false+null", "0"); - -// substraction -shouldBe("1-3", "-2"); -shouldBe("isNaN('a'-3)", "true"); -shouldBe("'3'-'-1'", "4"); -shouldBe("'4'-2", "2"); -shouldBe("true-false", "1"); -shouldBe("false-1", "-1"); -shouldBe("null-true", "-1"); - -// multiplication -shouldBe("2 * 3", "6"); -shouldBe("true * 3", "3"); -shouldBe("2 * '3'", "6"); - -// division -shouldBe("6 / 4", "1.5"); -//shouldBe("true / false", "Inf"); -shouldBe("'6' / '2'", "3"); -shouldBeTrue("isNaN('x' / 1)"); -shouldBeTrue("isNaN(1 / NaN)"); -shouldBeTrue("isNaN(Infinity / Infinity)"); -shouldBe("Infinity / 0", "Infinity"); -shouldBe("-Infinity / 0", "-Infinity"); -shouldBe("Infinity / 1", "Infinity"); -shouldBe("-Infinity / 1", "-Infinity"); -shouldBeTrue("1 / Infinity == +0"); -shouldBeTrue("1 / -Infinity == -0"); // how to check ? -shouldBeTrue("isNaN(0/0)"); -shouldBeTrue("0 / 1 === 0"); -shouldBeTrue("0 / -1 === -0"); // how to check ? -shouldBe("1 / 0", "Infinity"); -shouldBe("-1 / 0", "-Infinity"); - -// modulo -shouldBe("6 % 4", "2"); -shouldBe("'-6' % 4", "-2"); - -shouldBe("2==2", "true"); -shouldBe("1==2", "false"); - - - - -shouldBe("1<2", "true"); -shouldBe("1<=2", "true"); -shouldBe("2<1", "false"); -shouldBe("2<=1", "false"); - -shouldBe("2>1", "true"); -shouldBe("2>=1", "true"); -shouldBe("1>=2", "false"); -shouldBe("1>2", "false"); - - -shouldBeTrue("'abc' == 'abc'"); -shouldBeTrue("'abc' != 'xyz'"); -shouldBeTrue("true == true"); -shouldBeTrue("false == false"); -shouldBeTrue("true != false"); -shouldBeTrue("'a' != null"); -shouldBeTrue("'a' != undefined"); -shouldBeTrue("null == null"); -shouldBeTrue("null == undefined"); -shouldBeTrue("undefined == undefined"); -shouldBeTrue("NaN != NaN"); -shouldBeTrue("true != undefined"); -shouldBeTrue("true != null"); -shouldBeTrue("false != undefined"); -shouldBeTrue("false != null"); -shouldBeTrue("'0' == 0"); -shouldBeTrue("1 == '1'"); -shouldBeTrue("NaN != NaN"); -shouldBeTrue("NaN != 0"); -shouldBeTrue("NaN != undefined"); -shouldBeTrue("true == 1"); -shouldBeTrue("true != 2"); -shouldBeTrue("1 == true"); -shouldBeTrue("false == 0"); -shouldBeTrue("0 == false"); - - -shouldBe("'abc' < 'abx'", "true"); -shouldBe("'abc' < 'abcd'", "true"); -shouldBe("'abc' < 'abc'", "false"); -shouldBe("'abcd' < 'abcd'", "false"); -shouldBe("'abx' < 'abc'", "false"); - - -shouldBe("'abc' <= 'abc'", "true"); -shouldBe("'abc' <= 'abx'", "true"); -shouldBe("'abx' <= 'abc'", "false"); -shouldBe("'abcd' <= 'abc'", "false"); -shouldBe("'abc' <= 'abcd'", "true"); - - -shouldBe("'abc' > 'abx'", "false"); -shouldBe("'abc' > 'abc'", "false"); -shouldBe("'abcd' > 'abc'", "true"); -shouldBe("'abx' > 'abc'", "true"); -shouldBe("'abc' > 'abcd'", "false"); - - -shouldBe("'abc' >= 'abc'", "true"); -shouldBe("'abcd' >= 'abc'", "true"); -shouldBe("'abx' >= 'abc'", "true"); -shouldBe("'abc' >= 'abx'", "false"); -shouldBe("'abc' >= 'abx'", "false"); -shouldBe("'abc' >= 'abcd'", "false"); - - -shouldBeFalse("'abc' <= 0"); // #35246 -shouldBeTrue("'' <= 0"); -shouldBeTrue("' ' <= 0"); -shouldBeTrue("null <= 0"); -shouldBeFalse("0 <= 'abc'"); -shouldBeTrue("0 <= ''"); -shouldBeTrue("0 <= null"); -shouldBeTrue("null <= null"); -shouldBeTrue("6 < '52'"); -shouldBeTrue("6 < '72'"); // #36087 -shouldBeFalse("NaN < 0"); -shouldBeFalse("NaN <= 0"); -shouldBeFalse("NaN > 0"); -shouldBeFalse("NaN >= 0"); - - -// strict comparison === -shouldBeFalse("0 === false"); -shouldBeTrue("null === null"); -shouldBeFalse("NaN === NaN"); -shouldBeTrue("0.0 === 0"); -shouldBeTrue("'abc' === 'abc'"); -shouldBeFalse("'a' === 'x'"); -shouldBeFalse("1 === '1'"); -shouldBeFalse("'1' === 1"); -shouldBeTrue("true === true"); -shouldBeTrue("false === false"); -shouldBeFalse("true === false"); -shouldBeTrue("Math === Math"); -shouldBeFalse("Math === Boolean"); -shouldBeTrue("Infinity === Infinity"); - - -shouldBe("0 !== 0", "false"); -shouldBe("0 !== 1", "true"); - - -shouldBe("typeof undefined", "'undefined'"); -shouldBe("typeof null", "'object'"); -shouldBe("typeof true", "'boolean'"); -shouldBe("typeof false", "'boolean'"); -shouldBe("typeof 1", "'number'"); -shouldBe("typeof 'a'", "'string'"); -shouldBe("typeof shouldBe", "'function'"); -shouldBe("typeof Number.NaN", "'number'"); - - -shouldBe("11 && 22", "22"); -shouldBe("null && true", "null"); -shouldBe("11 || 22", "11"); -shouldBe("null || 'a'", "'a'"); - -shouldBeUndefined("void 1"); - -shouldBeTrue("1 in [1, 2]"); -shouldBeFalse("3 in [1, 2]"); -shouldBeTrue("'a' in { a:1, b:2 }"); - -// Make sure that for ... in reevaluates the scoping every time! -var P = { foo : 1, bar : 2, baz : 3 }; - -function testForIn() { - for (g in P) { - eval("var g;"); //Change the scope of g half-ways through the loop - } -} - -testForIn(); -shouldBe("g", "'foo'"); //Before the eval, g was in outer scope, but not after! - - -function testSwitch(v) { - var result = ""; - switch (v) { - case 0: result += 'a'; - case 1: result += 'b'; - case 1: result += 'c'; - case 2: result += 'd'; break; - } - return result; -} - -shouldBe("testSwitch(0)", "'abcd'"); -shouldBe("testSwitch(1)", "'bcd'"); // IE agrees, NS disagrees -shouldBe("testSwitch(2)", "'d'"); -shouldBe("testSwitch(false)", "''"); - -function testSwitch4(v) { - var result = ""; - switch (v) { - case 0: result += 'a'; result += 'b'; break; - } - return result; -}; - -shouldBe("testSwitch4(0)", "'ab'"); - -var myvar = 1; - -function varInFunction() { - return (myvar == undefined); - var myvar = 2; -} - -function varInVarList() { - return (myvar == undefined); - var a = 1, b = 0, myvar = 2; -} - -function varListOrder() { - var tmp = 0; - var i = ++tmp, j = ++tmp; - return j == 2; -} - -function varInBlock() { - return (myvar == undefined); - { - var myvar = 2; - } -} - -function varInIf() { - return (myvar == undefined); - if (false) - var myvar = 2; -} - -function varInElse() { - return (myvar == undefined); - if (true) { - } - else - var myvar = 2; -} - -function varInDoWhile() { - return (myvar == undefined); - do - var myvar = 2; - while (false); -} - -function varInWhile() { - return (myvar == undefined); - while (false) - var myvar = 2; -} - -function varInFor() { - return (myvar == undefined); - var i; - for (i = 0; i < 0; i++) - var myvar = 2; -} - -function varInForInitExpr() { - return (myvar == undefined); - for (var myvar = 2; i < 2; i++) { - } -} - -function varInWith() { - return (myvar == undefined); - with (String) - var myvar = 2; -} - -function varInCase() { - return (myvar == undefined); - switch (1) { - case 0: var myvar = 2; - case 1: - } -} - -if (!varGlobal) - var varGlobal = 1; - -shouldBe("varInFunction()","true"); -shouldBe("varInVarList()","true"); -shouldBe("varListOrder()","true"); -shouldBe("varInBlock()","true"); -shouldBe("varInIf()","true"); -shouldBe("varInElse()","true"); -shouldBe("varInDoWhile()","true"); -shouldBe("varInWhile()","true"); -shouldBe("varInFor()","true"); -shouldBe("varInWith()","true"); -shouldBe("varInCase()","true"); -shouldBe("varInForInitExpr()","true"); -shouldBe("varGlobal", "1"); - -var overrideVar = 1; -var overrideVar; -shouldBe("overrideVar", "1"); - -var overrideVar2 = 1; -var overrideVar2 = 2; -shouldBe("overrideVar2", "2"); - -console.log(testsPass+" out of "+tests+" passed"); -result = testsPass == tests; -