I am trying to make something like Java's enum. It works perfectly, but when I try to compile it with ClosureCompiler, I get this warning:
F:\Site\_data\test-ccomp>ccomp -O ADVANCED --js source.js --js_output_file compiled.js
source.js:49:18: WARNING - [JSC_TYPE_MISMATCH] Can only iterate over a (non-null) Iterable type
found : (typeof Color)
required: Iterable
49| for( const clr of Color ) {
^^^^^
0 error(s), 1 warning(s), 87.2% typed
How to get rid of it? Which annotations to use?
If I make Color an object, not a function, with properties RED, GREEN, BLUE of some ColorObj type, and [Symbol.iterator] to iterate over them, it is still the same warning.
//////////////////
// Color type
//////////////////
/**
* For id generation.
*/
let color_counter = 0;
/**
* #constructor
* #param {string} txt Color name.
*/
function Color(txt) {
const id = color_counter++;
const text = txt;
this.ordinal = function() {
return id;
}
this.name = function() {
return text;
}
}
/**
* #const
* #type {Color}
*/
Color.BLUE = new Color("blue");
/**
* #const
* #type {Color}
*/
Color.RED = new Color("red");
/**
* #const
* #type {Color}
*/
Color.YELLOW = new Color("yellow");
/**
* What to annotate here for warning to go away?!
*/
Color[Symbol.iterator] = function*() {
yield Color.BLUE;
yield Color.RED;
yield Color.YELLOW;
}
///////////////////
// Test program
///////////////////
console.info("Colors:");
for (const clr of Color) {
console.info(" - " + clr.name());
}
console.info("Color.BLUE is " + Color.BLUE.name());
console.info("Color.RED is " + Color.RED.name());
Related
I'm doing a coding challenge and I'm having difficulty returning anything besides undefined. Instead of true or false from the function defined after
Logger.prototype.shouldPrintMessage = function(timestamp, message)
I use MD5 Hashing to match if an exact message has been sent prior.
It's probably something obvious but I don't have enough experience with class/constructor functions.
You need to copy and paste the MD5 Hash function to the top of the code for it to work. You can find it in the first comment of this code.
// MD5 Hash
// Taken from : http://www.myersdaily.org/joseph/javascript/md5-text.html
// Paste Here
/**
* Initialize your data structure here.
*/
var Logger = function() {
this.messageStore = {}
};
/**
* Returns true if the message should be printed in the given timestamp, otherwise returns false.
If this method returns false, the message will not be printed.
The timestamp is in seconds granularity.
* #param {number} timestamp
* #param {string} message
* #return {boolean}
*/
Logger.prototype.shouldPrintMessage = function(timestamp, message) {
let hashedMessage = md5(message);
if (!(hashedMessage in this.messageStore)){
this.messageStore[message] = timestamp;
} else if ((timestamp - this.messageStore.hashedMessage) > 9) {
return true;
}
else if ((timestamp - this.messageStore.hashedMessage) < 10) {
return false;
}
else {
return undefined;
}
};
/**
* Your Logger object will be instantiated and called as such:
* var obj = new Logger()
* var param_1 = obj.shouldPrintMessage(timestamp,message)
*/
var obj = new Logger();
console.log(obj.shouldPrintMessage(1,"foo"));
console.log(obj.shouldPrintMessage(2,"bar"));
console.log(obj.shouldPrintMessage(3,"foo"));
console.log(obj.shouldPrintMessage(8,"bar"));
console.log(obj.shouldPrintMessage(10,"foo"));
console.log(obj.shouldPrintMessage(11,"foo"));
You need to do this.messageStore[hashedMessage] = timestamp; since that is what you are storing it as, and then anywhere you do this.messageStore.hashedMessage you need to do this.messageStore[hashedMessage']:
// MD5 Hash
// Taken from : http://www.myersdaily.org/joseph/javascript/md5-text.html
// Paste Here
md5 = window.btoa;
/**
* Initialize your data structure here.
*/
var Logger = function() {
this.messageStore = {}
};
/**
* Returns true if the message should be printed in the given timestamp, otherwise returns false.
If this method returns false, the message will not be printed.
The timestamp is in seconds granularity.
* #param {number} timestamp
* #param {string} message
* #return {boolean}
*/
Logger.prototype.shouldPrintMessage = function(timestamp, message) {
let hashedMessage = md5(message);
if (!(hashedMessage in this.messageStore)){
this.messageStore[hashedMessage] = timestamp;
return;
} else if ((timestamp - this.messageStore[hashedMessage]) > 9) {
return true;
}
else if ((timestamp - this.messageStore[hashedMessage]) < 10) {
return false;
}
};
/**
* Your Logger object will be instantiated and called as such:
* var obj = new Logger()
* var param_1 = obj.shouldPrintMessage(timestamp,message)
*/
var obj = new Logger();
console.log(obj.shouldPrintMessage(1,"foo"));
console.log(obj.shouldPrintMessage(2,"bar"));
console.log(obj.shouldPrintMessage(3,"foo"));
console.log(obj.shouldPrintMessage(8,"bar"));
console.log(obj.shouldPrintMessage(10,"foo"));
console.log(obj.shouldPrintMessage(11,"foo"));
I do have following Errors when js file is minified
/* Minification failed. Returning unminified contents.
(36307,7-8): run-time error JS1010: Expected identifier: .
(36307,7-8): run-time error JS1195: Expected expression: .
(36817,7-8): run-time error JS1010: Expected identifier: .
(36817,7-8): run-time error JS1195: Expected expression: .
(36820,7-8): run-time error JS1010: Expected identifier: .
(36820,7-8): run-time error JS1195: Expected expression: .
*/
The JavaScript file script file below,
However I found the JavaScript that contains "module.export"
will not be minified and I also use
online tool
for minification, but the minified file does not contain "module.export"
and it is removed
during minification. Any help how to sort the minification problem of JavaScript file contain module.export
(function e(t, n, r) {
module.exports = FilterCSS;
}, {
"./default": 7,
"./parser": 9,
"./util": 10
}], 7: [function(require, module, exports) {
/**
* cssfilter
*
* #author ??<leizongmin#gmail.com>
*/
function getDefaultWhiteList() {
// ??????:
// true: ?????
// Function: function (val) { } ??true???????,?????????
// RegExp: regexp.test(val) ??true???????,?????????
// ??????????????
var whiteList = {};
whiteList['align-content'] = false; // default: auto
whiteList['align-items'] = false; // default: auto
*
*
#param {
String
}
name
*
#param {
String
}
value
*
#param {
Object
}
options
*
#return {
String
}
*/
function onAttr(name, value, options) {
// do nothing
}
/**
* ???????????????
*
* #param {String} name
* #param {String} value
* #param {Object} options
* #return {String}
*/
function onIgnoreAttr(name, value, options) {
// do nothing
}
var REGEXP_URL_JAVASCRIPT = /javascript\s*\:/img;
/**
* ?????
*
* #param {String} name
* #param {String} value
* #return {String}
*/
function safeAttrValue(name, value) {
if (REGEXP_URL_JAVASCRIPT.test(value)) return '';
return value;
}
exports.whiteList = getDefaultWhiteList();
exports.getDefaultWhiteList = getDefaultWhiteList;
exports.onAttr = onAttr;
exports.onIgnoreAttr = onIgnoreAttr;
exports.safeAttrValue = safeAttrValue;
}, {}], 8: [function(require, module, exports) {
/**
* cssfilter
*
* #author ??<leizongmin#gmail.com>
*/
var DEFAULT = require('./default');
var FilterCSS = require('./css');
/**
* XSS??
*
* #param {String} css ????CSS??
* #param {Object} options ??:whiteList, onAttr, onIgnoreAttr
* #return {String}
*/
function filterCSS(html, options) {
var xss = new FilterCSS(options);
return xss.process(html);
}
// ??
exports = module.exports = filterCSS;
exports.FilterCSS = FilterCSS;
for (var i in DEFAULT) exports[i] = DEFAULT[i];
// ???????
if (typeof window !== 'undefined') {
window.filterCSS = module.exports;
}
}, {
"./css": 6,
"./default": 7
}], 9: [function(require, module, exports) {
/**
* cssfilter
*
* #author ??<leizongmin#gmail.com>
*/
var _ = require('./util');
Try like this:
['module'].exports = FilterCSS;
Also this:
[module].exports = FilterCSS;
It worked for me, but no idea why.
Before this epiphany, I had solve similar issues with global variables, using something like this:
window['module'].exports=......
But, at least in my case, module was no global.
No idea how I came up with this, and less idea how it worked. I'm new with javascript.
Resume: I'm currently writting an ActionScript 3 lexer that transforms a source code into tokens. I chosen to interpret the input, a String with optional surrogate pairs wrapped in a class UString, by code points. Under the hood I cache the last read position by using the UStringPos class.
I've tested how it scans the identifier "huehuehue" with...
'use strict';
import {Lexer} from 'core/Lexer';
import {UString} from 'utils/UString';
import ErrorHandler from 'core/ErrorHandler';
const errorHandler = new ErrorHandler(true);
// Tell the length to the `Lexer` manually.
const lexer = new Lexer(
new UString('huehuehue'), 9, errorHandler);
// Scan first token
lexer.next();
const id = lexer.lookahead.value;
console.log(
id,
id.length
);
It should have logged "huehuehue", 9, but was another story...
Why is it missing the last 'e'? The innermost method related on scanning this is Lexer#getCommonIdentifier. I've already tested my UString part and it works okay, by the way.
Lexer Related Definitions
/*
* Class that turns AS3 code into tokens.
*/
export class Lexer
{
/*
* #param {UString} source
* #param {Number} length
* #param {ErrorHandler} errorHandler
*/
constructor(source, length, errorHandler)
{
this.source = source;
this.length = length;
this.index = 0;
this.lineStart = 0;
this.lineNumber = 1;
this.comments = [];
this.errorHandler = errorHandler;
this.previousToken = null;
this.token = null;
this.lookahead = null;
this._special = [];
}
/*
* Verifies the end of file.
*/
eof()
{
return this.index >= this.length;
}
/*
* Advance the previous, current and lookahead tokens.
* The lexer however does not depend on these tokens.
*/
next()
{
this.previousToken = this.token;
this.token = this.lookahead;
this.lookahead = this.lex();
}
/*
* Consumes the next token and return it.
*/
lex()
{
this.consumeWhiteSpaces();
while (this.consumeComment())
this.consumeWhiteSpaces();
let cp = this.source.codePointAt(this.index);
let pureIdentifier =
Character.isIdentifierStart(cp);
if (pureIdentifier || (cp === 0x5C))
return this.scanIdentifierOrKeyword(!pureIdentifier);
if (this.eof())
{
let loc = [ this.index, this.lineNumber ];
return new Token(TokenType.EOF, loc, loc, '<end>');
}
}
/*
* Scan an identifier, keyword or boolean literal.
*/
scanIdentifierOrKeyword(usingEscape)
{
const start = this.index;
let id;
/* Like Esprima does: only identifiers containing
* escapes need some overheads. */
if (usingEscape)
{
id = this.getEscapedIdentifier(
String.fromCodePoint(this.scanUnicodeEscapeSequence()));
}
else
id = this.getCommonIdentifier();
return new Token(
TokenType.IDENTIFIER,
[ start , this.lineNumber ],
[ this.index, this.lineNumber ],
id
);
}
/*
* Interprets an identifier. If any escape appears, switches to
* getEscapedIdentifier().
*/
getCommonIdentifier()
{
const start = this.source.position.offset;
let cp = 0;
// Jump the starting symbol.
++this.index;
while (!this.eof())
{
cp = this.source.codePointAt(this.index);
if (Character.isIdentifierPart(cp))
++this.index;
// Switches to escape-minded task...
else if (cp === 0x5C)
return this.getUnicodeEscapedIdentifier(
this.source.string.slice(
start, this.source.position.offset
)
);
else break;
}
return this.source.string.slice(
start, this.source.position.offset
);
}
/* ... */
}
utils/UString.js
'use strict';
/*
* String wrapper with methods _based_ on code points.
*/
export class UString
{
/*
* Constructs the {UString}.
*
* #param {String} s String to be wrapped.
*/
constructor(s)
{
/*
* #type {String}
*/
this.string = s;
/*
* Tracks the last accessed position.
*
* #type {UStringPos}
*/
this.position = new UStringPos(0, 0);
}
/*
* Reads a code point at specific index.
*
* #param {Number} index
* #return {Number}
*/
codePointAt(index)
{
this.position.walk(this.string, index);
return this.string.codePointAt(this.position.offset);
}
/*
* Slices the internal string by code point indices.
*
* #param {Number} i
* #param {Number} j
* #return {String}
*/
slice(i, j)
{
this.position.walk(this.string, i);
i = this.position.offset;
this.position.walk(this.string, j);
j = this.position.offset;
return this.string.slice(i, j);
}
};
/*
* Class that tracks the position of a code point on a string.
*/
export class UStringPos
{
/*
* Constructs the {UStringPos}.
*
* #param {Number} index The initial index.
* #param {Number} offset The initial offset.
*/
constructor(index, offset)
{
/*
* #type {Number}
*/
this.index = index;
/*
* #type {Number}
*/
this.offset = offset;
}
/*
* Walks to the given index.
*
* #param {String} s
* #param {Number} index
* #note No backward. Track the previous position instead.
* #return {void}
*/
walk(s, index)
{
for (; this.index < index; ++this.index)
this.offset += (
this._usingSurrogates(
s.charCodeAt(this.offset)
) ? 2 : 1
);
}
/*
* #private
*/
_usingSurrogates(ch)
{
return (ch >= 0xD800) && (ch <= 0xDBFF);
}
};
Anything?
Okay. So it was a problem with this.source.position.offset: when I do ++this.index, the offset of my UStringPos doesn't update. The problem was with the slice thing.
this.source.string.slice(
start, this.source.position.offset
);
This slice was based on offsets, since I had to track the previous offset where the identifier started.
Solution
I can use the slice of my own UString class and use the first parameter as an offset and the last one as a normal index.
'use strict';
export class UString
{
// ...
/*
* Slices the internal string by using a pair of
* offset and code point indices.
*
* #param {Number} i Offset
* #param {Number} j
* #return {String}
*/
slice(i, j)
{
this.position.walk(this.string, j);
j = this.position.offset;
return this.string.slice(i, j);
}
};
Hello StackOverflowers,
I'm implementing a LocalStorage system for my web application.
But my pity is that all the objects in the Model abstraction layer have to be serialized.
I am aware that functions don't get serialized when the object is converted to JSON.
I was about to use Option 1 described here. (Which is, creating a 'static' method which returns a brand new object including the functions).
However, the problem is that even the non-function members of the objects don't get converted as well.
For example at divvie.textContent = JSON.stringify( coffee );, divvie.textContent becomes "{}".
Why? What's going on?
Sample code, not my real application but the situation is much the same:
Check Coffee
var coffee = new Coffee( "0001", "DE Red Coarse",
"Douwe Egberts, red & coarse grounded." );
coffee.addSugarCube( "0x0F_BROWN", "15" );
coffee.addSugarCube( "0x0C_WHITE", "12" );
var divvie = document.getElementById( "run" );
divvie.textContent = JSON.stringify( coffee );
</script>
</body>
</html>
/**
* #class This class represents a coffee.
*
* #param {String} id
* #param {String} name
* #param {String} description
* #returns {Coffee}
*/
function Coffee( id, name, description )
{
var sugarCubes = new Array();
var maxAmountOfSweetness = 0;
/**
* #returns {String}
*/
this.getId = function()
{
return id;
};
/**
* #returns {String}
*/
this.getName = function()
{
return name;
};
/**
* #returns {String}
*/
this.getDescription = function()
{
return description;
};
/**
* #param {String} sugarCubeId
* #param {Number} amountOfSweetness
*/
this.addSugarCube = function( sugarCubeId, amountOfSweetness )
{
/// First check if this sugarCube is already in our coffee.
var sugarCubeFound = false;
for( var i = 0; i < sugarCubes.length; i++ )
{
if( sugarCubes[ i ].getId() === sugarCubeId )
{
sugarCubeFound = true;
i = sugarCubes.length;
}
}
if( !sugarCubeFound )
{
/// Oh Sweet! A new sugar cube to add in our coffee!
sugarCubes.push( new SugarCube( sugarCubeId, amountOfSweetness ) );
maxAmountOfSweetness = Math.max( maxAmountOfSweetness, amountOfSweetness );
}
};
/**
* #param {String} sugarCubeId
* #returns {SugarCube}
*/
this.getSugarCube = function( sugarCubeId )
{
for( var i = 0; i < sugarCubes.length; i++ )
{
if( sugarCubes[ i ].getId() === sugarCubeId )
{
return sugarCubes[ i ];
}
}
};
/**
* #returns {Boolean} True when the amount of sugar cubes in this coffee is 1 or more,
* false when not.
*/
this.isSweet = function()
{
if( 0 < sugarCubes.length )
{
return true;
}
return false;
};
}
/**
* #class This class represents a sugar cube
*
* #param {String} id
* #param {Number} amountOfSweetness
* #returns {SugarCube}
*/
function SugarCube( id, amountOfSweetness )
{
/**
* #returns {String}
*/
this.getId = function()
{
return id;
};
/**
* #returns {Number}
*/
this.getAmountOfSweetness = function()
{
return amountOfSweetness;
};
}
The only properties on instances created by the Coffee constructor seem to be the methods, which have no JSON representation (because they're functions). Your data is all kept as variables or parameters within the closures created. These are not properties and therefore again not something that JSON is designed for.
To get it working as you expect, you'll either need to write your own toJSON method, or move your data from vars to properties
For example, using properties
function Coffee(id, name, description) {
this.id = id;
this.name = name;
this.description = description;
this.sugarCubes = [];
this.maxAmountOfSweetness = 0;
}
// and now you could even move the methods into the prototype
Coffee.prototype = {
getId: function () {return this.id;}
// etc
};
// new Coffee() is now JSON-able
You probably used var to "hide" the data so it was only exposed through your methods, so the other way is to add a new method which converts it to JSON, e.g.
// in Coffee
this.toJSON = function (a, b) {
var o = {
id: id,
name: name
// etc
};
return JSON.stringify(o, a, b);
};
The javascript interpreter triggerred a SytaxError after i compressed my code, but i don't know the exact position triggered this problem at my code. It's very hard to find out. I tried jsLint aslo, but it can't point out problem aslo.
(function(){var session,start,end,pool={length:0},result=[],id=0;function poolAdd(procedure,expected,description){pool[id+++""]={"description":description||"No description","procedure":procedure,"expected":expected};pool["length"]++;return id-1;}function poolRun(id){var actual,expect,pass,test=pool[id];if(test){try{actual=typeof test.procedure==="function"?test.procedure():test.procedure;expect=typeof test.expected==="function"?test.expected():test.expected;pass=actual===expect;if(pass){test["pass"]=true}else{test["fail"]={"actual":actual,"expect":expect}}}catch(e){test["fail"]={"message":e.toString()}}finally{delete pool[id];pool.length--;return result.push(test)-1;}}return-1;}function percentage(fraction){return!(fraction>=0&&fraction<=1)?"Not available":(fraction*100+"").substr(0,5)+"%"}function Statics(data,session){if(this instanceof Statics){this.data=(data||result).slice(0);if(session){this.session={id:session.id,end:session.end,start:session.start}}}else{return new Statics(data,session)}}Statics.prototype={statics:function(format){var temp,report,passed=[],failed=[];format=format||"text";for(var i=0,l=this.data.length;i<l;i++){temp=this.data[i];if(typeof temp==="number"){temp=result[temp+""]}temp.pass&&passed.push(temp);temp.fail&&failed.push(temp)}if(format==="text"){report=[];report.push("***"+(this.session?" Session("+this.session.id+")":"")+" Test Report ***");report.push(" Cost: "+(this.session?(this.session.end-this.session.start)/1000:(end-start)/1000)+" seconds");report.push(" Total: "+this.data.length);report.push("Passed: "+passed.length);report.push("Failed: "+failed.length);report.push("Percet: "+percentage(passed.length/this.data.length));if(failed.length>0){report.push("Detail:");for(var i=0,l=failed.length;i<l;i++){temp=failed[i];if(temp.fail.message){report.push(i+1+")."+" >>>EXCEPTION THROWN<<< in test("+temp.description+") "+temp.fail.message)}else{report.push(i+1+")."+" >>>VALUE NOT MATCH<<< in test("+temp.description+") "+" expected "+temp.fail.expect+" actual "+temp.fail.actual)}}}return report.join("\n")}}}function session_start(){session=new Session();session.start=+new Date;}function session_end(){session&&(session.end=+new Date)}function session_destory(){session=null}function Session(){this.id=QQWB.uid();this.idPool=[];}Session.prototype={_addId:function(id){this.idPool.push(id)},add:function(procedure,expected,description){this._addId(poolAdd(procedure,expected,description));return this},run:function(){var temp,statics,results=[];for(var i=0,l=this.idPool.length;i<l;i++){temp=poolRun(this.idPool[i]);temp&&results.push(temp)}session_end();statics=Statics(results,session);session_destory();return statics}};QQWB.extend("test",{add:function(procedure,expected,description){session_start();session._addId(poolAdd(procedure,expected,description));return session},run:function(){session_destory();start=+new Date;for(var id in pool){if(id!=='length'&&pool.hasOwnProperty(id)){poolRun(id)}}end=+new Date;return Statics()},remains:function(){return pool.length}})}());
added my original code
(function (){
var
session, // test session
start, // start time of call test.run()
end, // end time of test.run()
pool = {length:0}, // pool of all the tests removed from pool when test done
result = [], // pool of the result of test
id = 0; // test unique id auto increment
/**
* Add a test item to pool
*
* #access private
* #return {Number} the index in the pool
*/
function poolAdd(procedure, expected, description) {
pool[id+++""] = {
"description": description || "No description"
,"procedure": procedure
,"expected": expected
};
pool["length"]++; // maintain the length property of pool
return id - 1; // id of current test
}
/**
* Run a test
*
* Test item will be marked as pass or fail then move from
* test's pool to result's pool
*
* #access private
* #param id {Number} the index in the test pool
* #return {Number} the index in the result pool
*/
function poolRun(id) {
var
actual, // actual value
expect, // expected value
pass, // indicate test whether passed
test = pool[id]; // the test object
if (test) {
try{
actual = typeof test.procedure === "function" ? test.procedure() : test.procedure; // run test procedure
expect = typeof test.expected === "function" ? test.expected() : test.expected; // run expect procedure
pass = actual === expect;
if (pass) {
test["pass"] = true;
} else {
test["fail"] = {
"actual": actual
,"expect": expect
}
}
} catch (e) {
test["fail"] = {
"message": e.toString()
}
} finally {
delete pool[id]; // remove from pool
pool.length--; // maintain the length property of pool
return result.push(test) - 1; // the index in the result array for current test item
}
}
return -1; // id not valid
}
/**
* Float to percentage string
*
* #access private
* #param fraction {Number} float number
* #return {String} percentage string
*/
function percentage (fraction) {
return !(fraction >= 0 && fraction <= 1) ? "Not available" : (fraction * 100 + "").substr(0,5) + "%";
}
/**
* Statics constructor
*
* Self invoke constructor
*
* Note:
* If data is passed in,it means the statics running in a session
* Data contains *position* of result pool
*
* #access private
* #param data {Array} partial result keys array or the result
* #return {Statics} a statics instance
*/
function Statics(data, session) {
if (this instanceof Statics) {
this.data = (data || result).slice(0); // clone array
if (session) {
this.session = {
id: session.id
,end: session.end
,start: session.start
};
}
} else {
return new Statics(data, session);
}
}
Statics.prototype = {
/**
* Get statics
*
* Currently only support text statics
*
* #access public
* #param format {String} text,xml,json etc.
* #return {Mixed} result statics
*/
statics: function (format) {
var
temp, // temp value
report, // statics report
passed = [], // passed tests pool
failed = []; // failed tests pool
format = format || "text"; // default report type is text
for (var i=0,l=this.data.length; i<l; i++) {
temp = this.data[i];
if (typeof temp === "number") { // convert key to its value
temp = result[temp+""];
}
temp.pass && passed.push(temp);
temp.fail && failed.push(temp);
}
if (format === "text") {
report = [];
report.push("***" + (this.session ? " Session(" + this.session.id +")":"") + " Test Report ***");
report.push(" Cost: " + (this.session ? (this.session.end - this.session.start)/1000 : (end - start)/1000) + " seconds");
report.push(" Total: " + this.data.length);
report.push("Passed: " + passed.length);
report.push("Failed: " + failed.length);
report.push("Percet: " + percentage(passed.length/this.data.length));
if (failed.length > 0) {
report.push("Detail:");
for (var i=0,l=failed.length; i<l; i++) {
temp = failed[i];
if (temp.fail.message) {
report.push( i + 1 + ")." + " >>>EXCEPTION THROWN<<< in test(" + temp.description + ") " + temp.fail.message);
} else {
report.push( i + 1 + ")." + " >>>VALUE NOT MATCH<<< in test(" + temp.description + ") " + " expected " + temp.fail.expect + " actual " + temp.fail.actual);
}
}
}
return report.join("\n");
}
}
}
/**
* Start a new test session
*
* #access private
* #return {Void}
*/
function session_start() {
// if checked T.test.add(...); T.test.add(...); will be treated as one session till run is called
// if (!session) {
session = new Session();
session.start = + new Date;
// }
}
/**
* End current session
*
* #access private
* #return {Void}
*/
function session_end() {
session && (session.end = + new Date);
}
/**
* Destory current session
*
* #access private
* #return {Void}
*/
function session_destory() {
session = null;
}
/**
* Session constructor
*
* #access private
* #return {Session} session instance
*/
function Session() {
this.id = QQWB.uid(); // random session id
this.idPool = []; // contains id of test
}
Session.prototype = {
/**
* Add test id to session
*
* #access private
* #param id {Number} test id
* #return {Void}
*/
_addId: function (id) {
this.idPool.push(id);
}
/**
* Add test item to session
*
* Note:
* Test item added to global pool
* Session just store the position of test in the pool
*
* Example usage:
* T.test
* .add(...) //add a test to session(and pool)
* .add(...) //add a test to session(and pool)
* ...
*
*
* #access public
* #param procedure {Mixed} the procedure to finish this test
* if procedure is a function it will
* be executed automaticlly
* #param expected {Mixed} the expected value of this test
* if expected is a function it will
* be executed automaticlly
* #param description {String} speak more about this test
* #return {Session} the current session
*/
,add: function (procedure, expected, description) {
this._addId(poolAdd(procedure, expected, description));
return this;
}
/**
* Run the tests in current session
*
* Example usage:
* T.test
* .add(...) //add a test to session(and pool)
* .add(...) //add a test to session(and pool)
* ...
* .run() // run the test in this session
* .statics() // get statics for this session
*
* #access public
* #return {Statics} statics object
*/
,run: function () { // run in session
var temp,
statics,
results = [];
for (var i=0,l=this.idPool.length; i<l; i++) {
temp = poolRun(this.idPool[i]);
temp && results.push(temp);
}
session_end(); // record the time of this session end
statics = Statics(results,session);
session_destory();// destory test session
return statics;
}
};
QQWB.extend("test",{
/**
* Add test item to session
*
* Note:
* Test item added to global pool
* Current session will store the position of test in the pool
*
* Example usage:
* T.test
* .add(...) //add a test to session(and pool)
* .add(...) //add a test to session(and pool)
* ...
*
* #access public
* #param procedure {Mixed} the procedure to finish this test
* if procedure is a function it will
* be executed automaticlly
* #param expected {Mixed} the expected value of this test
* if expected is a function it will
* be executed automaticlly
* #param description {String} speak more about this test
* #return {Session} the current session
*/
add: function (procedure, expected, description) {
session_start(); // spawn a new session object
session._addId(poolAdd(procedure, expected, description));
return session;
}
/**
* Run all the tests except the test already done
*
* Example usage:
* T.test
* .add(...) //add a test to session(and pool)
* .add(...) //add a test to session(and pool)
* ...
*
* T.test
* .run() // run all the tests except already done
* .statics() //get full statics of all the tests
*
* #access public
* #return {Statics} statics object
*/
,run: function () {
session_destory(); // destory current session
start = + new Date; // start time of global test
for (var id in pool) {
if (id !== 'length' && pool.hasOwnProperty(id)) {
poolRun(id);
}
}
end = + new Date; // end time of global test
return Statics();
}
/**
* Get count of tests left in the pool
*
* #access public
* #return {Number} count of tests unfinished yet
*/
,remains: function () {
return pool.length;
}
});
}());
This is the script packer
http://dean.edwards.name/packer/
Edited:
I solved this problem by myself finally,I paste the code piece by piece to the packer. finally find out that the defination of Statics.prototype isn't end with a semicolon which caused SytaxError.
I tried copying the javascript to http://jsfiddle.net/ then clicking JSLint - gave me all sorts of errors.
What tool are you using for the compression?
My guess is missing semicolons.
For example:
test["fail"] = {
"message": e.toString()
}
Should have a semicolon after the closing brace.