alasql
Version:
AlaSQL.js - JavaScript SQL database library for relational and graph data manipulation with support of localStorage, IndexedDB, and Excel
761 lines (674 loc) • 22.6 kB
JavaScript
/*
//
// Utilities for Alasql.js
// Date: 03.11.2014
// (c) 2014, Andrey Gershun
//
*/
/**
Alasql utility functions
*/
var utils = alasql.utils = {};
/**
Return true.
Stub for non-ecisting WHERE clause, because is faster then if(whenrfn) whenfn()
@return {boolean} true
*/
function returnTrue () {return true};
/**
Return undefined
Stub for non-ecisting WHERE clause, because is faster then if(whenrfn) whenfn()
@return undefined
*/
function returnUndefined() {};
/**
Escape quotes
@param {string} s Source string
@return {string} Escaped string
*/
var escapeq = utils.escapeq = function(s) {
// console.log(s);
return s.replace(/\'/g,'\\\'');
}
/**
Double quotes
@param {string} s Source string
@return {string} Escaped string
*/
var escapeqq = utils.undoubleq = function(s) {
return s.replace(/(\')/g,'\'\'');
}
/**
Replace double quotes
@param {string} s Source string
@return {string} Replaced string
*/
var doubleq = utils.doubleq = function(s) {
return s.replace(/(\'\')/g,'\\\'');
}
/**
Replace sigle quote to escaped single quote
@param {string} s Source string
@return {string} Replaced string
*/
var doubleqq = utils.doubleqq = function(s) {
return s.replace(/\'/g,"\'");
}
var cutbom = function(s) {
if(s[0] == String.fromCharCode(65279)) s = s.substr(1);
return s;
};
/**
Load text file from anywhere
@param {string} path File path
@param {boolean} asy True - async call, false - sync call
@param {function} success Success function
@param {function} error Error function
*/
var loadFile = utils.loadFile = function(path, asy, success, error) {
if(typeof exports == 'object') {
// For Node.js
var fs = require('fs');
// console.log(36,path);
// console.log(typeof path);
if(!path) {
var buff = '';
process.stdin.setEncoding('utf8');
process.stdin.on('readable', function() {
var chunk = process.stdin.read();
if (chunk !== null) {
buff += chunk.toString();
}
});
process.stdin.on('end', function() {
success(cutbom(buff));
});
} else {
// var data = fs.readFileSync(path);
// success(data.toString());
if(asy) {
fs.readFile(path,function(err,data){
if(err) {
throw err;
}
success(cutbom(data.toString()));
});
} else {
var data = fs.readFileSync(path);
success(cutbom(data.toString()));
}
}
} else if(typeof cordova == 'object') {
// console.log('CORDOVA'+path);
// console.log(cordova);
// console.log('CORDOVA'+path);
// Cordova
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
fileSystem.root.getFile(path, {create:false}, function (fileEntry) {
// var reader = new FileReader();
// // console.log('READ FILE 2');
// reader.onloadend = function(e) {
// // console.log('READ FILE 3',this.result);
// success(this.result);
// };
// reader.readAsText(file);
fileEntry.file(function(file){
var fileReader = new FileReader();
fileReader.onloadend = function(e){
success(cutbom(this.result));
};
fileReader.readAsText(file);
});
// });
});
});
/*
var paths = path.split('/');
var filename = paths[paths.length-1];
var dirpath = path.substr(0,path.length-filename.length);
// console.log('CORDOVA',filename,dirpath);
//return success('[{"a":"'+filename+'"}]');
window.resolveLocalFileSystemURL(dirpath, function(dir) {
dir.getFile(filename, null, function(file) {
file.file(function(file) {
var reader = new FileReader();
// console.log('READ FILE 2');
reader.onloadend = function(e) {
// console.log('READ FILE 3',this.result);
success(this.result);
};
reader.readAsText(file);
});
});
});
*/
} else {
// if(typeof path == "string") {
// }
if(typeof path == "string") {
// For browser read from tag
if((path.substr(0,1) == '#') && (typeof document != 'undefined')) {
var data = document.querySelector(path).textContent;
success(data);
} else {
// For browser
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
if (success)
success(cutbom(xhr.responseText));
} else {
if (error)
error(xhr);
}
}
};
xhr.open("GET", path, asy); // Async
xhr.send();
}
} else if(path instanceof Event) {
// console.log("event");
var files = path.target.files;
var reader = new FileReader();
var name = files[0].name;
reader.onload = function(e) {
var data = e.target.result;
success(cutbom(data));
};
reader.readAsText(files[0]);
}
}
};
/**
Load binary file from anywhere
@param {string} path File path
@param {boolean} asy True - async call, false - sync call
@param {function} success Success function
@param {function} error Error function
@return 1 for Async, data - for sync version
*/
var loadBinaryFile = utils.loadBinaryFile = function(path, asy, success, error) {
if(typeof exports == 'object') {
// For Node.js
var fs = require('fs');
if(asy) {
fs.readFile(path,function(err,data){
if(err) {
throw err;
}
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
success(arr.join(""));
});
} else {
var data = fs.readFileSync(path);
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
success(arr.join(""));
}
// success(data);
} else {
if(typeof path == "string") {
// For browser
var xhr = new XMLHttpRequest();
xhr.open("GET", path, asy); // Async
xhr.responseType = "arraybuffer";
xhr.onload = function() {
var data = new Uint8Array(xhr.response);
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
success(arr.join(""));
};
xhr.send();
} else if(path instanceof Event) {
// console.log("event");
var files = path.target.files;
var reader = new FileReader();
var name = files[0].name;
reader.onload = function(e) {
var data = e.target.result;
success(data);
};
reader.readAsBinaryString(files[0]);
}
};
};
var removeFile = utils.removeFile = function(path,cb) {
if(typeof exports == 'object') {
var fs = require('fs');
fs.remove(path,cb);
} else if(typeof cordova == 'object') {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
fileSystem.root.getFile(path, {create:false}, function (fileEntry) {
fileEntry.remove(cb);
if(cb) cb();
}, function(){
if(cb) cb();
});
});
} else {
throw new Error('You can remove files only in Node.js and Apache Cordova');
};
};
var deleteFile = utils.deleteFile = function(path,cb){
if(typeof exports == 'object') {
var fs = require('fs');
fs.unlink(path, cb);
};
};
var fileExists = utils.fileExists = function(path,cb){
if(typeof exports == 'object') {
var fs = require('fs');
fs.exists(path,cb);
} else if(typeof cordova == 'object') {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
fileSystem.root.getFile(path, {create:false}, function (fileEntry) {
cb(true);
}, function(){
cb(false);
});
});
/*
function fail(){
callback(false);
}
try {
// Cordova
var paths = path.split('/');
var filename = paths[paths.length-1];
var dirpath = path.substr(0,path.length-filename.length);
window.resolveLocalFileSystemURL(dirpath, function(dir) {
dir.getFile(filename, null, function(file) {
file.file(function(file) {
callback(true);
},fail);
},fail);
},fail);
} catch(err) {
fail();
};
*/
} else {
// TODO Cordova, etc.
throw new Error('You can use exists() only in Node.js or Apach Cordova');
}
};
/**
Save text file from anywhere
@param {string} path File path
@param {array of objects} data Data object
@param {function} cb Callback
*/
var saveFile = utils.saveFile = function(path, data, cb) {
var res = 1;
if(typeof path == 'undefined') {
//
// Return data into result variable
// like: alasql('SELECT * INTO TXT() FROM ?',[data]);
//
res = data;
if(cb) res = cb(res);
} else {
if(typeof exports == 'object') {
// For Node.js
var fs = require('fs');
var data = fs.writeFileSync(path,data);
if(cb) res = cb(res);
} else if(typeof cordova == 'object') {
// For Apache Cordova
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
// alasql.utils.removeFile(path,function(){
fileSystem.root.getFile(path, {create:true}, function (fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function(){
if(cb) res = cb(res);
};
fileWriter.write(data);
});
});
// });
});
/*
} else if((typeof cordova == 'object') && cordova.file) {
// console.log('saveFile 1');
// Cordova
var paths = path.split('/');
var filename = paths[paths.length-1];
var dirpath = path.substr(0,path.length-filename.length);
// console.log('CORDOVA',filename,dirpath);
//return success('[{"a":"'+filename+'"}]');
window.resolveLocalFileSystemURL(dirpath, function(dir) {
// console.log('saveFile 2');
dir.getFile(filename, {create:true}, function(file) {
// console.log('saveFile 3');
// file.file(function(file) {
// console.log('saveFile 4');
file.createWriter(function(fileWriter) {
// fileWriter.seek(fileWriter.length);
var blob = new Blob([data], {type:'text/plain'});
fileWriter.write(blob);
fileWriter.onwriteend = function(){
if(cb) cb();
};
// console.log("ok, in theory i worked");
});
*/
/*
// Corodva
function writeFinish() {
// ... your done code here...
return cb()
};
var written = 0;
var BLOCK_SIZE = 1*1024*1024; // write 1M every time of write
function writeNext(cbFinish) {
var sz = Math.min(BLOCK_SIZE, data.length - written);
var sub = data.slice(written, written+sz);
writer.write(sub);
written += sz;
writer.onwrite = function(evt) {
if (written < data.length)
writeNext(cbFinish);
else
cbFinish();
};
}
writeNext(writeFinish);
}
*/
// });
// });
// });
} else {
if(isIE() == 9) {
// Solution was taken from
// http://megatuto.com/formation-JAVASCRIPT.php?JAVASCRIPT_Example=Javascript+Save+CSV+file+in+IE+8/IE+9+without+using+window.open()+Categorie+javascript+internet-explorer-8&category=&article=7993
// var URI = 'data:text/plain;charset=utf-8,';
// Prepare data
var ndata = data.replace(/\r\n/g,'&#A;&#D;');
ndata = ndata.replace(/\n/g,'&#D;');
ndata = ndata.replace(/\t/g,'	');
var testlink = window.open("about:blank", "_blank");
testlink.document.write(ndata); //fileData has contents for the file
testlink.document.close();
testlink.document.execCommand('SaveAs', false, path);
testlink.close();
} else {
var blob = new Blob([data], {type: "text/plain;charset=utf-8"});
saveAs(blob, path);
if(cb) res = cb(res);
}
}
};
return res;
};
// For compatibility issues
function isIE () {
var myNav = navigator.userAgent.toLowerCase();
return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
}
// For LOAD
// var saveBinaryFile = utils.saveFile = function(path, data, cb) {
// if(typeof exports == 'object') {
// // For Node.js
// var fs = require('fs');
// var data = fs.writeFileSync(path,data);
// } else {
// var blob = new Blob([data], {type: "text/plain;charset=utf-8"});
// saveAs(blob, path);
// }
// };
// Fast hash function
/**
Hash string to integer number
@param {string} str Source string
@return {integer} hash number
*/
var hash = utils.hash = function hash(str){
var h = 0;
if (str.length == 0) return h;
for (var i = 0; i < str.length; i++) {
h = ((h<<5)-h)+str.charCodeAt(i);
h = h & h;
}
return h;
};
/**
Union arrays
*/
var arrayUnion = utils.arrayUnion = function (a,b) {
var r = b.slice(0);
a.forEach(function(i) { if (r.indexOf(i) < 0) r.push(i); });
return r;
};
/**
Array Difference
*/
var arrayDiff = utils.arrayDiff = function (a,b) {
return a.filter(function(i) {return b.indexOf(i) < 0;});
};
/**
Arrays deep intersect (with records)
*/
var arrayIntersect = utils.arrayIntersect = function(a,b) {
var r = [];
a.forEach(function(ai) {
var found = false;
b.forEach(function(bi){
found = found || (ai==bi);
});
if(found) {
r.push(ai);
}
});
return r;
};
/**
Arrays deep union (with records)
*/
var arrayUnionDeep = utils.arrayUnionDeep = function (a,b) {
var r = b.slice(0);
a.forEach(function(ai) {
var found = false;
r.forEach(function(ri){
// found = found || equalDeep(ai, ri, true);
found = found || deepEqual(ai, ri);
});
if(!found) {
r.push(ai);
}
});
return r;
};
/**
Arrays deep union (with records)
*/
var arrayExceptDeep = utils.arrayExceptDeep = function (a,b) {
var r = [];
a.forEach(function(ai) {
var found = false;
b.forEach(function(bi){
// found = found || equalDeep(ai, bi, true);
found = found || deepEqual(ai, bi);
});
if(!found) {
r.push(ai);
}
});
return r;
};
/**
Arrays deep intersect (with records)
*/
var arrayIntersectDeep = utils.arrayIntersectDeep = function(a,b) {
var r = [];
a.forEach(function(ai) {
var found = false;
b.forEach(function(bi){
// found = found || equalDeep(ai, bi, true);
found = found || deepEqual(ai, bi, true);
});
if(found) {
r.push(ai);
}
});
return r;
};
/**
Deep clone obects
*/
var cloneDeep = utils.cloneDeep = function cloneDeep(obj) {
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = obj.constructor(); // changed
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
temp[key] = cloneDeep(obj[key]);
}
}
return temp;
}
/**
Check equality of objects
*/
var equalDeep = utils.equalDeep = function equalDeep (x, y, deep) {
if (deep) {
if (x == y) return true;
var p;
for (p in y) {
if (typeof (x[p]) == 'undefined') { return false; }
}
for (p in y) {
if (y[p]) {
switch (typeof (y[p])) {
case 'object':
if (!equalDeep(y[p],x[p])) { return false; } break;
case 'function':
if (typeof (x[p]) == 'undefined' ||
(p != 'equals' && y[p].toString() != x[p].toString()))
return false;
break;
default:
if (y[p] != x[p]) { return false; }
}
} else {
if (x[p])
return false;
}
}
for (p in x) {
if (typeof (y[p]) == 'undefined') { return false; }
}
return true;
}
return x == y;
};
/**
COmpare two object in deep
*/
var deepEqual = utils.deepEqual = function (x, y) {
if ((typeof x == "object" && x != null) && (typeof y == "object" && y != null)) {
if (Object.keys(x).length != Object.keys(y).length)
return false;
for (var prop in x) {
if (y.hasOwnProperty(prop))
{
if (! deepEqual(x[prop], y[prop]))
return false;
}
else
return false;
}
return true;
}
else if (x !== y)
return false;
else
return true;
}
/**
Extend object
*/
var extend = utils.extend = function extend (a,b){
if(typeof a == 'undefined') a = {};
for(var key in b) {
if(b.hasOwnProperty(key)) {
a[key] = b[key]
}
}
return a;
};;
/**
Flat array by first row
*/
var flatArray = utils.flatArray = function(a) {
if(!a || a.length == 0) return [];
var key = Object.keys(a[0])[0];
if(typeof key == 'undefined') return [];
return a.map(function(ai) {return ai[key]});
};
/**
Convert array of objects to array of arrays
*/
var arrayOfArrays = utils.arrayOfArrays = function (a) {
return a.map(function(aa){
var ar = [];
for(var key in aa) ar.push(aa[key]);
return ar;
});
};
/**
Excel:convert number to Excel column, like 1 => 'A'
@param {integer} i Column number, starting with 0
@return {string} Column name, starting with 'A'
*/
var xlsnc = utils.xlsnc = function(i) {
var addr = String.fromCharCode(65+i%26);
if(i>=26) {
i=((i/26)|0)-1;
addr = String.fromCharCode(65+i%26)+addr;
if(i>26) {
i=((i/26)|0)-1;
addr = String.fromCharCode(65+i%26)+addr;
};
};
return addr;
};
/**
Excel:conver Excel column name to number
@param {integer} i Column number, like 'A' or 'BE'
@return {string} Column name, starting with 0
*/
var xlscn = utils.xlscn = function(s) {
var n = s.charCodeAt(0)-65;
if(s.length>1) {
n = (n+1)*26+s.charCodeAt(1)-65;
// console.log(n, s.charCodeAt(0)-65, s.charCodeAt(1)-65);
if(s.length>2) {
n = (n+1)*26+s.charCodeAt(2)-65;
}
}
return n;
};
var domEmptyChildren = utils.domEmptyChildren = function (container){
var len = container.childNodes.length;
while (len--) {
container.removeChild(container.lastChild);
};
};
var distinctArray = utils.distinctArray = function(data) {
var uniq = {};
// TODO: Speedup, because Object.keys is slow
for(var i=0,ilen=data.length;i<ilen;i++) {
if(typeof data[i] == 'object') {
var uix = Object.keys(data[i]).sort().map(function(k){return k+'`'+data[i][k]}).join('`');
} else {
var uix = data[i];
}
uniq[uix] = data[i];
};
var res = [];
for(var key in uniq) res.push(uniq[key]);
return res;
}