support for ascii qrcodes in yout terminal [./bin/qrcode texthere]

This commit is contained in:
Ryan Day 2012-03-02 11:24:25 -08:00
parent 70b0f6f91d
commit e097dc1a8b
18 changed files with 1499 additions and 1437 deletions

20
bin/qrcode Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env node
try{
var qr = require('qrcode');
} catch (e) {
qr = require(__dirname+'/../qrcode.js');
}
var text = process.argv[2];
if (text && text.length) {
qr.drawAscii(text,function(error,text){
process.stdout.write(text);
process.stdout.write("\n");
});
} else {
process.stderr.write("text required as first argument.\n");
}

42
build.js Normal file
View file

@ -0,0 +1,42 @@
var spawn = require('child_process').spawn,
fs = require('fs');
var q = [
function(){
var browserify = spawn('node',['node_modules/browserify/bin/cli.js','qrcodeclient.js','-o', 'build/qrcode.js']);
browserify.stdin.end();
browserify.stdout.pipe(process.stdout);
browserify.on('exit',function(code){
if(code){
console.error('browserify failed!');
process.exit(code);
}
done();
});
},
function(){
var uglify = spawn('node',['node_modules/uglify-js/bin/uglifyjs','build/qrcode.js']);
var minStream = fs.createWriteStream('build/qrcode.min.js');
uglify.stdout.pipe(minStream);
uglify.stdin.end();
uglify.on('exit',function(code){
if(code){
console.error('uglify failed!');
fs.unlink('build/qrcode.min.js',function(){
process.exit(code);
});
}
done();
});
}
],done = function(){
var j = q.shift();
if(j) j();
else complete()
},
complete = function(){
console.log('build complete =)');
};
done();

0
build/.g Normal file
View file

View file

@ -6,18 +6,8 @@ each entry is in the order of error correct level
the qrcode lib sets strange values for QRErrorCorrectLevel having to do with masking against patterns
the maximum string length for error correct level H is 1273 characters long.
*/
(function(){
//grab a refrence to the window but dont fail if im on the server side
var G = typeof window !='undefined'?window:{}
, m = typeof module == 'undefined'?{}:module
, nodeQrcode;
if(!m.exports) m = {exports:{}};
if(!G.nodeQrcode) G.nodeQrcode = {};
nodeQrcode = G.nodeQrcode;
nodeQrcode.QRVersionCapacityTable = m.exports = [
exports.QRCapacityTable = [
[17,14,11,7]
,[32,26,20,14]
,[53,42,32,24]
@ -59,4 +49,3 @@ nodeQrcode.QRVersionCapacityTable = m.exports = [
,[2809,2213,1579,1219]
,[2953,2331,1663,1273]//40
];
}());

View file

@ -1,266 +1,268 @@
/*
* copyright 2010 Ryan Day
* http://github.com/soldair/node-qrcode
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* canvas example and fallback support example provided by Joshua Koo
* http://jabtunes.com/labs/qrcode.html
* "Instant QRCode Mashup by Joshua Koo!"
* as far as i can tell the page and the code on the page are public domain
*
* original table example and library provided by Kazuhiko Arase
* http://d-project.googlecode.com/svn/trunk/misc/qrcode/js/
*
*
*/
(function(){
//grab a refrence to the window but dont fail if im on the server side
var G = typeof window !='undefined'?window:{}
, m = typeof module == 'undefined'?{}:module
, dir = typeof __dirname == 'undefined'?'':__dirname
, nodeQrcode
//bind dependencies
, QRCodeLib
, QRVersionCapacityTable;
* copyright 2010 Ryan Day
* http://github.com/soldair/node-qrcode
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* canvas example and fallback support example provided by Joshua Koo
* http://jabtunes.com/labs/qrcode.html
* "Instant QRCode Mashup by Joshua Koo!"
* as far as i can tell the page and the code on the page are public domain
*
* original table example and library provided by Kazuhiko Arase
* http://d-project.googlecode.com/svn/trunk/misc/qrcode/js/
*
*/
//if im loaded in the brower i wont have module. in theory
if(!m.exports) m = {exports:{}};
if(!G.nodeQrcode) G.nodeQrcode = {};
//local refrence of global package name
nodeQrcode = G.nodeQrcode;
var QRCodeLib = require('./qrcode.js');
var QRVersionCapacityTable = require('./qrcapacitytable.js').QRCapacityTable;
var QRCode = QRCodeLib.QRCode;
if(nodeQrcode.QRCode) {
QRCodeLib = {
QRCode:nodeQrcode.QRCode,
QRErrorCorrectLevel:nodeQrcode.QRErrorCorrectLevel
};
QRVersionCapacityTable = nodeQrcode.QRVersionCapacityTable;
} else if (typeof require == 'function'){
QRCodeLib = require(dir+'/qrcode.js');
QRVersionCapacityTable = require(dir+'/qrcapacitytable.js');
} else {
throw new Error('cannot locate required libs in global nodeQrcode and i dont have a require function');
}
//export QRCodeDraw
nodeQrcode.QRCodeDraw = module.exports = QRCodeDraw;
exports.QRCodeDraw = QRCodeDraw;
exports.QRVersionCapacityTable = QRVersionCapacityTable;
exports.QRErrorCorrectLevel = QRCodeLib.QRErrorCorrectLevel;
exports.QRCode = QRCodeLib.QRCode;
function QRCodeDraw(){}
QRCodeDraw.prototype = {
scale:4,//4 px module size
defaultMargin:20,
marginScaleFactor:5,
// you may configure the error behavior for input string too long
errorBehavior:{
length:'trim'
},
color:{
dark:'black',
light:'white'
},
defaultErrorCorrectLevel:QRCodeLib.QRErrorCorrectLevel.H,
QRErrorCorrectLevel:QRCodeLib.QRErrorCorrectLevel,
draw:function(canvas,text/*,errorCorrectLevel,cb*/) {
var cb,errorCorrectLevel
,level,error;
scale:4,//4 px module size
defaultMargin:20,
marginScaleFactor:5,
// you may configure the error behavior for input string too long
errorBehavior:{
length:'trim'
},
color:{
dark:'black',
light:'white'
},
defaultErrorCorrectLevel:QRCodeLib.QRErrorCorrectLevel.H,
QRErrorCorrectLevel:QRCodeLib.QRErrorCorrectLevel,
draw:function(canvas,text,options,cb){
var cb,
options = {},
level,
error;
//argument processing
if(arguments.length == 2 || typeof arguments[arguments.length-1] != 'function') {
//enforce callback api just in case the processing can be made async in the future
// or support proc open to libqrencode
throw new Error('callback required as last argument');
}
cb = arguments[arguments.length-1];
if(arguments.length > 3){
errorCorrectLevel = arguments[2];
}
//this interface kinda sucks - there is very small likelyhood of this ever being async
this.QRLevel(text,errorCorrectLevel,function(e,t,l,ec){
text = t,level = l,error = e,errorCorrectLevel = ec;
});
var args = Array.prototype.slice.call(arguments);
cb = args.pop();
canvas = args.shift();
options = args.shift()||{};
if(!level) {
//if we are unable to find an appropriate qr level error out
cb(error,canvas);
return;
}
if(typeof cb != 'function') {
//enforce callback api just in case the processing can be made async in the future
// or support proc open to libqrencode
throw new Error('callback required');
}
if(typeof options !== "object"){
options.errorCorrectLevel = options;
}
//create qrcode!
try{
var qr = new QRCodeLib.QRCode(level, errorCorrectLevel)
, scale = this.scale||4
, ctx = canvas.getContext('2d')
, width = 0;
this.QRVersion(text,options.errorCorrectLevel||this.QRErrorCorrectLevel.H,options.version,function(e,t,l,ec){
text = t,level = l,error = e,errorCorrectLevel = ec;
});
qr.addData(text);
qr.make();
this.scale = options.scale||this.scale;
this.margin = options.margin||this.scale*2;
if(!level) {
//if we are unable to find an appropriate qr level error out
cb(error,canvas);
return;
}
var margin = this.marginWidth();
var currenty = margin
width = this.dataWidth(qr)+ margin*2;
this.resetCanvas(canvas,ctx,width);
//create qrcode!
try{
var qr = new QRCodeLib.QRCode(level, errorCorrectLevel)
, scale = this.scale||4
, ctx = canvas.getContext('2d')
, width = 0;
for (var r = 0,rl=qr.getModuleCount(); r < rl; r++) {
var currentx = margin;
for (var c = 0,cl=qr.getModuleCount(); c < cl; c++) {
if (qr.isDark(r, c) ) {
ctx.fillStyle = this.color.dark;
ctx.fillRect (currentx, currenty, 1*scale, 1*scale);
} else if(this.color.light){
//if falsy configured color
ctx.fillStyle = this.color.light;
ctx.fillRect (currentx, currenty, 1*scale, 1*scale);
}
currentx += scale *1;
}
currenty += scale*1;
}
} catch (e) {
error = e;
}
cb(error,canvas,width);
},
drawBitArray:function(text/*,errorCorrectLevel,cb*/) {
console.log('called');
var cb,errorCorrectLevel
,level,error;
qr.addData(text);
qr.make();
//argument processing
if(arguments.length == 1 || typeof arguments[arguments.length-1] != 'function') {
//enforce callback api just in case the processing can be made async in the future
// or support proc open to libqrencode
throw new Error('callback required as last argument');
}
cb = arguments[arguments.length-1];
if(arguments.length > 2){
errorCorrectLevel = arguments[2];
}
var margin = this.marginWidth();
var currenty = margin;
width = this.dataWidth(qr)+ margin*2;
this.resetCanvas(canvas,ctx,width);
for (var r = 0,rl=qr.getModuleCount(); r < rl; r++) {
var currentx = margin;
for (var c = 0,cl=qr.getModuleCount(); c < cl; c++) {
if (qr.isDark(r, c) ) {
ctx.fillStyle = this.color.dark;
ctx.fillRect (currentx, currenty, scale, scale);
} else if(this.color.light){
//if falsy configured color
ctx.fillStyle = this.color.light;
ctx.fillRect (currentx, currenty, scale, scale);
}
currentx += scale;
}
currenty += scale;
}
} catch (e) {
error = e;
}
cb(error,canvas,width);
},
drawBitArray:function(text/*,errorCorrectLevel,options,cb*/) {
var args = Array.prototype.slice.call(arguments),
cb = args.pop(),
text = args.shift(),
errorCorrectLevel = args.shift(),
options = args.shift() || {};
//argument processing
if(typeof cb != 'function') {
//enforce callback api just in case the processing can be made async in the future
// or support proc open to libqrencode
throw new Error('callback required as last argument');
}
cb = arguments[arguments.length-1];
if(arguments.length > 2){
errorCorrectLevel = arguments[2];
}
//this interface kinda sucks - there is very small likelyhood of this ever being async
this.QRLevel(text,errorCorrectLevel,function(e,t,l,ec){
text = t,level = l,error = e,errorCorrectLevel = ec;
});
if(!level) {
//if we are unable to find an appropriate qr level error out
cb(error,[],0);
return;
}
//this interface kinda sucks - there is very small likelyhood of this ever being async
this.QRVersion(text,errorCorrectLevel,(options||{}).version,function(e,t,l,ec){
text = t,level = l,error = e,errorCorrectLevel = ec;
});
//create qrcode!
try{
//console.log(text,level,error,errorCorrectLevel);
var qr = new QRCodeLib.QRCode(level, errorCorrectLevel)
, scale = this.scale||4
, width = 0,bits,bitc=0,currenty=0;
qr.addData(text);
qr.make();
width = this.dataWidth(qr);
bits = new Array(width*width);
if(!level) {
//if we are unable to find an appropriate qr level error out
cb(error,[],0);
return;
}
for (var r = 0,rl=qr.getModuleCount(); r < rl; r++) {
var currentx = 0;//margin;
for (var c = 0,cl=qr.getModuleCount(); c < cl; c++) {
if (qr.isDark(r, c) ) {
bits[bitc] = 1;
} else {
bits[bitc] = 0;
}
currentx += scale;
bitc++;
}
currenty += scale;
}
} catch (e) {
error = e;
console.log(e.stack);
}
cb(error,bits,width);
},
//changed the interface here
QRLevel:function(text,errorCorrectLevel,cb){
var c = text.length
, error
, level = 0
,errorCorrectLevel = this.QRErrorCorrectLevel[errorCorrectLevel]||this.defaultErrorCorrectLevel
,errorCorrectIndex = [1,0,3,2]//fix odd mapping to order in table
,keys = ['L','M','Q','H'];
//create qrcode!
try{
var qr = new QRCodeLib.QRCode(level, errorCorrectLevel)
, scale = this.scale||4
, width = 0,bits,bitc=0,currenty=0;
qr.addData(text);
qr.make();
width = this.dataWidth(qr,1);
bits = new Array(width*width);
//TODO ADD THROW FOR INVALID errorCorrectLevel...?
for(var i=0,j=QRVersionCapacityTable.length;i<j;i++) {
if(c < QRVersionCapacityTable[i][errorCorrectIndex[errorCorrectLevel]]){
level = i+1;
break;
}
}
for (var r = 0,rl=qr.getModuleCount(); r < rl; r++) {
for (var c = 0,cl=qr.getModuleCount(); c < cl; c++) {
if (qr.isDark(r, c) ) {
bits[bitc] = 1;
} else {
bits[bitc] = 0;
}
bitc++;
}
}
} catch (e) {
error = e;
console.log(e.stack);
}
cb(error,bits,width);
},
QRVersion:function(text,errorCorrectLevel,version,cb){
var c = text.length,
error,
errorCorrectLevel = this.QRErrorCorrectLevel[errorCorrectLevel]||this.defaultErrorCorrectLevel,
errorCorrectIndex = [1,0,3,2],//fix odd mapping to order in table
keys = ['L','M','Q','H'],
capacity = 0,
versionSpecified = false;
if(!level){
if(this.errorBehavior.length == 'trim'){
text = text.substr(0,QRVersionCapacityTable[QRVersionCapacityTable.length-1][errorCorrectIndex[errorCorrectLevel]]);
level = QRVersionCapacityTable.length;
} else {
error = new Error('input string too long for error correction '
+keys[errorCorrectIndex[errorCorrectLevel]]
+' max length '
+QRVersionCapacityTable[QRVersionCapacityTable.length-1][errorCorrectIndex[errorCorrectLevel]]
+' for qrcode version '+(QRVersionCapacityTable.length-1)
);
}
}
if(cb) {
cb(error,text,level,errorCorrectLevel);
}
return level;
},
marginWidth:function(){
var margin = this.defaultMargin;
this.scale = this.scale||4;
//elegant white space next to code is required by spec
if (this.scale * this.marginScaleFactor > margin) {
margin = this.scale * this.marginScaleFactor;
}
return margin;
},
dataWidth:function(qr){
return qr.getModuleCount()*(this.scale||4);
},
resetCanvas:function(canvas,ctx,width){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(!canvas.style) canvas.style = {};
canvas.style.height = canvas.height = width;//square!
canvas.style.width = canvas.width = width;
if(this.color.light){
ctx.fillStyle = this.color.light;
ctx.fillRect(0,0,canvas.width,canvas.height);
} else {
//support transparent backgrounds?
//not exactly to spec but i really would like someone to be able to add a background with heavily reduced luminosity for simple branding
//i could just ditch this because you could also just set #******00 as the color =P
ctx.clearRect(0,0,canvas.width,canvas.height);
}
}
if(typeof version !== "undefined" && version !== null) {
versionSpecified = true;
}
//TODO ADD THROW FOR INVALID errorCorrectLevel...?
if(versionSpecified){
console.log('SPECIFIED VERSION! ',version);
//i have specified a version. this will give me a fixed size qr code. version must be valid. 1-40
capacity = QRVersionCapacityTable[version][errorCorrectIndex[errorCorrectLevel]];
} else {
//figure out what version can hold the amount of text
for(var i=0,j=QRVersionCapacityTable.length;i<j;i++) {
capacity = QRVersionCapacityTable[i][errorCorrectIndex[errorCorrectLevel]];
if(c < QRVersionCapacityTable[i][errorCorrectIndex[errorCorrectLevel]]){
version = i+1;
break;
}
}
//if not version set to max
if(!version) {
version = QRVersionCapacityTable.length-1;
}
}
if(capacity < c){
if(this.errorBehavior.length == 'trim'){
text = text.substr(0,capacity);
level = QRVersionCapacityTable.length;
} else {
error = new Error('input string too long for error correction '
+keys[errorCorrectIndex[errorCorrectLevel]]
+' max length '
+ capacity
+' for qrcode version '+version
);
}
}
if(cb) {
cb(error,text,version,errorCorrectLevel);
}
return version;
},
marginWidth:function(){
var margin = this.defaultMargin;
this.scale = this.scale||4;
//elegant white space next to code is required by spec
if (this.scale * this.marginScaleFactor > margin) {
margin = this.scale * this.marginScaleFactor;
}
return margin;
},
dataWidth:function(qr,scale){
return qr.getModuleCount()*(scale||this.scale||4);
},
resetCanvas:function(canvas,ctx,width){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(!canvas.style) canvas.style = {};
canvas.style.height = canvas.height = width;//square!
canvas.style.width = canvas.width = width;
if(this.color.light){
ctx.fillStyle = this.color.light;
ctx.fillRect(0,0,canvas.width,canvas.height);
} else {
//support transparent backgrounds?
//not exactly to spec but i really would like someone to be able to add a background with heavily reduced luminosity for simple branding
//i could just ditch this because you could also just set #******00 as the color =P
ctx.clearRect(0,0,canvas.width,canvas.height);
}
}
};
}());

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/*---------------------------------------------------------------------
/**
* QRCode for JavaScript
*
* modified by Ryan Day for nodejs support
* Copyright (c) 2010 Ryan Day
* Copyright (c) 2011 Ryan Day
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
@ -12,21 +12,6 @@
* QRCode:QRCode
* QRErrorCorrectLevel:QRErrorCorrectLevel
* }
*/
(function(){
//grab a refrence to the window but dont fail if im on the server side
var G = typeof window !='undefined'?window:{}
, m = typeof module == 'undefined'?{}:module
, nodeQrcode;
//if im loaded in the brower i wont have module.
//module (in node at least) is in a weird way not a property of the GLOBAL object
if(!m.exports) m = {exports:{}};
if(!G.nodeQrcode) G.nodeQrcode = {};
nodeQrcode = G.nodeQrcode;
//---------------------------------------------------------------------
// QRCode for JavaScript
//
@ -42,36 +27,13 @@ nodeQrcode = G.nodeQrcode;
// http://www.denso-wave.com/qrcode/faqpatent-e.html
//
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// QR8bitByte
//---------------------------------------------------------------------
function QR8bitByte(data) {
this.mode = QRMode.MODE_8BIT_BYTE;
this.data = data;
}
QR8bitByte.prototype = {
getLength : function(buffer) {
return this.data.length;
},
write : function(buffer) {
for (var i = 0; i < this.data.length; i++) {
// not JIS ...
buffer.put(this.data.charCodeAt(i), 8);
}
}
};
*/
//---------------------------------------------------------------------
// QRCode
//---------------------------------------------------------------------
//EXPORT
nodeQrcode.QRCode = m.exports.QRCode = QRCode;
exports.QRCode = QRCode;
function QRCode(typeNumber, errorCorrectLevel) {
this.typeNumber = typeNumber;
@ -387,7 +349,7 @@ QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {
}
return QRCode.createBytes(buffer, rsBlocks);
}
};
QRCode.createBytes = function(buffer, rsBlocks) {
@ -452,8 +414,31 @@ QRCode.createBytes = function(buffer, rsBlocks) {
return data;
};
//---------------------------------------------------------------------
// QR8bitByte
//---------------------------------------------------------------------
function QR8bitByte(data) {
this.mode = QRMode.MODE_8BIT_BYTE;
this.data = data;
}
QR8bitByte.prototype = {
getLength : function(buffer) {
return this.data.length;
},
write : function(buffer) {
for (var i = 0; i < this.data.length; i++) {
// not JIS ...
buffer.put(this.data.charCodeAt(i), 8);
}
}
};
//---------------------------------------------------------------------
// QRMode
//---------------------------------------------------------------------
@ -468,10 +453,9 @@ var QRMode = {
//---------------------------------------------------------------------
// QRErrorCorrectLevel
//---------------------------------------------------------------------
//QRCodeLib is used when called from client side js
//exported
var QRErrorCorrectLevel = nodeQrcode.QRErrorCorrectLevel = m.exports.QRErrorCorrectLevel = {
var QRErrorCorrectLevel = exports.QRErrorCorrectLevel = {
L : 1,
M : 0,
Q : 3,
@ -1178,5 +1162,3 @@ QRBitBuffer.prototype = {
this.length++;
}
};
}());

61
lib/termialrender.js Normal file
View file

@ -0,0 +1,61 @@
var colors = require('colors');
module.exports.renderBits = renderBits;
function renderBits(bits,width,inverse){
var bottom = '▄';
var both = '█';
var top = '▀';
var bit = 0,nextRow,_b,_t,out = ' ',_debug = [],row = 0,i,j;
//add one row to out for top framing
for(i=0;i<width;++i) {
out += " ";
}
out += " \n";
for(i = 0;i<(width/2);i++){
//console.error('row ',i);
//_debug[row] = [];
//_debug[row+1] = [];
out += " ";
for(j = 0;j<width;j++){
//console.log('column ',j);
nextRow = bit+width;
c = ' ';
_t = 0;
_b = 0;
if(bits[bit]) {
_t = 1;
c = top.bold;
}
if(bits[nextRow]){
_b = 1;
c = bottom.underline;
}
if(_b && _t) {
c = both.underline;
}
//_debug[row].push(_t+'');
//_debug[row+1].push(_b+'');
out += c;
bit++;
}
bit += width;
//console.log('advancing bit to ',bit);
row += 2;
out += " \n";
}
//defaults tp inverse. this makes sense for people with dark terminals.
return inverse?out:out.inverse;
}

View file

@ -1,15 +1,24 @@
{ "name": "qrcode"
, "description": "QRCode / 2d Barcode api with both server side and client side support using canvas"
, "version": "0.1.1"
, "author": "Ryan Day <soldair@gmail.com>"
, "keywords": ["canvas", "qrcode", "barcode"]
, "main": "./qrcode.js"
,"description": "QRCode / 2d Barcode api with both server side and client side support using canvas"
,"version": "0.1.1"
,"author": "Ryan Day <soldair@gmail.com>"
,"keywords": ["canvas", "qrcode", "barcode"]
,"main": "./qrcode.js"
,"homepage":"http://github.com/soldair/node-qrcode"
,"scripts":{
"pretest":"node build.js"
,"prepublish":"node build.js"
,"test":"./test.sh"
}
,"dependencies": {
"canvas": ">= 0.4.3"
}
,"devDependencies":{
"connect":"1.8.*"
"express":"2.5.x"
,"browserify":"1.9.x"
,"uglify-js":"1.2.x"
,"connect":"1.8.x"
,"colors":"*"
}
,"repository":{
"type":"git"

122
qrcode.js
View file

@ -9,24 +9,22 @@
*
*/
var QRCodeDraw = require(__dirname+'/lib/qrcode-draw')
var QRCodeLib = require(__dirname+'/lib/qrcode-draw')
, termialRender = require(__dirname+'/lib/termialrender.js')
, Canvas = require('canvas')
, fs = require('fs')
, drawInstance = new QRCodeDraw();
, fs = require('fs');
var QRCodeDraw = QRCodeLib.QRCodeDraw,
QRCode = QRCodeLib.QRCode;
//EXPORTS
/*
so i return an instance... this is a terrible api because it kinda blocks
being able to do more than just extensions and the opperation of the qrcode
lib now alters the state of the object..
which is not an issue unless you need to read properties off of an object in a callback
*/
exports.QRCodeDraw = drawInstance;
/*
to make amends im adding the constriuctor to this object so extensions and instances can be made easily
this has to be done i dont like this name but backwards compat requires that the top object be an instance
*/
exports.QRCodeDrawConstructor = QRCodeDraw;
//
// breaking change to 0.1 this used to be an instance. now it returns the constructor.
//
exports.QRCodeDraw = QRCodeDraw;
/*
* provide an api to return the max characters allowed for given dimensions, and miniumum error correction level
@ -43,63 +41,61 @@ exports.getMaxChars = function(minErrorCorrectionLevel,width,moduleScale){
*/
var draw = exports.draw = function(text,options,cb){
arguments = [].slice.call(arguments);
cb = arguments.pop();
var args = Array.prototype.slice.call(arguments);
cb = args.pop();
if(typeof cb != 'function') {
throw new TypeError('last argument must be a function');
}
text = arguments.shift();
options = arguments.shift()||{};
//TODO add optional options argument before calback
//TODO in order to be predictable from a design perspective qr codes with dynamic data cannot break layout
text = args.shift();
options = args.shift()||{};
//NOTE the width and height are determined from within the qr code lib and are not configurable from the outside yet
drawInstance.draw(new Canvas(200,200),text,function(error,canvas){
var drawInstance = new QRCodeDraw();
drawInstance.draw(new Canvas(200,200),text,options,function(error,canvas){
cb(error,canvas)
});
};
//returns data uri for drawn qrcode png
exports.toDataURL = exports.toDataURI = function(text,cb){
draw(text,function(error,canvas){
if(error) {
cb(error,'');
} else {
canvas.toDataURL(cb);
}
});
draw(text,function(error,canvas){
if(error) {
cb(error,'');
} else {
canvas.toDataURL(cb);
}
});
}
//synchronous PNGStream
var pngStream = exports.toPNGStream = function (text, WSpath, cb) {
var out = fs.createWriteStream(WSpath);
draw(text,function (error,canvas) {
if(error) {
cb(error,'');
} else {
stream = canvas.createPNGStream();
}
exports.toPNGStream = function (text, WSpath, cb) {
var out = fs.createWriteStream(WSpath);
stream.on('end', function () {
cb(error,'');
});
draw(text,function (error,canvas) {
if(error) {
cb(error,'');
} else {
stream = canvas.createPNGStream();
}
stream.pipe(out);
stream.pipe(out);
});
stream.on('end', function () {
cb(error,'');
});
stream.on('end', function () {
cb(error,'');
});
stream.pipe(out);
stream.pipe(out);
});
return out;
return out;
}
//returns bytes written to file
exports.save = function(path,text,cb){
draw(text,function(error,canvas){
var fd,buf,fdAndBuf = function(){
@ -127,12 +123,26 @@ exports.save = function(path,text,cb){
};
/*
this returns an array of points that have either a 0 or 1 value representing 0 for light and 1 for dark
these values include points in the white edge of the qrcode because that edge is actually part of the spec
*/
//
//this returns an array of points that have either a 0 or 1 value representing 0 for light and 1 for dark
//these values include points in the white edge of the qrcode because that edge is actually part of the spec
//
exports.drawBitArray = function(text,cb){
drawInstance.drawBitArray(text,function(error,bits,width){
cb(error,bits,width);
});
var drawInstance = new QRCodeDraw();
drawInstance.drawBitArray(text,function(error,bits,width){
cb(error,bits,width);
});
}
exports.drawForConsole = exports.drawAscii = function(text,cb){
var drawInstance = new QRCodeDraw();
drawInstance.drawBitArray(text,function(error,bits,width){
if (!error) {
var code = termialRender.renderBits(bits,width);
cb(error,code);
} else {
cb(error,null);
}
});
}

5
test/cli.js Normal file
View file

@ -0,0 +1,5 @@
var QRCode = require(__dirname+'/../qrcode.js');
QRCode.drawAscii('yo yo yo',function(error,data){
console.log(data);
});

View file

@ -2,9 +2,8 @@
<html>
<head>
<title>client side test for node-qrcode</title>
<script src="/qrcode.js"></script>
<script src="/qrcapacitytable.js"></script>
<script src="/qrcode-draw.js"></script>
<!--[if IE]><script type="text/javascript" src="vendors/excanvas/excanvas.js"></script><![endif]-->
<script src="/build/qrcode.js"></script>
<style>.b{display:block;}</style>
</head>
<body>
@ -14,36 +13,38 @@
<textarea id="test-text" class="b"></textarea>
<small style="color:#d4d4d4;" class="b">* i did not include jquery on purpose</small>
<script>
if(!window.console) window.console = {log:function(){},warn:function(){}};
//triggered errors will throw
QRCodeDraw.errorBehavior.length = false;
var drawQR = function(text){
QRCodeDraw.draw(document.getElementById('test'),text,function(error,canvas){
if(error) {
if(window.console && window.console.warn) {
console.warn(error);
} else {
alert(error);
}
}
});
}
if(!window.console) window.console = {log:function(){},warn:function(){}};
var ta = document.getElementById('test-text'),last=0,lastTime = 1;
ta.addEventListener('keyup',function(){
var QRCodeDraw = new QRCodeLib.QRCodeDraw();
//triggered errors will throw
QRCodeDraw.errorBehavior.length = false;
var drawQR = function(text){
QRCodeDraw.draw(document.getElementById('test'),text,function(error,canvas){
if(error) {
if(window.console && window.console.warn) {
console.warn(error);
} else {
alert(error);
}
}
});
}
var l = Date.now(),z = this;
last = l;
setTimeout(function(){
//this will kinda lock the browsers event loop for a sec.
//it could have some setTimeout within processing to make it more client side friendly. or web workers...
if(l == last) {
var s=Date.now();
drawQR(z.value);
lastTime = Date.now()-s;
}
},lastTime+(lastTime/2));
},false);
var ta = document.getElementById('test-text'),last=0,lastTime = 1;
ta.addEventListener('keyup',function(){
var l = Date.now(),z = this;
last = l;
setTimeout(function(){
//this will kinda lock the browsers event loop for a sec.
//it could have some setTimeout within processing to make it more client side friendly. or web workers...
if(l == last) {
var s=Date.now();
drawQR(z.value);
lastTime = Date.now()-s;
}
},lastTime+(lastTime/2));
},false);
ta.value = 'i work client side too?';
drawQR('i work client side too?');

View file

@ -10,7 +10,7 @@ app.configure(function(){
app.use(express.methodOverride());
app.use(express.bodyParser());
app.use(app.router);
app.use(express.static(__dirname + '/../lib'));
app.use(express.static(__dirname + '/../'));
});
app.get('/', function(req, res){

View file

@ -1,4 +0,0 @@
var QRCode = require('qrcode')
, sys = require('sys');
sys.print(typeof QRCode.draw == 'function'?"PASS: qrcode is accessible in node path\n":"FAIL: qrcode failed to include\n");

View file

@ -1,6 +1,5 @@
// simple test
var utl = require('util');
var QRCode = require(__dirname+'/../qrcode');
var QRCode = require(__dirname+'/../qrcode.js');
var shouldBe = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAACMCAIAAAAhotZpAAAABmJLR0QA/wD/AP+gvaeTAAAC0UlEQVR4nO2dQW7EIAwAm6r//3J6qLSiB1Qj24TpzhxXCdlo5FiAgeu+7w85m8+n/4D8jZIAKAmAkgAoCYCSACgJgJIAKAmAkgAoCYCSACgJgJIAKAmAkgAoCYCSACgJwFfm5uu6qv7Hi7HmYmx/tRYjcu/smu73WsVIAqAkAEoCkMpJI5lvbiYHzO6N5KEIT73XiJEEQEkAlASgLCeNRL7FkW99pM+0mnu6c0xHbb2RBEBJAJQEoCUnVREZW5vlgNn1HbmqGyMJgJIAKAnA0TlpZHUcLDMXdRpGEgAlAVASgJac1J0DVmsWup/bjZEEQEkAlASgLCd11KqNZOrxMmOA3e8VwUgCoCQASgJwEce1MrmE+L5GEgAlAVASgPaclFknFLkmknsy9XKr93bkQiMJgJIAKAlA2ZrZzJrTzPc6k3sy9Xg7x/SMJABKAqAkAKmctFqHPbLav9k55nba+J6RBEBJAJQEoGzsrirHdPexqsYSd2IkAVASACUBSOWkqv3oRjLzSVW1eZH/E6EqtxlJAJQEQEkAttY4jHTUcGfmeKrqFKxxeFOUBEBJAFpqHEYy3/HVZ60+N9O+NQ7yCyUBUBKAlvmkzNhax7zUU+ttqzCSACgJgJIAbD0/qXuNUdVaokj7O/OTkQRASQCUBODofRwyfaZM/sv8n9n1zif9c5QEQEkAjj5ntmot7ez3yNqpTDtVGEkAlARASQAw58xW7bPQUR/R3Y6RBEBJAJQE4OhzZmfXz/YEyoy/VdWRd2AkAVASACUBOPpMv9X5pO7991afZT/pjVASACUBODonrY6zndDX6chPRhIAJQFQEgDMObPd9RQdz7Wf9EYoCYCSAJTtd1dF1b55kTYz65l2niVoJAFQEgAlATh6fZL8YCQBUBIAJQFQEgAlAVASACUBUBIAJQFQEgAlAVASACUBUBIAJQFQEgAlAVASACUB+AZ8GY0T5C7RkwAAAABJRU5ErkJggg==";

22
vendors/excanvas/README vendored Normal file
View file

@ -0,0 +1,22 @@
ExplorerCanvas
Copyright 2006 Google Inc.
-------------------------------------------------------------------------------
DESCRIPTION
Firefox, Safari and Opera 9 support the canvas tag to allow 2D command-based
drawing operations. ExplorerCanvas brings the same functionality to Internet
Explorer; web developers only need to include a single script tag in their
existing canvas webpages to enable this support.
-------------------------------------------------------------------------------
INSTALLATION
Include the ExplorerCanvas tag in the same directory as your HTML files, and
add the following code to your page, preferably in the <head> tag.
<!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->
If you run into trouble, please look at the included example code to see how
to best implement this

35
vendors/excanvas/excanvas.compiled.js vendored Normal file
View file

@ -0,0 +1,35 @@
// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_||
b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d<c.length;d++)this.initElement(c[d])},
initElement:function(b){if(!b.getContext){b.getContext=X;b.innerHTML="";b.attachEvent("onpropertychange",Z);b.attachEvent("onresize",$);var a=b.attributes;if(a.width&&a.width.specified)b.style.width=a.width.nodeValue+"px";else b.width=b.clientWidth;if(a.height&&a.height.specified)b.style.height=a.height.nodeValue+"px";else b.height=b.clientHeight}return b}};function Z(b){var a=b.srcElement;switch(b.propertyName){case "width":a.style.width=a.attributes.width.nodeValue+"px";a.getContext().clearRect();
break;case "height":a.style.height=a.attributes.height.nodeValue+"px";a.getContext().clearRect();break}}function $(b){var a=b.srcElement;if(a.firstChild){a.firstChild.style.width=a.clientWidth+"px";a.firstChild.style.height=a.clientHeight+"px"}}M.init();var N=[],B=0;for(;B<16;B++){var C=0;for(;C<16;C++)N[B*16+C]=B.toString(16)+C.toString(16)}function I(){return[[1,0,0],[0,1,0],[0,0,1]]}function y(b,a){var c=I(),d=0;for(;d<3;d++){var f=0;for(;f<3;f++){var h=0,g=0;for(;g<3;g++)h+=b[d][g]*a[g][f];c[d][f]=
h}}return c}function O(b,a){a.fillStyle=b.fillStyle;a.lineCap=b.lineCap;a.lineJoin=b.lineJoin;a.lineWidth=b.lineWidth;a.miterLimit=b.miterLimit;a.shadowBlur=b.shadowBlur;a.shadowColor=b.shadowColor;a.shadowOffsetX=b.shadowOffsetX;a.shadowOffsetY=b.shadowOffsetY;a.strokeStyle=b.strokeStyle;a.globalAlpha=b.globalAlpha;a.arcScaleX_=b.arcScaleX_;a.arcScaleY_=b.arcScaleY_;a.lineScale_=b.lineScale_}function P(b){var a,c=1;b=String(b);if(b.substring(0,3)=="rgb"){var d=b.indexOf("(",3),f=b.indexOf(")",d+
1),h=b.substring(d+1,f).split(",");a="#";var g=0;for(;g<3;g++)a+=N[Number(h[g])];if(h.length==4&&b.substr(3,1)=="a")c=h[3]}else a=b;return{color:a,alpha:c}}function aa(b){switch(b){case "butt":return"flat";case "round":return"round";case "square":default:return"square"}}function H(b){this.m_=I();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=k*1;this.globalAlpha=1;this.canvas=b;
var a=b.ownerDocument.createElement("div");a.style.width=b.clientWidth+"px";a.style.height=b.clientHeight+"px";a.style.overflow="hidden";a.style.position="absolute";b.appendChild(a);this.element_=a;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}var i=H.prototype;i.clearRect=function(){this.element_.innerHTML=""};i.beginPath=function(){this.currentPath_=[]};i.moveTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};
i.lineTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};i.bezierCurveTo=function(b,a,c,d,f,h){var g=this.getCoords_(f,h),l=this.getCoords_(b,a),e=this.getCoords_(c,d);Q(this,l,e,g)};function Q(b,a,c,d){b.currentPath_.push({type:"bezierCurveTo",cp1x:a.x,cp1y:a.y,cp2x:c.x,cp2y:c.y,x:d.x,y:d.y});b.currentX_=d.x;b.currentY_=d.y}i.quadraticCurveTo=function(b,a,c,d){var f=this.getCoords_(b,a),h=this.getCoords_(c,d),g={x:this.currentX_+
0.6666666666666666*(f.x-this.currentX_),y:this.currentY_+0.6666666666666666*(f.y-this.currentY_)};Q(this,g,{x:g.x+(h.x-this.currentX_)/3,y:g.y+(h.y-this.currentY_)/3},h)};i.arc=function(b,a,c,d,f,h){c*=k;var g=h?"at":"wa",l=b+G(d)*c-v,e=a+F(d)*c-v,m=b+G(f)*c-v,r=a+F(f)*c-v;if(l==m&&!h)l+=0.125;var n=this.getCoords_(b,a),o=this.getCoords_(l,e),q=this.getCoords_(m,r);this.currentPath_.push({type:g,x:n.x,y:n.y,radius:c,xStart:o.x,yStart:o.y,xEnd:q.x,yEnd:q.y})};i.rect=function(b,a,c,d){this.moveTo(b,
a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath()};i.strokeRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.stroke();this.currentPath_=f};i.fillRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.fill();this.currentPath_=f};i.createLinearGradient=function(b,
a,c,d){var f=new D("gradient");f.x0_=b;f.y0_=a;f.x1_=c;f.y1_=d;return f};i.createRadialGradient=function(b,a,c,d,f,h){var g=new D("gradientradial");g.x0_=b;g.y0_=a;g.r0_=c;g.x1_=d;g.y1_=f;g.r1_=h;return g};i.drawImage=function(b){var a,c,d,f,h,g,l,e,m=b.runtimeStyle.width,r=b.runtimeStyle.height;b.runtimeStyle.width="auto";b.runtimeStyle.height="auto";var n=b.width,o=b.height;b.runtimeStyle.width=m;b.runtimeStyle.height=r;if(arguments.length==3){a=arguments[1];c=arguments[2];h=g=0;l=d=n;e=f=o}else if(arguments.length==
5){a=arguments[1];c=arguments[2];d=arguments[3];f=arguments[4];h=g=0;l=n;e=o}else if(arguments.length==9){h=arguments[1];g=arguments[2];l=arguments[3];e=arguments[4];a=arguments[5];c=arguments[6];d=arguments[7];f=arguments[8]}else throw Error("Invalid number of arguments");var q=this.getCoords_(a,c),t=[];t.push(" <g_vml_:group",' coordsize="',k*10,",",k*10,'"',' coordorigin="0,0"',' style="width:',10,"px;height:",10,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]){var E=[];E.push("M11=",
this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",j(q.x/k),",","Dy=",j(q.y/k),"");var p=q,z=this.getCoords_(a+d,c),w=this.getCoords_(a,c+f),x=this.getCoords_(a+d,c+f);p.x=s.max(p.x,z.x,w.x,x.x);p.y=s.max(p.y,z.y,w.y,x.y);t.push("padding:0 ",j(p.x/k),"px ",j(p.y/k),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",E.join(""),", sizingmethod='clip');")}else t.push("top:",j(q.y/k),"px;left:",j(q.x/k),"px;");t.push(' ">','<g_vml_:image src="',b.src,
'"',' style="width:',k*d,"px;"," height:",k*f,'px;"',' cropleft="',h/n,'"',' croptop="',g/o,'"',' cropright="',(n-h-l)/n,'"',' cropbottom="',(o-g-e)/o,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("<g_vml_:shape",' filled="',!!b,'"',' style="position:absolute;width:',10,"px;height:",10,'px;"',' coordorigin="0 0" coordsize="',k*10," ",k*10,'"',' stroked="',
!b,'"',' path="');var h={x:null,y:null},g={x:null,y:null},l=0;for(;l<this.currentPath_.length;l++){var e=this.currentPath_[l];switch(e.type){case "moveTo":a.push(" m ",j(e.x),",",j(e.y));break;case "lineTo":a.push(" l ",j(e.x),",",j(e.y));break;case "close":a.push(" x ");e=null;break;case "bezierCurveTo":a.push(" c ",j(e.cp1x),",",j(e.cp1y),",",j(e.cp2x),",",j(e.cp2y),",",j(e.x),",",j(e.y));break;case "at":case "wa":a.push(" ",e.type," ",j(e.x-this.arcScaleX_*e.radius),",",j(e.y-this.arcScaleY_*e.radius),
" ",j(e.x+this.arcScaleX_*e.radius),",",j(e.y+this.arcScaleY_*e.radius)," ",j(e.xStart),",",j(e.yStart)," ",j(e.xEnd),",",j(e.yEnd));break}if(e){if(h.x==null||e.x<h.x)h.x=e.x;if(g.x==null||e.x>g.x)g.x=e.x;if(h.y==null||e.y<h.y)h.y=e.y;if(g.y==null||e.y>g.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_),
z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l<J;l++){var T=u[l];S.push(T.offset*q+
o+" "+T.color)}a.push('<g_vml_:fill type="',m.type_,'"',' method="none" focus="100%"',' color="',da,'"',' color2="',ea,'"',' colors="',S.join(","),'"',' opacity="',ga,'"',' g_o_:opacity2="',fa,'"',' angle="',r,'"',' focusposition="',n.x,",",n.y,'" />')}else a.push('<g_vml_:fill color="',d,'" opacity="',f,'" />');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("<g_vml_:stroke",' opacity="',f,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',aa(this.lineCap),
'"',' weight="',K,'px"',' color="',d,'" />')}a.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(),
this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a,
0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager=
M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})();

924
vendors/excanvas/excanvas.js vendored Normal file
View file

@ -0,0 +1,924 @@
// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Known Issues:
//
// * Patterns are not implemented.
// * Radial gradient are not implemented. The VML version of these look very
// different from the canvas one.
// * Clipping paths are not implemented.
// * Coordsize. The width and height attribute have higher priority than the
// width and height style values which isn't correct.
// * Painting mode isn't implemented.
// * Canvas width/height should is using content-box by default. IE in
// Quirks mode will draw the canvas using border-box. Either change your
// doctype to HTML5
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
// or use Box Sizing Behavior from WebFX
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
// * Non uniform scaling does not correctly scale strokes.
// * Optimize. There is always room for speed improvements.
// Only add this code if we do not already have a canvas implementation
if (!document.createElement('canvas').getContext) {
(function() {
// alias some functions to make (compiled) code shorter
var m = Math;
var mr = m.round;
var ms = m.sin;
var mc = m.cos;
var abs = m.abs;
var sqrt = m.sqrt;
// this is used for sub pixel precision
var Z = 10;
var Z2 = Z / 2;
/**
* This funtion is assigned to the <canvas> elements as element.getContext().
* @this {HTMLElement}
* @return {CanvasRenderingContext2D_}
*/
function getContext() {
return this.context_ ||
(this.context_ = new CanvasRenderingContext2D_(this));
}
var slice = Array.prototype.slice;
/**
* Binds a function to an object. The returned function will always use the
* passed in {@code obj} as {@code this}.
*
* Example:
*
* g = bind(f, obj, a, b)
* g(c, d) // will do f.call(obj, a, b, c, d)
*
* @param {Function} f The function to bind the object to
* @param {Object} obj The object that should act as this when the function
* is called
* @param {*} var_args Rest arguments that will be used as the initial
* arguments when the function is called
* @return {Function} A new function that has bound this
*/
function bind(f, obj, var_args) {
var a = slice.call(arguments, 2);
return function() {
return f.apply(obj, a.concat(slice.call(arguments)));
};
}
var G_vmlCanvasManager_ = {
init: function(opt_doc) {
if (/MSIE/.test(navigator.userAgent) && !window.opera) {
var doc = opt_doc || document;
// Create a dummy element so that IE will allow canvas elements to be
// recognized.
doc.createElement('canvas');
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
}
},
init_: function(doc) {
// create xmlns
if (!doc.namespaces['g_vml_']) {
doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',
'#default#VML');
}
if (!doc.namespaces['g_o_']) {
doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',
'#default#VML');
}
// Setup default CSS. Only add one style sheet per document
if (!doc.styleSheets['ex_canvas_']) {
var ss = doc.createStyleSheet();
ss.owningElement.id = 'ex_canvas_';
ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
// default size is 300x150 in Gecko and Opera
'text-align:left;width:300px;height:150px}' +
'g_vml_\\:*{behavior:url(#default#VML)}' +
'g_o_\\:*{behavior:url(#default#VML)}';
}
// find all canvas elements
var els = doc.getElementsByTagName('canvas');
for (var i = 0; i < els.length; i++) {
this.initElement(els[i]);
}
},
/**
* Public initializes a canvas element so that it can be used as canvas
* element from now on. This is called automatically before the page is
* loaded but if you are creating elements using createElement you need to
* make sure this is called on the element.
* @param {HTMLElement} el The canvas element to initialize.
* @return {HTMLElement} the element that was created.
*/
initElement: function(el) {
if (!el.getContext) {
el.getContext = getContext;
// Remove fallback content. There is no way to hide text nodes so we
// just remove all childNodes. We could hide all elements and remove
// text nodes but who really cares about the fallback content.
el.innerHTML = '';
// do not use inline function because that will leak memory
el.attachEvent('onpropertychange', onPropertyChange);
el.attachEvent('onresize', onResize);
var attrs = el.attributes;
if (attrs.width && attrs.width.specified) {
// TODO: use runtimeStyle and coordsize
// el.getContext().setWidth_(attrs.width.nodeValue);
el.style.width = attrs.width.nodeValue + 'px';
} else {
el.width = el.clientWidth;
}
if (attrs.height && attrs.height.specified) {
// TODO: use runtimeStyle and coordsize
// el.getContext().setHeight_(attrs.height.nodeValue);
el.style.height = attrs.height.nodeValue + 'px';
} else {
el.height = el.clientHeight;
}
//el.getContext().setCoordsize_()
}
return el;
}
};
function onPropertyChange(e) {
var el = e.srcElement;
switch (e.propertyName) {
case 'width':
el.style.width = el.attributes.width.nodeValue + 'px';
el.getContext().clearRect();
break;
case 'height':
el.style.height = el.attributes.height.nodeValue + 'px';
el.getContext().clearRect();
break;
}
}
function onResize(e) {
var el = e.srcElement;
if (el.firstChild) {
el.firstChild.style.width = el.clientWidth + 'px';
el.firstChild.style.height = el.clientHeight + 'px';
}
}
G_vmlCanvasManager_.init();
// precompute "00" to "FF"
var dec2hex = [];
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 16; j++) {
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
}
}
function createMatrixIdentity() {
return [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
}
function matrixMultiply(m1, m2) {
var result = createMatrixIdentity();
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
var sum = 0;
for (var z = 0; z < 3; z++) {
sum += m1[x][z] * m2[z][y];
}
result[x][y] = sum;
}
}
return result;
}
function copyState(o1, o2) {
o2.fillStyle = o1.fillStyle;
o2.lineCap = o1.lineCap;
o2.lineJoin = o1.lineJoin;
o2.lineWidth = o1.lineWidth;
o2.miterLimit = o1.miterLimit;
o2.shadowBlur = o1.shadowBlur;
o2.shadowColor = o1.shadowColor;
o2.shadowOffsetX = o1.shadowOffsetX;
o2.shadowOffsetY = o1.shadowOffsetY;
o2.strokeStyle = o1.strokeStyle;
o2.globalAlpha = o1.globalAlpha;
o2.arcScaleX_ = o1.arcScaleX_;
o2.arcScaleY_ = o1.arcScaleY_;
o2.lineScale_ = o1.lineScale_;
}
function processStyle(styleString) {
var str, alpha = 1;
styleString = String(styleString);
if (styleString.substring(0, 3) == 'rgb') {
var start = styleString.indexOf('(', 3);
var end = styleString.indexOf(')', start + 1);
var guts = styleString.substring(start + 1, end).split(',');
str = '#';
for (var i = 0; i < 3; i++) {
str += dec2hex[Number(guts[i])];
}
if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
alpha = guts[3];
}
} else {
str = styleString;
}
return {color: str, alpha: alpha};
}
function processLineCap(lineCap) {
switch (lineCap) {
case 'butt':
return 'flat';
case 'round':
return 'round';
case 'square':
default:
return 'square';
}
}
/**
* This class implements CanvasRenderingContext2D interface as described by
* the WHATWG.
* @param {HTMLElement} surfaceElement The element that the 2D context should
* be associated with
*/
function CanvasRenderingContext2D_(surfaceElement) {
this.m_ = createMatrixIdentity();
this.mStack_ = [];
this.aStack_ = [];
this.currentPath_ = [];
// Canvas context properties
this.strokeStyle = '#000';
this.fillStyle = '#000';
this.lineWidth = 1;
this.lineJoin = 'miter';
this.lineCap = 'butt';
this.miterLimit = Z * 1;
this.globalAlpha = 1;
this.canvas = surfaceElement;
var el = surfaceElement.ownerDocument.createElement('div');
el.style.width = surfaceElement.clientWidth + 'px';
el.style.height = surfaceElement.clientHeight + 'px';
el.style.overflow = 'hidden';
el.style.position = 'absolute';
surfaceElement.appendChild(el);
this.element_ = el;
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
this.lineScale_ = 1;
}
var contextPrototype = CanvasRenderingContext2D_.prototype;
contextPrototype.clearRect = function() {
this.element_.innerHTML = '';
};
contextPrototype.beginPath = function() {
// TODO: Branch current matrix so that save/restore has no effect
// as per safari docs.
this.currentPath_ = [];
};
contextPrototype.moveTo = function(aX, aY) {
var p = this.getCoords_(aX, aY);
this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
this.currentX_ = p.x;
this.currentY_ = p.y;
};
contextPrototype.lineTo = function(aX, aY) {
var p = this.getCoords_(aX, aY);
this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
this.currentX_ = p.x;
this.currentY_ = p.y;
};
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
aCP2x, aCP2y,
aX, aY) {
var p = this.getCoords_(aX, aY);
var cp1 = this.getCoords_(aCP1x, aCP1y);
var cp2 = this.getCoords_(aCP2x, aCP2y);
bezierCurveTo(this, cp1, cp2, p);
};
// Helper function that takes the already fixed cordinates.
function bezierCurveTo(self, cp1, cp2, p) {
self.currentPath_.push({
type: 'bezierCurveTo',
cp1x: cp1.x,
cp1y: cp1.y,
cp2x: cp2.x,
cp2y: cp2.y,
x: p.x,
y: p.y
});
self.currentX_ = p.x;
self.currentY_ = p.y;
}
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
// the following is lifted almost directly from
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
var cp = this.getCoords_(aCPx, aCPy);
var p = this.getCoords_(aX, aY);
var cp1 = {
x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
};
var cp2 = {
x: cp1.x + (p.x - this.currentX_) / 3.0,
y: cp1.y + (p.y - this.currentY_) / 3.0
};
bezierCurveTo(this, cp1, cp2, p);
};
contextPrototype.arc = function(aX, aY, aRadius,
aStartAngle, aEndAngle, aClockwise) {
aRadius *= Z;
var arcType = aClockwise ? 'at' : 'wa';
var xStart = aX + mc(aStartAngle) * aRadius - Z2;
var yStart = aY + ms(aStartAngle) * aRadius - Z2;
var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
// IE won't render arches drawn counter clockwise if xStart == xEnd.
if (xStart == xEnd && !aClockwise) {
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
// that can be represented in binary
}
var p = this.getCoords_(aX, aY);
var pStart = this.getCoords_(xStart, yStart);
var pEnd = this.getCoords_(xEnd, yEnd);
this.currentPath_.push({type: arcType,
x: p.x,
y: p.y,
radius: aRadius,
xStart: pStart.x,
yStart: pStart.y,
xEnd: pEnd.x,
yEnd: pEnd.y});
};
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
};
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
var oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
this.currentPath_ = oldPath;
};
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
var oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.fill();
this.currentPath_ = oldPath;
};
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
var gradient = new CanvasGradient_('gradient');
gradient.x0_ = aX0;
gradient.y0_ = aY0;
gradient.x1_ = aX1;
gradient.y1_ = aY1;
return gradient;
};
contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
aX1, aY1, aR1) {
var gradient = new CanvasGradient_('gradientradial');
gradient.x0_ = aX0;
gradient.y0_ = aY0;
gradient.r0_ = aR0;
gradient.x1_ = aX1;
gradient.y1_ = aY1;
gradient.r1_ = aR1;
return gradient;
};
contextPrototype.drawImage = function(image, var_args) {
var dx, dy, dw, dh, sx, sy, sw, sh;
// to find the original width we overide the width and height
var oldRuntimeWidth = image.runtimeStyle.width;
var oldRuntimeHeight = image.runtimeStyle.height;
image.runtimeStyle.width = 'auto';
image.runtimeStyle.height = 'auto';
// get the original size
var w = image.width;
var h = image.height;
// and remove overides
image.runtimeStyle.width = oldRuntimeWidth;
image.runtimeStyle.height = oldRuntimeHeight;
if (arguments.length == 3) {
dx = arguments[1];
dy = arguments[2];
sx = sy = 0;
sw = dw = w;
sh = dh = h;
} else if (arguments.length == 5) {
dx = arguments[1];
dy = arguments[2];
dw = arguments[3];
dh = arguments[4];
sx = sy = 0;
sw = w;
sh = h;
} else if (arguments.length == 9) {
sx = arguments[1];
sy = arguments[2];
sw = arguments[3];
sh = arguments[4];
dx = arguments[5];
dy = arguments[6];
dw = arguments[7];
dh = arguments[8];
} else {
throw Error('Invalid number of arguments');
}
var d = this.getCoords_(dx, dy);
var w2 = sw / 2;
var h2 = sh / 2;
var vmlStr = [];
var W = 10;
var H = 10;
// For some reason that I've now forgotten, using divs didn't work
vmlStr.push(' <g_vml_:group',
' coordsize="', Z * W, ',', Z * H, '"',
' coordorigin="0,0"' ,
' style="width:', W, 'px;height:', H, 'px;position:absolute;');
// If filters are necessary (rotation exists), create them
// filters are bog-slow, so only create them if abbsolutely necessary
// The following check doesn't account for skews (which don't exist
// in the canvas spec (yet) anyway.
if (this.m_[0][0] != 1 || this.m_[0][1]) {
var filter = [];
// Note the 12/21 reversal
filter.push('M11=', this.m_[0][0], ',',
'M12=', this.m_[1][0], ',',
'M21=', this.m_[0][1], ',',
'M22=', this.m_[1][1], ',',
'Dx=', mr(d.x / Z), ',',
'Dy=', mr(d.y / Z), '');
// Bounding box calculation (need to minimize displayed area so that
// filters don't waste time on unused pixels.
var max = d;
var c2 = this.getCoords_(dx + dw, dy);
var c3 = this.getCoords_(dx, dy + dh);
var c4 = this.getCoords_(dx + dw, dy + dh);
max.x = m.max(max.x, c2.x, c3.x, c4.x);
max.y = m.max(max.y, c2.y, c3.y, c4.y);
vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
filter.join(''), ", sizingmethod='clip');")
} else {
vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
}
vmlStr.push(' ">' ,
'<g_vml_:image src="', image.src, '"',
' style="width:', Z * dw, 'px;',
' height:', Z * dh, 'px;"',
' cropleft="', sx / w, '"',
' croptop="', sy / h, '"',
' cropright="', (w - sx - sw) / w, '"',
' cropbottom="', (h - sy - sh) / h, '"',
' />',
'</g_vml_:group>');
this.element_.insertAdjacentHTML('BeforeEnd',
vmlStr.join(''));
};
contextPrototype.stroke = function(aFill) {
var lineStr = [];
var lineOpen = false;
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
var color = a.color;
var opacity = a.alpha * this.globalAlpha;
var W = 10;
var H = 10;
lineStr.push('<g_vml_:shape',
' filled="', !!aFill, '"',
' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
' stroked="', !aFill, '"',
' path="');
var newSeq = false;
var min = {x: null, y: null};
var max = {x: null, y: null};
for (var i = 0; i < this.currentPath_.length; i++) {
var p = this.currentPath_[i];
var c;
switch (p.type) {
case 'moveTo':
c = p;
lineStr.push(' m ', mr(p.x), ',', mr(p.y));
break;
case 'lineTo':
lineStr.push(' l ', mr(p.x), ',', mr(p.y));
break;
case 'close':
lineStr.push(' x ');
p = null;
break;
case 'bezierCurveTo':
lineStr.push(' c ',
mr(p.cp1x), ',', mr(p.cp1y), ',',
mr(p.cp2x), ',', mr(p.cp2y), ',',
mr(p.x), ',', mr(p.y));
break;
case 'at':
case 'wa':
lineStr.push(' ', p.type, ' ',
mr(p.x - this.arcScaleX_ * p.radius), ',',
mr(p.y - this.arcScaleY_ * p.radius), ' ',
mr(p.x + this.arcScaleX_ * p.radius), ',',
mr(p.y + this.arcScaleY_ * p.radius), ' ',
mr(p.xStart), ',', mr(p.yStart), ' ',
mr(p.xEnd), ',', mr(p.yEnd));
break;
}
// TODO: Following is broken for curves due to
// move to proper paths.
// Figure out dimensions so we can do gradient fills
// properly
if (p) {
if (min.x == null || p.x < min.x) {
min.x = p.x;
}
if (max.x == null || p.x > max.x) {
max.x = p.x;
}
if (min.y == null || p.y < min.y) {
min.y = p.y;
}
if (max.y == null || p.y > max.y) {
max.y = p.y;
}
}
}
lineStr.push(' ">');
if (!aFill) {
var lineWidth = this.lineScale_ * this.lineWidth;
// VML cannot correctly render a line if the width is less than 1px.
// In that case, we dilute the color to make the line look thinner.
if (lineWidth < 1) {
opacity *= lineWidth;
}
lineStr.push(
'<g_vml_:stroke',
' opacity="', opacity, '"',
' joinstyle="', this.lineJoin, '"',
' miterlimit="', this.miterLimit, '"',
' endcap="', processLineCap(this.lineCap), '"',
' weight="', lineWidth, 'px"',
' color="', color, '" />'
);
} else if (typeof this.fillStyle == 'object') {
var fillStyle = this.fillStyle;
var angle = 0;
var focus = {x: 0, y: 0};
// additional offset
var shift = 0;
// scale factor for offset
var expansion = 1;
if (fillStyle.type_ == 'gradient') {
var x0 = fillStyle.x0_ / this.arcScaleX_;
var y0 = fillStyle.y0_ / this.arcScaleY_;
var x1 = fillStyle.x1_ / this.arcScaleX_;
var y1 = fillStyle.y1_ / this.arcScaleY_;
var p0 = this.getCoords_(x0, y0);
var p1 = this.getCoords_(x1, y1);
var dx = p1.x - p0.x;
var dy = p1.y - p0.y;
angle = Math.atan2(dx, dy) * 180 / Math.PI;
// The angle should be a non-negative number.
if (angle < 0) {
angle += 360;
}
// Very small angles produce an unexpected result because they are
// converted to a scientific notation string.
if (angle < 1e-6) {
angle = 0;
}
} else {
var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);
var width = max.x - min.x;
var height = max.y - min.y;
focus = {
x: (p0.x - min.x) / width,
y: (p0.y - min.y) / height
};
width /= this.arcScaleX_ * Z;
height /= this.arcScaleY_ * Z;
var dimension = m.max(width, height);
shift = 2 * fillStyle.r0_ / dimension;
expansion = 2 * fillStyle.r1_ / dimension - shift;
}
// We need to sort the color stops in ascending order by offset,
// otherwise IE won't interpret it correctly.
var stops = fillStyle.colors_;
stops.sort(function(cs1, cs2) {
return cs1.offset - cs2.offset;
});
var length = stops.length;
var color1 = stops[0].color;
var color2 = stops[length - 1].color;
var opacity1 = stops[0].alpha * this.globalAlpha;
var opacity2 = stops[length - 1].alpha * this.globalAlpha;
var colors = [];
for (var i = 0; i < length; i++) {
var stop = stops[i];
colors.push(stop.offset * expansion + shift + ' ' + stop.color);
}
// When colors attribute is used, the meanings of opacity and o:opacity2
// are reversed.
lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
' method="none" focus="100%"',
' color="', color1, '"',
' color2="', color2, '"',
' colors="', colors.join(','), '"',
' opacity="', opacity2, '"',
' g_o_:opacity2="', opacity1, '"',
' angle="', angle, '"',
' focusposition="', focus.x, ',', focus.y, '" />');
} else {
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
'" />');
}
lineStr.push('</g_vml_:shape>');
this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
};
contextPrototype.fill = function() {
this.stroke(true);
}
contextPrototype.closePath = function() {
this.currentPath_.push({type: 'close'});
};
/**
* @private
*/
contextPrototype.getCoords_ = function(aX, aY) {
var m = this.m_;
return {
x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
}
};
contextPrototype.save = function() {
var o = {};
copyState(this, o);
this.aStack_.push(o);
this.mStack_.push(this.m_);
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
};
contextPrototype.restore = function() {
copyState(this.aStack_.pop(), this);
this.m_ = this.mStack_.pop();
};
function matrixIsFinite(m) {
for (var j = 0; j < 3; j++) {
for (var k = 0; k < 2; k++) {
if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
return false;
}
}
}
return true;
}
function setM(ctx, m, updateLineScale) {
if (!matrixIsFinite(m)) {
return;
}
ctx.m_ = m;
if (updateLineScale) {
// Get the line scale.
// Determinant of this.m_ means how much the area is enlarged by the
// transformation. So its square root can be used as a scale factor
// for width.
var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
ctx.lineScale_ = sqrt(abs(det));
}
}
contextPrototype.translate = function(aX, aY) {
var m1 = [
[1, 0, 0],
[0, 1, 0],
[aX, aY, 1]
];
setM(this, matrixMultiply(m1, this.m_), false);
};
contextPrototype.rotate = function(aRot) {
var c = mc(aRot);
var s = ms(aRot);
var m1 = [
[c, s, 0],
[-s, c, 0],
[0, 0, 1]
];
setM(this, matrixMultiply(m1, this.m_), false);
};
contextPrototype.scale = function(aX, aY) {
this.arcScaleX_ *= aX;
this.arcScaleY_ *= aY;
var m1 = [
[aX, 0, 0],
[0, aY, 0],
[0, 0, 1]
];
setM(this, matrixMultiply(m1, this.m_), true);
};
contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
var m1 = [
[m11, m12, 0],
[m21, m22, 0],
[dx, dy, 1]
];
setM(this, matrixMultiply(m1, this.m_), true);
};
contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
var m = [
[m11, m12, 0],
[m21, m22, 0],
[dx, dy, 1]
];
setM(this, m, true);
};
/******** STUBS ********/
contextPrototype.clip = function() {
// TODO: Implement
};
contextPrototype.arcTo = function() {
// TODO: Implement
};
contextPrototype.createPattern = function() {
return new CanvasPattern_;
};
// Gradient / Pattern Stubs
function CanvasGradient_(aType) {
this.type_ = aType;
this.x0_ = 0;
this.y0_ = 0;
this.r0_ = 0;
this.x1_ = 0;
this.y1_ = 0;
this.r1_ = 0;
this.colors_ = [];
}
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
aColor = processStyle(aColor);
this.colors_.push({offset: aOffset,
color: aColor.color,
alpha: aColor.alpha});
};
function CanvasPattern_() {}
// set up externs
G_vmlCanvasManager = G_vmlCanvasManager_;
CanvasRenderingContext2D = CanvasRenderingContext2D_;
CanvasGradient = CanvasGradient_;
CanvasPattern = CanvasPattern_;
})();
} // if