Commit e227ad26 authored by Romain Courteaud's avatar Romain Courteaud

Drop not needed files.

parent 4f84be88
...@@ -148,30 +148,6 @@ module.exports = function (grunt) { ...@@ -148,30 +148,6 @@ module.exports = function (grunt) {
jschannel: { jschannel: {
src: 'http://mozilla.github.io/jschannel/src/jschannel.js', src: 'http://mozilla.github.io/jschannel/src/jschannel.js',
dest: 'lib/jschannel/jschannel.js' dest: 'lib/jschannel/jschannel.js'
},
jquery: {
src: 'http://code.jquery.com/jquery-2.0.3.js',
dest: 'lib/jquery/jquery.js'
},
jio: {
src: 'http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/jio.js',
dest: 'lib/jio/jio.js'
},
md5: {
src: 'http://git.erp5.org/gitweb/jio.git/blob_plain/HEAD:/src/md5.amd.js',
dest: 'lib/jio/md5.js'
},
sha256: {
src: 'http://git.erp5.org/gitweb/jio.git/blob_plain/HEAD:/src/sha256.amd.js',
dest: 'lib/jio/sha256.js'
},
localstorage: {
src: 'http://git.erp5.org/gitweb/jio.git/blob_plain/HEAD:/src/jio.storage/localstorage.js',
dest: 'lib/jio/localstorage.js'
},
complex_queries: {
src: 'http://git.erp5.org/gitweb/jio.git/blob_plain/HEAD:/complex_queries.js',
dest: 'lib/jio/complex_queries.js'
} }
}, },
......
/*
* Copyright 2013, Nexedi SA
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/**
* Provides some function to use complex queries with item list
*
* @module complex_queries
*/
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports);
}
window.complex_queries = {};
module(window.complex_queries);
}(['exports'], function (to_export) {
"use strict";
/**
* Add a secured (write permission denied) property to an object.
*
* @param {Object} object The object to fill
* @param {String} key The object key where to store the property
* @param {Any} value The value to store
*/
function _export(key, value) {
Object.defineProperty(to_export, key, {
"configurable": false,
"enumerable": true,
"writable": false,
"value": value
});
}
/**
* Parse a text request to a json query object tree
*
* @param {String} string The string to parse
* @return {Object} The json query tree
*/
function parseStringToObject(string) {
/*
Default template driver for JS/CC generated parsers running as
browser-based JavaScript/ECMAScript applications.
WARNING: This parser template will not run as console and has lesser
features for debugging than the console derivates for the
various JavaScript platforms.
Features:
- Parser trace messages
- Integrated panic-mode error recovery
Written 2007, 2008 by Jan Max Meyer, J.M.K S.F. Software Technologies
This is in the public domain.
*/
var NODEJS__dbg_withtrace = false;
var NODEJS__dbg_string = new String();
function __NODEJS_dbg_print( text )
{
NODEJS__dbg_string += text + "\n";
}
function __NODEJS_lex( info )
{
var state = 0;
var match = -1;
var match_pos = 0;
var start = 0;
var pos = info.offset + 1;
do
{
pos--;
state = 0;
match = -2;
start = pos;
if( info.src.length <= start )
return 19;
do
{
switch( state )
{
case 0:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 8 ) || ( info.src.charCodeAt( pos ) >= 10 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || info.src.charCodeAt( pos ) == 59 || ( info.src.charCodeAt( pos ) >= 63 && info.src.charCodeAt( pos ) <= 64 ) || ( info.src.charCodeAt( pos ) >= 66 && info.src.charCodeAt( pos ) <= 77 ) || ( info.src.charCodeAt( pos ) >= 80 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 9 ) state = 2;
else if( info.src.charCodeAt( pos ) == 40 ) state = 3;
else if( info.src.charCodeAt( pos ) == 41 ) state = 4;
else if( info.src.charCodeAt( pos ) == 60 || info.src.charCodeAt( pos ) == 62 ) state = 5;
else if( info.src.charCodeAt( pos ) == 34 ) state = 11;
else if( info.src.charCodeAt( pos ) == 79 ) state = 12;
else if( info.src.charCodeAt( pos ) == 32 ) state = 13;
else if( info.src.charCodeAt( pos ) == 61 ) state = 14;
else if( info.src.charCodeAt( pos ) == 65 ) state = 18;
else if( info.src.charCodeAt( pos ) == 78 ) state = 19;
else state = -1;
break;
case 1:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 10;
match_pos = pos;
break;
case 2:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 1;
match_pos = pos;
break;
case 3:
state = -1;
match = 3;
match_pos = pos;
break;
case 4:
state = -1;
match = 4;
match_pos = pos;
break;
case 5:
if( info.src.charCodeAt( pos ) == 61 ) state = 14;
else state = -1;
match = 11;
match_pos = pos;
break;
case 6:
state = -1;
match = 8;
match_pos = pos;
break;
case 7:
state = -1;
match = 9;
match_pos = pos;
break;
case 8:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 6;
match_pos = pos;
break;
case 9:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 5;
match_pos = pos;
break;
case 10:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else state = -1;
match = 7;
match_pos = pos;
break;
case 11:
if( info.src.charCodeAt( pos ) == 34 ) state = 7;
else if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 11;
else if( info.src.charCodeAt( pos ) == 92 ) state = 15;
else state = -1;
break;
case 12:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 81 ) || ( info.src.charCodeAt( pos ) >= 83 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 82 ) state = 8;
else state = -1;
match = 10;
match_pos = pos;
break;
case 13:
state = -1;
match = 1;
match_pos = pos;
break;
case 14:
state = -1;
match = 11;
match_pos = pos;
break;
case 15:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 254 ) ) state = 11;
else state = -1;
break;
case 16:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 67 ) || ( info.src.charCodeAt( pos ) >= 69 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 68 ) state = 9;
else state = -1;
match = 10;
match_pos = pos;
break;
case 17:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 83 ) || ( info.src.charCodeAt( pos ) >= 85 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 84 ) state = 10;
else state = -1;
match = 10;
match_pos = pos;
break;
case 18:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 77 ) || ( info.src.charCodeAt( pos ) >= 79 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 78 ) state = 16;
else state = -1;
match = 10;
match_pos = pos;
break;
case 19:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 78 ) || ( info.src.charCodeAt( pos ) >= 80 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1;
else if( info.src.charCodeAt( pos ) == 58 ) state = 6;
else if( info.src.charCodeAt( pos ) == 79 ) state = 17;
else state = -1;
match = 10;
match_pos = pos;
break;
}
pos++;
}
while( state > -1 );
}
while( 1 > -1 && match == 1 );
if( match > -1 )
{
info.att = info.src.substr( start, match_pos - start );
info.offset = match_pos;
}
else
{
info.att = new String();
match = -1;
}
return match;
}
function __NODEJS_parse( src, err_off, err_la )
{
var sstack = new Array();
var vstack = new Array();
var err_cnt = 0;
var act;
var go;
var la;
var rval;
var parseinfo = new Function( "", "var offset; var src; var att;" );
var info = new parseinfo();
/* Pop-Table */
var pop_tab = new Array(
new Array( 0/* begin' */, 1 ),
new Array( 13/* begin */, 1 ),
new Array( 12/* search_text */, 1 ),
new Array( 12/* search_text */, 2 ),
new Array( 12/* search_text */, 3 ),
new Array( 14/* and_expression */, 1 ),
new Array( 14/* and_expression */, 3 ),
new Array( 15/* boolean_expression */, 2 ),
new Array( 15/* boolean_expression */, 1 ),
new Array( 16/* expression */, 3 ),
new Array( 16/* expression */, 2 ),
new Array( 16/* expression */, 1 ),
new Array( 17/* value */, 2 ),
new Array( 17/* value */, 1 ),
new Array( 18/* string */, 1 ),
new Array( 18/* string */, 1 )
);
/* Action-Table */
var act_tab = new Array(
/* State 0 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 1 */ new Array( 19/* "$" */,0 ),
/* State 2 */ new Array( 19/* "$" */,-1 ),
/* State 3 */ new Array( 6/* "OR" */,14 , 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 , 19/* "$" */,-2 , 4/* "RIGHT_PARENTHESE" */,-2 ),
/* State 4 */ new Array( 5/* "AND" */,16 , 19/* "$" */,-5 , 7/* "NOT" */,-5 , 3/* "LEFT_PARENTHESE" */,-5 , 8/* "COLUMN" */,-5 , 11/* "OPERATOR" */,-5 , 10/* "WORD" */,-5 , 9/* "STRING" */,-5 , 6/* "OR" */,-5 , 4/* "RIGHT_PARENTHESE" */,-5 ),
/* State 5 */ new Array( 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 6 */ new Array( 19/* "$" */,-8 , 7/* "NOT" */,-8 , 3/* "LEFT_PARENTHESE" */,-8 , 8/* "COLUMN" */,-8 , 11/* "OPERATOR" */,-8 , 10/* "WORD" */,-8 , 9/* "STRING" */,-8 , 6/* "OR" */,-8 , 5/* "AND" */,-8 , 4/* "RIGHT_PARENTHESE" */,-8 ),
/* State 7 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 8 */ new Array( 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 9 */ new Array( 19/* "$" */,-11 , 7/* "NOT" */,-11 , 3/* "LEFT_PARENTHESE" */,-11 , 8/* "COLUMN" */,-11 , 11/* "OPERATOR" */,-11 , 10/* "WORD" */,-11 , 9/* "STRING" */,-11 , 6/* "OR" */,-11 , 5/* "AND" */,-11 , 4/* "RIGHT_PARENTHESE" */,-11 ),
/* State 10 */ new Array( 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 11 */ new Array( 19/* "$" */,-13 , 7/* "NOT" */,-13 , 3/* "LEFT_PARENTHESE" */,-13 , 8/* "COLUMN" */,-13 , 11/* "OPERATOR" */,-13 , 10/* "WORD" */,-13 , 9/* "STRING" */,-13 , 6/* "OR" */,-13 , 5/* "AND" */,-13 , 4/* "RIGHT_PARENTHESE" */,-13 ),
/* State 12 */ new Array( 19/* "$" */,-14 , 7/* "NOT" */,-14 , 3/* "LEFT_PARENTHESE" */,-14 , 8/* "COLUMN" */,-14 , 11/* "OPERATOR" */,-14 , 10/* "WORD" */,-14 , 9/* "STRING" */,-14 , 6/* "OR" */,-14 , 5/* "AND" */,-14 , 4/* "RIGHT_PARENTHESE" */,-14 ),
/* State 13 */ new Array( 19/* "$" */,-15 , 7/* "NOT" */,-15 , 3/* "LEFT_PARENTHESE" */,-15 , 8/* "COLUMN" */,-15 , 11/* "OPERATOR" */,-15 , 10/* "WORD" */,-15 , 9/* "STRING" */,-15 , 6/* "OR" */,-15 , 5/* "AND" */,-15 , 4/* "RIGHT_PARENTHESE" */,-15 ),
/* State 14 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 15 */ new Array( 19/* "$" */,-3 , 4/* "RIGHT_PARENTHESE" */,-3 ),
/* State 16 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ),
/* State 17 */ new Array( 19/* "$" */,-7 , 7/* "NOT" */,-7 , 3/* "LEFT_PARENTHESE" */,-7 , 8/* "COLUMN" */,-7 , 11/* "OPERATOR" */,-7 , 10/* "WORD" */,-7 , 9/* "STRING" */,-7 , 6/* "OR" */,-7 , 5/* "AND" */,-7 , 4/* "RIGHT_PARENTHESE" */,-7 ),
/* State 18 */ new Array( 4/* "RIGHT_PARENTHESE" */,23 ),
/* State 19 */ new Array( 19/* "$" */,-10 , 7/* "NOT" */,-10 , 3/* "LEFT_PARENTHESE" */,-10 , 8/* "COLUMN" */,-10 , 11/* "OPERATOR" */,-10 , 10/* "WORD" */,-10 , 9/* "STRING" */,-10 , 6/* "OR" */,-10 , 5/* "AND" */,-10 , 4/* "RIGHT_PARENTHESE" */,-10 ),
/* State 20 */ new Array( 19/* "$" */,-12 , 7/* "NOT" */,-12 , 3/* "LEFT_PARENTHESE" */,-12 , 8/* "COLUMN" */,-12 , 11/* "OPERATOR" */,-12 , 10/* "WORD" */,-12 , 9/* "STRING" */,-12 , 6/* "OR" */,-12 , 5/* "AND" */,-12 , 4/* "RIGHT_PARENTHESE" */,-12 ),
/* State 21 */ new Array( 19/* "$" */,-4 , 4/* "RIGHT_PARENTHESE" */,-4 ),
/* State 22 */ new Array( 19/* "$" */,-6 , 7/* "NOT" */,-6 , 3/* "LEFT_PARENTHESE" */,-6 , 8/* "COLUMN" */,-6 , 11/* "OPERATOR" */,-6 , 10/* "WORD" */,-6 , 9/* "STRING" */,-6 , 6/* "OR" */,-6 , 4/* "RIGHT_PARENTHESE" */,-6 ),
/* State 23 */ new Array( 19/* "$" */,-9 , 7/* "NOT" */,-9 , 3/* "LEFT_PARENTHESE" */,-9 , 8/* "COLUMN" */,-9 , 11/* "OPERATOR" */,-9 , 10/* "WORD" */,-9 , 9/* "STRING" */,-9 , 6/* "OR" */,-9 , 5/* "AND" */,-9 , 4/* "RIGHT_PARENTHESE" */,-9 )
);
/* Goto-Table */
var goto_tab = new Array(
/* State 0 */ new Array( 13/* begin */,1 , 12/* search_text */,2 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 1 */ new Array( ),
/* State 2 */ new Array( ),
/* State 3 */ new Array( 12/* search_text */,15 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 4 */ new Array( ),
/* State 5 */ new Array( 16/* expression */,17 , 17/* value */,9 , 18/* string */,11 ),
/* State 6 */ new Array( ),
/* State 7 */ new Array( 12/* search_text */,18 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 8 */ new Array( 16/* expression */,19 , 17/* value */,9 , 18/* string */,11 ),
/* State 9 */ new Array( ),
/* State 10 */ new Array( 18/* string */,20 ),
/* State 11 */ new Array( ),
/* State 12 */ new Array( ),
/* State 13 */ new Array( ),
/* State 14 */ new Array( 12/* search_text */,21 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 15 */ new Array( ),
/* State 16 */ new Array( 14/* and_expression */,22 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ),
/* State 17 */ new Array( ),
/* State 18 */ new Array( ),
/* State 19 */ new Array( ),
/* State 20 */ new Array( ),
/* State 21 */ new Array( ),
/* State 22 */ new Array( ),
/* State 23 */ new Array( )
);
/* Symbol labels */
var labels = new Array(
"begin'" /* Non-terminal symbol */,
"WHITESPACE" /* Terminal symbol */,
"WHITESPACE" /* Terminal symbol */,
"LEFT_PARENTHESE" /* Terminal symbol */,
"RIGHT_PARENTHESE" /* Terminal symbol */,
"AND" /* Terminal symbol */,
"OR" /* Terminal symbol */,
"NOT" /* Terminal symbol */,
"COLUMN" /* Terminal symbol */,
"STRING" /* Terminal symbol */,
"WORD" /* Terminal symbol */,
"OPERATOR" /* Terminal symbol */,
"search_text" /* Non-terminal symbol */,
"begin" /* Non-terminal symbol */,
"and_expression" /* Non-terminal symbol */,
"boolean_expression" /* Non-terminal symbol */,
"expression" /* Non-terminal symbol */,
"value" /* Non-terminal symbol */,
"string" /* Non-terminal symbol */,
"$" /* Terminal symbol */
);
info.offset = 0;
info.src = src;
info.att = new String();
if( !err_off )
err_off = new Array();
if( !err_la )
err_la = new Array();
sstack.push( 0 );
vstack.push( 0 );
la = __NODEJS_lex( info );
while( true )
{
act = 25;
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( act_tab[sstack[sstack.length-1]][i] == la )
{
act = act_tab[sstack[sstack.length-1]][i+1];
break;
}
}
if( NODEJS__dbg_withtrace && sstack.length > 0 )
{
__NODEJS_dbg_print( "\nState " + sstack[sstack.length-1] + "\n" +
"\tLookahead: " + labels[la] + " (\"" + info.att + "\")\n" +
"\tAction: " + act + "\n" +
"\tSource: \"" + info.src.substr( info.offset, 30 ) + ( ( info.offset + 30 < info.src.length ) ?
"..." : "" ) + "\"\n" +
"\tStack: " + sstack.join() + "\n" +
"\tValue stack: " + vstack.join() + "\n" );
}
//Panic-mode: Try recovery when parse-error occurs!
if( act == 25 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "Error detected: There is no reduce or shift on the symbol " + labels[la] );
err_cnt++;
err_off.push( info.offset - info.att.length );
err_la.push( new Array() );
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
err_la[err_la.length-1].push( labels[act_tab[sstack[sstack.length-1]][i]] );
//Remember the original stack!
var rsstack = new Array();
var rvstack = new Array();
for( var i = 0; i < sstack.length; i++ )
{
rsstack[i] = sstack[i];
rvstack[i] = vstack[i];
}
while( act == 25 && la != 19 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tError recovery\n" +
"Current lookahead: " + labels[la] + " (" + info.att + ")\n" +
"Action: " + act + "\n\n" );
if( la == -1 )
info.offset++;
while( act == 25 && sstack.length > 0 )
{
sstack.pop();
vstack.pop();
if( sstack.length == 0 )
break;
act = 25;
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( act_tab[sstack[sstack.length-1]][i] == la )
{
act = act_tab[sstack[sstack.length-1]][i+1];
break;
}
}
}
if( act != 25 )
break;
for( var i = 0; i < rsstack.length; i++ )
{
sstack.push( rsstack[i] );
vstack.push( rvstack[i] );
}
la = __NODEJS_lex( info );
}
if( act == 25 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tError recovery failed, terminating parse process..." );
break;
}
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tError recovery succeeded, continuing" );
}
/*
if( act == 25 )
break;
*/
//Shift
if( act > 0 )
{
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "Shifting symbol: " + labels[la] + " (" + info.att + ")" );
sstack.push( act );
vstack.push( info.att );
la = __NODEJS_lex( info );
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tNew lookahead symbol: " + labels[la] + " (" + info.att + ")" );
}
//Reduce
else
{
act *= -1;
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "Reducing by producution: " + act );
rval = void(0);
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tPerforming semantic action..." );
switch( act )
{
case 0:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 1:
{
result = vstack[ vstack.length - 1 ];
}
break;
case 2:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 3:
{
rval = mkComplexQuery('OR',[vstack[ vstack.length - 2 ],vstack[ vstack.length - 1 ]]);
}
break;
case 4:
{
rval = mkComplexQuery('OR',[vstack[ vstack.length - 3 ],vstack[ vstack.length - 1 ]]);
}
break;
case 5:
{
rval = vstack[ vstack.length - 1 ] ;
}
break;
case 6:
{
rval = mkComplexQuery('AND',[vstack[ vstack.length - 3 ],vstack[ vstack.length - 1 ]]);
}
break;
case 7:
{
rval = mkNotQuery(vstack[ vstack.length - 1 ]);
}
break;
case 8:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 9:
{
rval = vstack[ vstack.length - 2 ];
}
break;
case 10:
{
simpleQuerySetKey(vstack[ vstack.length - 1 ],vstack[ vstack.length - 2 ].split(':').slice(0,-1).join(':')); rval = vstack[ vstack.length - 1 ];
}
break;
case 11:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 12:
{
vstack[ vstack.length - 1 ].operator = vstack[ vstack.length - 2 ] ; rval = vstack[ vstack.length - 1 ];
}
break;
case 13:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 14:
{
rval = mkSimpleQuery('',vstack[ vstack.length - 1 ]);
}
break;
case 15:
{
rval = mkSimpleQuery('',vstack[ vstack.length - 1 ].split('"').slice(1,-1).join('"'));
}
break;
}
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tPopping " + pop_tab[act][1] + " off the stack..." );
for( var i = 0; i < pop_tab[act][1]; i++ )
{
sstack.pop();
vstack.pop();
}
go = -1;
for( var i = 0; i < goto_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( goto_tab[sstack[sstack.length-1]][i] == pop_tab[act][0] )
{
go = goto_tab[sstack[sstack.length-1]][i+1];
break;
}
}
if( act == 0 )
break;
if( NODEJS__dbg_withtrace )
__NODEJS_dbg_print( "\tPushing non-terminal " + labels[ pop_tab[act][0] ] );
sstack.push( go );
vstack.push( rval );
}
if( NODEJS__dbg_withtrace )
{
alert( NODEJS__dbg_string );
NODEJS__dbg_string = new String();
}
}
if( NODEJS__dbg_withtrace )
{
__NODEJS_dbg_print( "\nParse complete." );
alert( NODEJS__dbg_string );
}
return err_cnt;
}
var arrayExtend = function () {
var j, i, newlist = [], list_list = arguments;
for (j = 0; j < list_list.length; j += 1) {
for (i = 0; i < list_list[j].length; i += 1) {
newlist.push(list_list[j][i]);
}
}
return newlist;
}, mkSimpleQuery = function (key, value, operator) {
return {"type": "simple", "operator": "=", "key": key, "value": value};
}, mkNotQuery = function (query) {
if (query.operator === "NOT") {
return query.query_list[0];
}
return {"type": "complex", "operator": "NOT", "query_list": [query]};
}, mkComplexQuery = function (operator, query_list) {
var i, query_list2 = [];
for (i = 0; i < query_list.length; i += 1) {
if (query_list[i].operator === operator) {
query_list2 = arrayExtend(query_list2, query_list[i].query_list);
} else {
query_list2.push(query_list[i]);
}
}
return {type:"complex",operator:operator,query_list:query_list2};
}, simpleQuerySetKey = function (query, key) {
var i;
if (query.type === "complex") {
for (i = 0; i < query.query_list.length; ++i) {
simpleQuerySetKey (query.query_list[i],key);
}
return true;
}
if (query.type === "simple" && !query.key) {
query.key = key;
return true;
}
return false;
},
error_offsets = [],
error_lookaheads = [],
error_count = 0,
result;
if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0) {
var i;
for (i = 0; i < error_count; i += 1) {
throw new Error("Parse error near \"" +
string.substr(error_offsets[i]) +
"\", expecting \"" +
error_lookaheads[i].join() + "\"");
}
}
return result;
} // parseStringToObject
_export('parseStringToObject', parseStringToObject);
/*jslint indent: 2, maxlen: 80, sloppy: true */
var query_class_dict = {};
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query: true, query_class_dict: true, inherits: true,
_export: true, QueryFactory: true */
/**
* The ComplexQuery inherits from Query, and compares one or several metadata
* values.
*
* @class ComplexQuery
* @extends Query
* @param {Object} [spec={}] The specifications
* @param {String} [spec.operator="AND"] The compare method to use
* @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare
*/
function ComplexQuery(spec) {
Query.call(this);
/**
* Logical operator to use to compare object values
*
* @attribute operator
* @type String
* @default "AND"
* @optional
*/
this.operator = spec.operator || "AND";
/**
* The sub Query list which are used to query an item.
*
* @attribute query_list
* @type Array
* @default []
* @optional
*/
this.query_list = spec.query_list || [];
this.query_list = this.query_list.map(QueryFactory.create);
}
inherits(ComplexQuery, Query);
/**
* #crossLink "Query/match:method"
*/
ComplexQuery.prototype.match = function (item, wildcard_character) {
return this[this.operator](item, wildcard_character);
};
/**
* #crossLink "Query/toString:method"
*/
ComplexQuery.prototype.toString = function () {
var str_list = ["("], this_operator = this.operator;
this.query_list.forEach(function (query) {
str_list.push(query.toString());
str_list.push(this_operator);
});
str_list.pop(); // remove last operator
str_list.push(")");
return str_list.join(" ");
};
/**
* #crossLink "Query/serialized:method"
*/
ComplexQuery.prototype.serialized = function () {
var s = {
"type": "complex",
"operator": this.operator,
"query_list": []
};
this.query_list.forEach(function (query) {
s.query_list.push(query.serialized());
});
return s;
};
/**
* Comparison operator, test if all sub queries match the
* item value
*
* @method AND
* @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character
* @return {Boolean} true if all match, false otherwise
*/
ComplexQuery.prototype.AND = function (item, wildcard_character) {
var i;
for (i = 0; i < this.query_list.length; i += 1) {
if (!this.query_list[i].match(item, wildcard_character)) {
return false;
}
}
return true;
};
/**
* Comparison operator, test if one of the sub queries matches the
* item value
*
* @method OR
* @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character
* @return {Boolean} true if one match, false otherwise
*/
ComplexQuery.prototype.OR = function (item, wildcard_character) {
var i;
for (i = 0; i < this.query_list.length; i += 1) {
if (this.query_list[i].match(item, wildcard_character)) {
return true;
}
}
return false;
};
/**
* Comparison operator, test if the sub query does not match the
* item value
*
* @method NOT
* @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character
* @return {Boolean} true if one match, false otherwise
*/
ComplexQuery.prototype.NOT = function (item, wildcard_character) {
return !this.query_list[0].match(item, wildcard_character);
};
query_class_dict.complex = ComplexQuery;
_export("ComplexQuery", ComplexQuery);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
true, select: true, _export: true, stringEscapeRegexpCharacters: true,
deepClone: true */
/**
* The query to use to filter a list of objects.
* This is an abstract class.
*
* @class Query
* @constructor
*/
function Query() {
/**
* Called before parsing the query. Must be overridden!
*
* @method onParseStart
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseStart = emptyFunction;
/**
* Called when parsing a simple query. Must be overridden!
*
* @method onParseSimpleQuery
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseSimpleQuery = emptyFunction;
/**
* Called when parsing a complex query. Must be overridden!
*
* @method onParseComplexQuery
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseComplexQuery = emptyFunction;
/**
* Called after parsing the query. Must be overridden!
*
* @method onParseEnd
* @param {Object} object The object shared in the parse process
* @param {Object} option Some option gave in parse()
*/
this.onParseEnd = emptyFunction;
}
/**
* Filter the item list with matching item only
*
* @method exec
* @param {Array} item_list The list of object
* @param {Object} [option] Some operation option
* @param {String} [option.wildcard_character="%"] The wildcard character
* @param {Array} [option.select_list] A object keys to retrieve
* @param {Array} [option.sort_on] Couples of object keys and "ascending"
* or "descending"
* @param {Array} [option.limit] Couple of integer, first is an index and
* second is the length.
*/
Query.prototype.exec = function (item_list, option) {
var i = 0;
if (!Array.isArray(item_list)) {
throw new TypeError("Query().exec(): Argument 1 is not of type 'array'");
}
if (option === undefined) {
option = {};
}
if (typeof option !== 'object') {
throw new TypeError("Query().exec(): " +
"Optional argument 2 is not of type 'object'");
}
if (option.wildcard_character === undefined) {
option.wildcard_character = '%';
}
while (i < item_list.length) {
if (!item_list[i] || !this.match(item_list[i], option.wildcard_character)) {
item_list.splice(i, 1);
} else {
i += 1;
}
}
if (option.sort_on) {
sortOn(option.sort_on, item_list);
}
if (option.limit) {
limit(option.limit, item_list);
}
select(option.select_list || [], item_list);
};
/**
* Test if an item matches this query
*
* @method match
* @param {Object} item The object to test
* @param {String} wildcard_character The wildcard character to use
* @return {Boolean} true if match, false otherwise
*/
Query.prototype.match = function () {
return true;
};
/**
* Browse the Query in deep calling parser method in each step.
*
* `onParseStart` is called first, on end `onParseEnd` is called.
* It starts from the simple queries at the bottom of the tree calling the
* parser method `onParseSimpleQuery`, and go up calling the
* `onParseComplexQuery` method.
*
* @method parse
* @param {Object} option Any options you want (except 'parsed')
* @return {Any} The parse result
*/
Query.prototype.parse = function (option) {
var that = this, object;
/**
* The recursive parser.
*
* @param {Object} object The object shared in the parse process
* @param {Object} options Some options usable in the parseMethods
* @return {Any} The parser result
*/
function recParse(object, option) {
var i, query = object.parsed;
if (query.type === "complex") {
for (i = 0; i < query.query_list.length; i += 1) {
object.parsed = query.query_list[i];
recParse(object, option);
query.query_list[i] = object.parsed;
}
object.parsed = query;
that.onParseComplexQuery(object, option);
} else if (query.type === "simple") {
that.onParseSimpleQuery(object, option);
}
}
object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};
that.onParseStart(object, option);
recParse(object, option);
that.onParseEnd(object, option);
return object.parsed;
};
/**
* Convert this query to a parsable string.
*
* @method toString
* @return {String} The string version of this query
*/
Query.prototype.toString = function () {
return "";
};
/**
* Convert this query to an jsonable object in order to be remake thanks to
* QueryFactory class.
*
* @method serialized
* @return {Object} The jsonable object
*/
Query.prototype.serialized = function () {
return undefined;
};
_export("Query", Query);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export, ComplexQuery, SimpleQuery, Query, parseStringToObject,
query_class_dict */
/**
* Provides static methods to create Query object
*
* @class QueryFactory
*/
function QueryFactory() {
return;
}
/**
* Creates Query object from a search text string or a serialized version
* of a Query.
*
* @method create
* @static
* @param {Object,String} object The search text or the serialized version
* of a Query
* @return {Query} A Query object
*/
QueryFactory.create = function (object) {
if (object === "") {
return new Query();
}
if (typeof object === "string") {
object = parseStringToObject(object);
}
if (typeof (object || {}).type === "string" &&
query_class_dict[object.type]) {
return new query_class_dict[object.type](object);
}
throw new TypeError("QueryFactory.create(): " +
"Argument 1 is not a search text or a parsable object");
};
_export("QueryFactory", QueryFactory);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export: true, to_export: true */
function objectToSearchText(query) {
var str_list = [];
if (query.type === "complex") {
str_list.push("(");
(query.query_list || []).forEach(function (sub_query) {
str_list.push(objectToSearchText(sub_query));
str_list.push(query.operator);
});
str_list.length -= 1;
str_list.push(")");
return str_list.join(" ");
}
if (query.type === "simple") {
return query.id + (query.id ? ": " : "") + (query.operator || "=") + ' "' +
query.value + '"';
}
throw new TypeError("This object is not a query");
}
_export("objectToSearchText", objectToSearchText);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query: true, inherits: true, query_class_dict: true, _export: true,
convertStringToRegExp: true */
/**
* The SimpleQuery inherits from Query, and compares one metadata value
*
* @class SimpleQuery
* @extends Query
* @param {Object} [spec={}] The specifications
* @param {String} [spec.operator="="] The compare method to use
* @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare
*/
function SimpleQuery(spec) {
Query.call(this);
/**
* Operator to use to compare object values
*
* @attribute operator
* @type String
* @default "="
* @optional
*/
this.operator = spec.operator || "=";
/**
* Key of the object which refers to the value to compare
*
* @attribute key
* @type String
*/
this.key = spec.key;
/**
* Value is used to do the comparison with the object value
*
* @attribute value
* @type String
*/
this.value = spec.value;
}
inherits(SimpleQuery, Query);
/**
* #crossLink "Query/match:method"
*/
SimpleQuery.prototype.match = function (item, wildcard_character) {
return this[this.operator](item[this.key], this.value, wildcard_character);
};
/**
* #crossLink "Query/toString:method"
*/
SimpleQuery.prototype.toString = function () {
return (this.key ? this.key + ": " : "") + (this.operator || "=") + ' "' +
this.value + '"';
};
/**
* #crossLink "Query/serialized:method"
*/
SimpleQuery.prototype.serialized = function () {
return {
"type": "simple",
"operator": this.operator,
"key": this.key,
"value": this.value
};
};
/**
* Comparison operator, test if this query value matches the item value
*
* @method =
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @param {String} wildcard_character The wildcard_character
* @return {Boolean} true if match, false otherwise
*/
SimpleQuery.prototype["="] = function (object_value, comparison_value,
wildcard_character) {
var value, i;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
for (i = 0; i < object_value.length; i += 1) {
value = object_value[i];
if (typeof value === 'object') {
value = value.content;
}
if (comparison_value === undefined) {
if (value === undefined) {
return true;
}
return false;
}
if (value === undefined) {
return false;
}
if (
convertStringToRegExp(
comparison_value.toString(),
wildcard_character
).test(value.toString())
) {
return true;
}
}
return false;
};
/**
* Comparison operator, test if this query value does not match the item value
*
* @method !=
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @param {String} wildcard_character The wildcard_character
* @return {Boolean} true if not match, false otherwise
*/
SimpleQuery.prototype["!="] = function (object_value, comparison_value,
wildcard_character) {
var value, i;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
for (i = 0; i < object_value.length; i += 1) {
value = object_value[i];
if (typeof value === 'object') {
value = value.content;
}
if (comparison_value === undefined) {
if (value === undefined) {
return false;
}
return true;
}
if (value === undefined) {
return true;
}
if (
convertStringToRegExp(
comparison_value.toString(),
wildcard_character
).test(value.toString())
) {
return false;
}
}
return true;
};
/**
* Comparison operator, test if this query value is lower than the item value
*
* @method <
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if lower, false otherwise
*/
SimpleQuery.prototype["<"] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return value < comparison_value;
};
/**
* Comparison operator, test if this query value is equal or lower than the
* item value
*
* @method <=
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or lower, false otherwise
*/
SimpleQuery.prototype["<="] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return value <= comparison_value;
};
/**
* Comparison operator, test if this query value is greater than the item
* value
*
* @method >
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if greater, false otherwise
*/
SimpleQuery.prototype[">"] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return value > comparison_value;
};
/**
* Comparison operator, test if this query value is equal or greater than the
* item value
*
* @method >=
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or greater, false otherwise
*/
SimpleQuery.prototype[">="] = function (object_value, comparison_value) {
var value;
if (!Array.isArray(object_value)) {
object_value = [object_value];
}
value = object_value[0];
if (typeof value === 'object') {
value = value.content;
}
return value >= comparison_value;
};
query_class_dict.simple = SimpleQuery;
_export("SimpleQuery", SimpleQuery);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export: true */
/**
* Escapes regexp special chars from a string.
*
* @param {String} string The string to escape
* @return {String} The escaped string
*/
function stringEscapeRegexpCharacters(string) {
if (typeof string === "string") {
return string.replace(/([\\\.\$\[\]\(\)\{\}\^\?\*\+\-])/g, "\\$1");
}
throw new TypeError("complex_queries.stringEscapeRegexpCharacters(): " +
"Argument no 1 is not of type 'string'");
}
_export("stringEscapeRegexpCharacters", stringEscapeRegexpCharacters);
/**
* Convert metadata values to array of strings. ex:
*
* "a" -> ["a"],
* {"content": "a"} -> ["a"]
*
* @param {Any} value The metadata value
* @return {Array} The value in string array format
*/
function metadataValueToStringArray(value) {
var i, new_value = [];
if (value === undefined) {
return undefined;
}
if (!Array.isArray(value)) {
value = [value];
}
for (i = 0; i < value.length; i += 1) {
if (typeof value[i] === 'object') {
new_value[i] = value[i].content;
} else {
new_value[i] = value[i];
}
}
return new_value;
}
/**
* A sort function to sort items by key
*
* @param {String} key The key to sort on
* @param {String} [way="ascending"] 'ascending' or 'descending'
* @return {Function} The sort function
*/
function sortFunction(key, way) {
if (way === 'descending') {
return function (a, b) {
// this comparison is 5 times faster than json comparison
var i, l;
a = metadataValueToStringArray(a[key]) || [];
b = metadataValueToStringArray(b[key]) || [];
l = a.length > b.length ? a.length : b.length;
for (i = 0; i < l; i += 1) {
if (a[i] === undefined) {
return 1;
}
if (b[i] === undefined) {
return -1;
}
if (a[i] > b[i]) {
return -1;
}
if (a[i] < b[i]) {
return 1;
}
}
return 0;
};
}
if (way === 'ascending') {
return function (a, b) {
// this comparison is 5 times faster than json comparison
var i, l;
a = metadataValueToStringArray(a[key]) || [];
b = metadataValueToStringArray(b[key]) || [];
l = a.length > b.length ? a.length : b.length;
for (i = 0; i < l; i += 1) {
if (a[i] === undefined) {
return -1;
}
if (b[i] === undefined) {
return 1;
}
if (a[i] > b[i]) {
return 1;
}
if (a[i] < b[i]) {
return -1;
}
}
return 0;
};
}
throw new TypeError("complex_queries.sortFunction(): " +
"Argument 2 must be 'ascending' or 'descending'");
}
/**
* Clones all native object in deep. Managed types: Object, Array, String,
* Number, Boolean, null.
*
* @param {A} object The object to clone
* @return {A} The cloned object
*/
function deepClone(object) {
var i, cloned;
if (Array.isArray(object)) {
cloned = [];
for (i = 0; i < object.length; i += 1) {
cloned[i] = deepClone(object[i]);
}
return cloned;
}
if (typeof object === "object") {
cloned = {};
for (i in object) {
if (object.hasOwnProperty(i)) {
cloned[i] = deepClone(object[i]);
}
}
return cloned;
}
return object;
}
/**
* Inherits the prototype methods from one constructor into another. The
* prototype of `constructor` will be set to a new object created from
* `superConstructor`.
*
* @param {Function} constructor The constructor which inherits the super one
* @param {Function} superConstructor The super constructor
*/
function inherits(constructor, superConstructor) {
constructor.super_ = superConstructor;
constructor.prototype = Object.create(superConstructor.prototype, {
"constructor": {
"configurable": true,
"enumerable": false,
"writable": true,
"value": constructor
}
});
}
/**
* Does nothing
*/
function emptyFunction() {
return;
}
/**
* Filter a list of items, modifying them to select only wanted keys. If
* `clone` is true, then the method will act on a cloned list.
*
* @param {Array} select_option Key list to keep
* @param {Array} list The item list to filter
* @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/
function select(select_option, list, clone) {
var i, j, new_item;
if (!Array.isArray(select_option)) {
throw new TypeError("complex_queries.select(): " +
"Argument 1 is not of type Array");
}
if (!Array.isArray(list)) {
throw new TypeError("complex_queries.select(): " +
"Argument 2 is not of type Array");
}
if (clone === true) {
list = deepClone(list);
}
for (i = 0; i < list.length; i += 1) {
new_item = {};
for (j = 0; j < select_option.length; j += 1) {
if (list[i].hasOwnProperty([select_option[j]])) {
new_item[select_option[j]] = list[i][select_option[j]];
}
}
for (j in new_item) {
if (new_item.hasOwnProperty(j)) {
list[i] = new_item;
break;
}
}
}
return list;
}
_export('select', select);
/**
* Sort a list of items, according to keys and directions. If `clone` is true,
* then the method will act on a cloned list.
*
* @param {Array} sort_on_option List of couples [key, direction]
* @param {Array} list The item list to sort
* @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/
function sortOn(sort_on_option, list, clone) {
var sort_index;
if (!Array.isArray(sort_on_option)) {
throw new TypeError("complex_queries.sortOn(): " +
"Argument 1 is not of type 'array'");
}
if (clone) {
list = deepClone(list);
}
for (sort_index = sort_on_option.length - 1; sort_index >= 0;
sort_index -= 1) {
list.sort(sortFunction(
sort_on_option[sort_index][0],
sort_on_option[sort_index][1]
));
}
return list;
}
_export('sortOn', sortOn);
/**
* Limit a list of items, according to index and length. If `clone` is true,
* then the method will act on a cloned list.
*
* @param {Array} limit_option A couple [from, length]
* @param {Array} list The item list to limit
* @param {Boolean} [clone=false] If true, modifies a clone of the list
* @return {Array} The filtered list
*/
function limit(limit_option, list, clone) {
if (!Array.isArray(limit_option)) {
throw new TypeError("complex_queries.limit(): " +
"Argument 1 is not of type 'array'");
}
if (!Array.isArray(list)) {
throw new TypeError("complex_queries.limit(): " +
"Argument 2 is not of type 'array'");
}
if (clone) {
list = deepClone(list);
}
list.splice(0, limit_option[0]);
if (limit_option[1]) {
list.splice(limit_option[1]);
}
return list;
}
_export('limit', limit);
/**
* Convert a search text to a regexp.
*
* @param {String} string The string to convert
* @param {String} [wildcard_character=undefined] The wildcard chararter
* @return {RegExp} The search text regexp
*/
function convertStringToRegExp(string, wildcard_character) {
if (typeof string !== 'string') {
throw new TypeError("complex_queries.convertStringToRegExp(): " +
"Argument 1 is not of type 'string'");
}
if (wildcard_character === undefined ||
wildcard_character === null || wildcard_character === '') {
return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$");
}
if (typeof wildcard_character !== 'string' || wildcard_character.length > 1) {
throw new TypeError("complex_queries.convertStringToRegExp(): " +
"Optional argument 2 must be a string of length <= 1");
}
return new RegExp("^" + stringEscapeRegexpCharacters(string).replace(
new RegExp(stringEscapeRegexpCharacters(wildcard_character), 'g'),
'.*'
) + "$");
}
_export('convertStringToRegExp', convertStringToRegExp);
return to_export;
}));
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
window.jIO = {};
module(window.jIO, RSVP, {hex_sha256: hex_sha256});
}(['exports', 'rsvp', 'sha256'], function (exports, RSVP, sha256) {
"use strict";
var hex_sha256 = sha256.hex_sha256;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global uniqueJSONStringify, methodType */
var defaults = {}, constants = {};
defaults.storage_types = {};
constants.dcmi_types = {
'Collection': 'Collection',
'Dataset': 'Dataset',
'Event': 'Event',
'Image': 'Image',
'InteractiveResource': 'InteractiveResource',
'MovingImage': 'MovingImage',
'PhysicalObject': 'PhysicalObject',
'Service': 'Service',
'Software': 'Software',
'Sound': 'Sound',
'StillImage': 'StillImage',
'Text': 'Text'
};
// if (dcmi_types.Collection === 'Collection') { is a DCMI type }
// if (typeof dcmi_types[name] === 'string') { is a DCMI type }
constants.http_status_text = {
"0": "Unknown",
"550": "Internal JIO Error",
"551": "Internal Storage Error",
"Unknown": "Unknown",
"Internal JIO Error": "Internal JIO Error",
"Internal Storage Error": "Internal Storage Error",
"unknown": "Unknown",
"internal_jio_error": "Internal JIO Error",
"internal_storage_error": "Internal Storage Error",
"200": "Ok",
"201": "Created",
"204": "No Content",
"205": "Reset Content",
"206": "Partial Content",
"400": "Bad Request",
"401": "Unauthorized",
"402": "Payment Required",
"403": "Forbidden",
"404": "Not Found",
"405": "Method Not Allowed",
"406": "Not Acceptable",
"407": "Proxy Authentication Required",
"408": "Request Timeout",
"409": "Conflict",
"410": "Gone",
"411": "Length Required",
"412": "Precondition Failed",
"413": "Request Entity Too Large",
"414": "Request-URI Too Long",
"415": "Unsupported Media Type",
"416": "Requested Range Not Satisfiable",
"417": "Expectation Failed",
"418": "I'm a teapot",
"419": "Authentication Timeout",
"500": "Internal Server Error",
"501": "Not Implemented",
"502": "Bad Gateway",
"503": "Service Unavailable",
"504": "Gateway Timeout",
"507": "Insufficient Storage",
"Ok": "Ok",
"OK": "Ok",
"Created": "Created",
"No Content": "No Content",
"Reset Content": "Reset Content",
"Partial Content": "Partial Content",
"Bad Request": "Bad Request",
"Unauthorized": "Unauthorized",
"Payment Required": "Payment Required",
"Forbidden": "Forbidden",
"Not Found": "Not Found",
"Method Not Allowed": "Method Not Allowed",
"Not Acceptable": "Not Acceptable",
"Proxy Authentication Required": "Proxy Authentication Required",
"Request Timeout": "Request Timeout",
"Conflict": "Conflict",
"Gone": "Gone",
"Length Required": "Length Required",
"Precondition Failed": "Precondition Failed",
"Request Entity Too Large": "Request Entity Too Large",
"Request-URI Too Long": "Request-URI Too Long",
"Unsupported Media Type": "Unsupported Media Type",
"Requested Range Not Satisfiable": "Requested Range Not Satisfiable",
"Expectation Failed": "Expectation Failed",
"I'm a teapot": "I'm a teapot",
"Authentication Timeout": "Authentication Timeout",
"Internal Server Error": "Internal Server Error",
"Not Implemented": "Not Implemented",
"Bad Gateway": "Bad Gateway",
"Service Unavailable": "Service Unavailable",
"Gateway Timeout": "Gateway Timeout",
"Insufficient Storage": "Insufficient Storage",
"ok": "Ok",
"created": "Created",
"no_content": "No Content",
"reset_content": "Reset Content",
"partial_content": "Partial Content",
"bad_request": "Bad Request",
"unauthorized": "Unauthorized",
"payment_required": "Payment Required",
"forbidden": "Forbidden",
"not_found": "Not Found",
"method_not_allowed": "Method Not Allowed",
"not_acceptable": "Not Acceptable",
"proxy_authentication_required": "Proxy Authentication Required",
"request_timeout": "Request Timeout",
"conflict": "Conflict",
"gone": "Gone",
"length_required": "Length Required",
"precondition_failed": "Precondition Failed",
"request_entity_too_large": "Request Entity Too Large",
"request-uri_too_long": "Request-URI Too Long",
"unsupported_media_type": "Unsupported Media Type",
"requested_range_not_satisfiable": "Requested Range Not Satisfiable",
"expectation_failed": "Expectation Failed",
"im_a_teapot": "I'm a teapot",
"authentication_timeout": "Authentication Timeout",
"internal_server_error": "Internal Server Error",
"not_implemented": "Not Implemented",
"bad_gateway": "Bad Gateway",
"service_unavailable": "Service Unavailable",
"gateway_timeout": "Gateway Timeout",
"insufficient_storage": "Insufficient Storage"
};
constants.http_status = {
"0": 0,
"550": 550,
"551": 551,
"Unknown": 0,
"Internal JIO Error": 550,
"Internal Storage Error": 551,
"unknown": 0,
"internal_jio_error": 550,
"internal_storage_error": 551,
"200": 200,
"201": 201,
"204": 204,
"205": 205,
"206": 206,
"400": 400,
"401": 401,
"402": 402,
"403": 403,
"404": 404,
"405": 405,
"406": 406,
"407": 407,
"408": 408,
"409": 409,
"410": 410,
"411": 411,
"412": 412,
"413": 413,
"414": 414,
"415": 415,
"416": 416,
"417": 417,
"418": 418,
"419": 419,
"500": 500,
"501": 501,
"502": 502,
"503": 503,
"504": 504,
"507": 507,
"Ok": 200,
"OK": 200,
"Created": 201,
"No Content": 204,
"Reset Content": 205,
"Partial Content": 206,
"Bad Request": 400,
"Unauthorized": 401,
"Payment Required": 402,
"Forbidden": 403,
"Not Found": 404,
"Method Not Allowed": 405,
"Not Acceptable": 406,
"Proxy Authentication Required": 407,
"Request Timeout": 408,
"Conflict": 409,
"Gone": 410,
"Length Required": 411,
"Precondition Failed": 412,
"Request Entity Too Large": 413,
"Request-URI Too Long": 414,
"Unsupported Media Type": 415,
"Requested Range Not Satisfiable": 416,
"Expectation Failed": 417,
"I'm a teapot": 418,
"Authentication Timeout": 419,
"Internal Server Error": 500,
"Not Implemented": 501,
"Bad Gateway": 502,
"Service Unavailable": 503,
"Gateway Timeout": 504,
"Insufficient Storage": 507,
"ok": 200,
"created": 201,
"no_content": 204,
"reset_content": 205,
"partial_content": 206,
"bad_request": 400,
"unauthorized": 401,
"payment_required": 402,
"forbidden": 403,
"not_found": 404,
"method_not_allowed": 405,
"not_acceptable": 406,
"proxy_authentication_required": 407,
"request_timeout": 408,
"conflict": 409,
"gone": 410,
"length_required": 411,
"precondition_failed": 412,
"request_entity_too_large": 413,
"request-uri_too_long": 414,
"unsupported_media_type": 415,
"requested_range_not_satisfiable": 416,
"expectation_failed": 417,
"im_a_teapot": 418,
"authentication_timeout": 419,
"internal_server_error": 500,
"not_implemented": 501,
"bad_gateway": 502,
"service_unavailable": 503,
"gateway_timeout": 504,
"insufficient_storage": 507
};
constants.http_action = {
"0": "error",
"550": "error",
"551": "error",
"Unknown": "error",
"Internal JIO Error": "error",
"Internal Storage Error": "error",
"unknown": "error",
"internal_jio_error": "error",
"internal_storage_error": "error",
"200": "success",
"201": "success",
"204": "success",
"205": "success",
"206": "success",
"400": "error",
"401": "error",
"402": "error",
"403": "error",
"404": "error",
"405": "error",
"406": "error",
"407": "error",
"408": "error",
"409": "error",
"410": "error",
"411": "error",
"412": "error",
"413": "error",
"414": "error",
"415": "error",
"416": "error",
"417": "error",
"418": "error",
"419": "retry",
"500": "retry",
"501": "error",
"502": "error",
"503": "retry",
"504": "retry",
"507": "error",
"Ok": "success",
"OK": "success",
"Created": "success",
"No Content": "success",
"Reset Content": "success",
"Partial Content": "success",
"Bad Request": "error",
"Unauthorized": "error",
"Payment Required": "error",
"Forbidden": "error",
"Not Found": "error",
"Method Not Allowed": "error",
"Not Acceptable": "error",
"Proxy Authentication Required": "error",
"Request Timeout": "error",
"Conflict": "error",
"Gone": "error",
"Length Required": "error",
"Precondition Failed": "error",
"Request Entity Too Large": "error",
"Request-URI Too Long": "error",
"Unsupported Media Type": "error",
"Requested Range Not Satisfiable": "error",
"Expectation Failed": "error",
"I'm a teapot": "error",
"Authentication Timeout": "retry",
"Internal Server Error": "retry",
"Not Implemented": "error",
"Bad Gateway": "error",
"Service Unavailable": "retry",
"Gateway Timeout": "retry",
"Insufficient Storage": "error",
"ok": "success",
"created": "success",
"no_content": "success",
"reset_content": "success",
"partial_content": "success",
"bad_request": "error",
"unauthorized": "error",
"payment_required": "error",
"forbidden": "error",
"not_found": "error",
"method_not_allowed": "error",
"not_acceptable": "error",
"proxy_authentication_required": "error",
"request_timeout": "error",
"conflict": "error",
"gone": "error",
"length_required": "error",
"precondition_failed": "error",
"request_entity_too_large": "error",
"request-uri_too_long": "error",
"unsupported_media_type": "error",
"requested_range_not_satisfiable": "error",
"expectation_failed": "error",
"im_a_teapot": "error",
"authentication_timeout": "retry",
"internal_server_error": "retry",
"not_implemented": "error",
"bad_gateway": "error",
"service_unavailable": "retry",
"gateway_timeout": "retry",
"insufficient_storage": "error"
};
constants.content_type_re =
/^([a-z]+\/[a-zA-Z0-9\+\-\.]+)(?:\s*;\s*charset\s*=\s*([a-zA-Z0-9\-]+))?$/;
/**
* Function that does nothing
*/
constants.emptyFunction = function () {
return;
};
defaults.job_rule_conditions = {};
/**
* Adds some job rule conditions
*/
(function () {
/**
* Compare two jobs and test if they use the same storage description
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function sameStorageDescription(a, b) {
return uniqueJSONStringify(a.storage_spec) ===
uniqueJSONStringify(b.storage_spec);
}
/**
* Compare two jobs and test if they are writers
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function areWriters(a, b) {
return methodType(a.method) === 'writer' &&
methodType(b.method) === 'writer';
}
/**
* Compare two jobs and test if they use metadata only
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function useMetadataOnly(a, b) {
if (['post', 'put', 'get', 'remove', 'allDocs'].indexOf(a.method) === -1) {
return false;
}
if (['post', 'put', 'get', 'remove', 'allDocs'].indexOf(b.method) === -1) {
return false;
}
return true;
}
/**
* Compare two jobs and test if they are readers
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function areReaders(a, b) {
return methodType(a.method) === 'reader' &&
methodType(b.method) === 'reader';
}
/**
* Compare two jobs and test if their methods are the same
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function sameMethod(a, b) {
return a.method === b.method;
}
/**
* Compare two jobs and test if their document ids are the same
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function sameDocumentId(a, b) {
return a.kwargs._id === b.kwargs._id;
}
/**
* Test if the jobs have a document id.
*
* @param {Object} a The first job to test
* @param {Object} b The second job to test
* @return {Boolean} True if ids exist, else false
*/
function haveDocumentIds(a, b) {
if (typeof a.kwargs._id !== "string" || a.kwargs._id === "") {
return false;
}
if (typeof b.kwargs._id !== "string" || b.kwargs._id === "") {
return false;
}
return true;
}
/**
* Compare two jobs and test if their kwargs are equal
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function sameParameters(a, b) {
return uniqueJSONStringify(a.kwargs) ===
uniqueJSONStringify(b.kwargs);
}
/**
* Compare two jobs and test if their options are equal
*
* @param {Object} a The first job to compare
* @param {Object} b The second job to compare
* @return {Boolean} True if equal, else false
*/
function sameOptions(a, b) {
return uniqueJSONStringify(a.options) ===
uniqueJSONStringify(b.options);
}
defaults.job_rule_conditions = {
"sameStorageDescription": sameStorageDescription,
"areWriters": areWriters,
"areReaders": areReaders,
"useMetadataOnly": useMetadataOnly,
"sameMethod": sameMethod,
"sameDocumentId": sameDocumentId,
"sameParameters": sameParameters,
"sameOptions": sameOptions,
"haveDocumentIds": haveDocumentIds
};
}());
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global exports, Blob, FileReader, RSVP, hex_sha256, XMLHttpRequest,
constants */
/**
* Do not exports these tools unless they are not writable, not configurable.
*/
exports.util = {};
/**
* Inherits the prototype methods from one constructor into another. The
* prototype of `constructor` will be set to a new object created from
* `superConstructor`.
*
* @param {Function} constructor The constructor which inherits the super
* one
* @param {Function} superConstructor The super constructor
*/
function inherits(constructor, superConstructor) {
constructor.super_ = superConstructor;
constructor.prototype = Object.create(superConstructor.prototype, {
"constructor": {
"configurable": true,
"enumerable": false,
"writable": true,
"value": constructor
}
});
}
/**
* Clones jsonable object in deep
*
* @param {A} object The jsonable object to clone
* @return {A} The cloned object
*/
function jsonDeepClone(object) {
var tmp = JSON.stringify(object);
if (tmp === undefined) {
return undefined;
}
return JSON.parse(tmp);
}
exports.util.jsonDeepClone = jsonDeepClone;
/**
* Clones all native object in deep. Managed types: Object, Array, String,
* Number, Boolean, Function, null.
*
* It can also clone object which are serializable, like Date.
*
* To make a class serializable, you need to implement the `toJSON` function
* which returns a JSON representation of the object. The return value is used
* as first parameter of the object constructor.
*
* @param {A} object The object to clone
* @return {A} The cloned object
*/
function deepClone(object) {
var i, cloned;
if (Array.isArray(object)) {
cloned = [];
for (i = 0; i < object.length; i += 1) {
cloned[i] = deepClone(object[i]);
}
return cloned;
}
if (object === null) {
return null;
}
if (typeof object === 'object') {
if (Object.getPrototypeOf(object) === Object.prototype) {
cloned = {};
for (i in object) {
if (object.hasOwnProperty(i)) {
cloned[i] = deepClone(object[i]);
}
}
return cloned;
}
if (object instanceof Date) {
// XXX this block is to enable phantomjs and browsers compatibility with
// Date.prototype.toJSON when it is a invalid date. In phantomjs, it
// returns `"Invalid Date"` but in browsers it returns `null`. In
// browsers, give `null` as parameter to `new Date()` doesn't return an
// invalid date.
// Clonning date with `return new Date(object)` make problems on Firefox.
// I don't know why... (Tested on Firefox 23)
if (isFinite(object.getTime())) {
return new Date(object.toJSON());
}
return new Date("Invalid Date");
}
// clone serializable objects
if (typeof object.toJSON === 'function') {
return new (Object.getPrototypeOf(object).constructor)(object.toJSON());
}
// cannot clone
return object;
}
return object;
}
exports.util.deepClone = deepClone;
/**
* Update a dictionnary by adding/replacing key values from another dict.
* Enumerable values equal to undefined are also used.
*
* @param {Object} original The dict to update
* @param {Object} other The other dict
* @return {Object} The updated original dict
*/
function dictUpdate(original, other) {
var k;
for (k in other) {
if (other.hasOwnProperty(k)) {
original[k] = other[k];
}
}
return original;
}
exports.util.dictUpdate = dictUpdate;
/**
* Like 'dict.clear()' in python. Delete all dict entries.
*
* @method dictClear
* @param {Object} self The dict to clear
*/
function dictClear(dict) {
var i;
for (i in dict) {
if (dict.hasOwnProperty(i)) {
delete dict[i];
// dictClear(dict);
// break;
}
}
}
exports.util.dictClear = dictClear;
/**
* Filter a dict to keep only values which keys are in `keys` list.
*
* @param {Object} dict The dict to filter
* @param {Array} keys The key list to keep
*/
function dictFilter(dict, keys) {
var i, buffer = [];
for (i = 0; i < keys.length; i += 1) {
buffer[i] = dict[keys[i]];
}
dictClear(dict);
for (i = 0; i < buffer.length; i += 1) {
dict[keys[i]] = buffer[i];
}
}
exports.util.dictFilter = dictFilter;
/**
* Gets all elements of an array and classifies them in a dict of array.
* Dict keys are element types, and values are list of element of type 'key'.
*
* @param {Array} array The array of elements to pop
* @return {Object} The type dict
*/
function arrayValuesToTypeDict(array) {
var i, type, types = {};
for (i = 0; i < array.length; i += 1) {
type = Array.isArray(array[i]) ? 'array' : typeof array[i];
if (!types[type]) {
types[type] = [array[i]];
} else {
types[type][types[type].length] = array[i];
}
}
return types;
}
/**
* An Universal Unique ID generator
*
* @return {String} The new UUID.
*/
function generateUuid() {
function S4() {
return ('0000' + Math.floor(
Math.random() * 0x10000 /* 65536 */
).toString(16)).slice(-4);
}
return S4() + S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + S4() + S4();
}
exports.util.generateUuid = generateUuid;
/**
* JSON stringify a value. Dict keys are sorted in order to make a kind of
* deepEqual thanks to a simple strict equal string comparison.
*
* JSON.stringify({"a": "b", "c": "d"}) ===
* JSON.stringify({"c": "d", "a": "b"}) // false
*
* deepEqual({"a": "b", "c": "d"}, {"c": "d", "a": "b"}); // true
*
* uniqueJSONStringify({"a": "b", "c": "d"}) ===
* uniqueJSONStringify({"c": "d", "a": "b"}) // true
*
* @param {Any} value The value to stringify
* @param {Function} [replacer] A function to replace values during parse
*/
function uniqueJSONStringify(value, replacer) {
function subStringify(value, key) {
var i, res;
if (typeof replacer === 'function') {
value = replacer(key, value);
}
if (Array.isArray(value)) {
res = [];
for (i = 0; i < value.length; i += 1) {
res[res.length] = subStringify(value[i], i);
if (res[res.length - 1] === undefined) {
res[res.length - 1] = 'null';
}
}
return '[' + res.join(',') + ']';
}
if (typeof value === 'object' && value !== null &&
typeof value.toJSON !== 'function') {
res = [];
for (i in value) {
if (value.hasOwnProperty(i)) {
res[res.length] = subStringify(value[i], i);
if (res[res.length - 1] !== undefined) {
res[res.length - 1] = JSON.stringify(i) + ":" + res[res.length - 1];
} else {
res.length -= 1;
}
}
}
res.sort();
return '{' + res.join(',') + '}';
}
return JSON.stringify(value);
}
return subStringify(value, '');
}
exports.util.uniqueJSONStringify = uniqueJSONStringify;
function makeBinaryStringDigest(string) {
return 'sha256-' + hex_sha256(string);
}
exports.util.makeBinaryStringDigest = makeBinaryStringDigest;
function readBlobAsBinaryString(blob) {
var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("load", resolve);
fr.addEventListener("error", reject);
fr.addEventListener("progress", notify);
fr.readAsBinaryString(blob);
}, function () {
fr.abort();
});
}
exports.util.readBlobAsBinaryString = readBlobAsBinaryString;
function readBlobAsArrayBuffer(blob) {
var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("load", resolve);
fr.addEventListener("error", reject);
fr.addEventListener("progress", notify);
fr.readAsArrayBuffer(blob);
}, function () {
fr.abort();
});
}
exports.util.readBlobAsArrayBuffer = readBlobAsArrayBuffer;
function readBlobAsText(blob) {
var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("load", resolve);
fr.addEventListener("error", reject);
fr.addEventListener("progress", notify);
fr.readAsText(blob);
}, function () {
fr.abort();
});
}
exports.util.readBlobAsText = readBlobAsText;
/**
* Send request with XHR and return a promise. xhr.onload: The promise is
* resolve when the status code is lower than 400 with the xhr object as first
* parameter. xhr.onerror: reject with xhr object as first
* parameter. xhr.onprogress: notifies the xhr object.
*
* @param {Object} param The parameters
* @param {String} [param.type="GET"] The request method
* @param {String} [param.dataType=""] The data type to retrieve
* @param {String} param.url The url
* @param {Any} [param.data] The data to send
* @param {Function} [param.beforeSend] A function called just before send
* request. The first parameter of this function is the XHR object.
* @return {Promise} The promise
*/
function ajax(param) {
var xhr = new XMLHttpRequest();
return new RSVP.Promise(function (resolve, reject, notify) {
var k;
xhr.open(param.type || "GET", param.url, true);
xhr.responseType = param.dataType || "";
if (typeof param.headers === 'object' && param.headers !== null) {
for (k in param.headers) {
if (param.headers.hasOwnProperty(k)) {
xhr.setRequestHeader(k, param.headers[k]);
}
}
}
xhr.addEventListener("load", function (e) {
if (e.target.status >= 400) {
return reject(e);
}
resolve(e);
});
xhr.addEventListener("error", reject);
xhr.addEventListener("progress", notify);
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
}
xhr.send(param.data);
}, function () {
xhr.abort();
});
}
exports.util.ajax = ajax;
/**
* Acts like `Array.prototype.concat` but does not create a copy of the original
* array. It extends the original array and return it.
*
* @param {Array} array The array to extend
* @param {Any} [args]* Values to add in the array
* @return {Array} The original array
*/
function arrayExtend(array) { // args*
var i, j;
for (i = 1; i < arguments.length; i += 1) {
if (Array.isArray(arguments[i])) {
for (j = 0; j < arguments[i].length; j += 1) {
array[array.length] = arguments[i][j];
}
} else {
array[array.length] = arguments[i];
}
}
return array;
}
exports.util.arrayExtend = arrayExtend;
/**
* Acts like `Array.prototype.concat` but does not create a copy of the original
* array. It extends the original array from a specific position and return it.
*
* @param {Array} array The array to extend
* @param {Number} position The position where to extend
* @param {Any} [args]* Values to add in the array
* @return {Array} The original array
*/
function arrayInsert(array, position) { // args*
var array_part = array.splice(position, array.length - position);
arrayExtend.apply(null, arrayExtend([
], [array], Array.prototype.slice.call(arguments, 2)));
return arrayExtend(array, array_part);
}
exports.util.arrayInsert = arrayInsert;
/**
* Guess if the method is a writer or a reader.
*
* @param {String} method The method name
* @return {String} "writer", "reader" or "unknown"
*/
function methodType(method) {
switch (method) {
case "post":
case "put":
case "putAttachment":
case "remove":
case "removeAttachment":
case "repair":
return 'writer';
case "get":
case "getAttachment":
case "allDocs":
case "check":
return 'reader';
default:
return 'unknown';
}
}
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global secureMethods, exports, console */
/**
* Inspired by nodejs EventEmitter class
* http://nodejs.org/api/events.html
*
* When an EventEmitter instance experiences an error, the typical action is
* to emit an 'error' event. Error events are treated as a special case in
* node. If there is no listener for it, then the default action throws the
* exception again.
*
* All EventEmitters emit the event 'newListener' when new listeners are added
* and 'removeListener' when a listener is removed.
*
* @class EventEmitter
* @constructor
*/
function EventEmitter() {
this._events = {};
this._maxListeners = 10;
}
/**
* Adds a listener to the end of the listeners array for the specified
* event.
*
* @method addListener
* @param {String} event The event name
* @param {Function} listener The listener callback
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.addListener = function (event, listener) {
var listener_list;
if (typeof listener !== "function") {
return this;
}
this.emit("newListener", event, listener);
listener_list = this._events[event];
if (listener_list === undefined) {
this._events[event] = listener;
listener_list = listener;
} else if (typeof listener_list === "function") {
this._events[event] = [listener_list, listener];
listener_list = this._events[event];
} else {
listener_list[listener_list.length] = listener;
}
if (this._maxListeners > 0 &&
typeof listener_list !== "function" &&
listener_list.length > this._maxListeners &&
listener_list.warned !== true) {
console.warn("warning: possible EventEmitter memory leak detected. " +
listener_list.length + " listeners added. " +
"Use emitter.setMaxListeners() to increase limit.");
listener_list.warned = true;
}
return this;
};
/**
* #crossLink "EventEmitter/addListener:method"
*
* @method on
*/
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
/**
* Adds a one time listener for the event. This listener is invoked only the
* next time the event is fired, after which it is removed.
*
* @method once
* @param {String} event The event name
* @param {Function} listener The listener callback
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.once = function (event, listener) {
var that = this, wrapper = function () {
that.removeListener(event, wrapper);
listener.apply(that, arguments);
};
wrapper.original = listener;
return that.on(event, wrapper);
};
/**
* Remove a listener from the listener array for the specified event.
* Caution: changes array indices in the listener array behind the listener
*
* @method removeListener
* @param {String} event The event name
* @param {Function} listener The listener callback
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.removeListener = function (event, listener) {
var listener_list = this._events[event], i;
if (listener_list) {
if (typeof listener_list === "function") {
if (listener_list === listener || listener_list.original === listener) {
delete this._events[event];
}
return this;
}
for (i = 0; i < listener_list.length; i += 1) {
if (listener_list[i] === listener ||
listener_list[i].original === listener) {
listener_list.splice(i, 1);
this.emit("removeListener", event, listener);
break;
}
}
if (listener_list.length === 1) {
this._events[event] = listener_list[0];
}
if (listener_list.length === 0) {
this._events[event] = undefined;
}
}
return this;
};
/**
* Removes all listeners, or those of the specified event.
*
* @method removeAllListeners
* @param {String} event The event name (optional)
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.removeAllListeners = function (event) {
var key;
if (event === undefined) {
for (key in this._events) {
if (this._events.hasOwnProperty(key)) {
delete this._events[key];
}
}
return this;
}
delete this._events[event];
return this;
};
/**
* By default EventEmitters will print a warning if more than 10 listeners
* are added for a particular event. This is a useful default which helps
* finding memory leaks. Obviously not all Emitters should be limited to 10.
* This function allows that to be increased. Set to zero for unlimited.
*
* @method setMaxListeners
* @param {Number} max_listeners The maximum of listeners
*/
EventEmitter.prototype.setMaxListeners = function (max_listeners) {
this._maxListeners = max_listeners;
};
/**
* Execute each of the listeners in order with the supplied arguments.
*
* @method emit
* @param {String} event The event name
* @param {Any} [args]* The listener argument to give
* @return {Boolean} true if event had listeners, false otherwise.
*/
EventEmitter.prototype.emit = function (event) {
var i, argument_list, listener_list;
listener_list = this._events[event];
if (typeof listener_list === 'function') {
listener_list = [listener_list];
} else if (Array.isArray(listener_list)) {
listener_list = listener_list.slice();
} else {
return false;
}
argument_list = Array.prototype.slice.call(arguments, 1);
for (i = 0; i < listener_list.length; i += 1) {
try {
listener_list[i].apply(this, argument_list);
} catch (e) {
if (this.listeners("error").length > 0) {
this.emit("error", e);
break;
}
throw e;
}
}
return true;
};
/**
* Returns an array of listeners for the specified event.
*
* @method listeners
* @param {String} event The event name
* @return {Array} The array of listeners
*/
EventEmitter.prototype.listeners = function (event) {
return (typeof this._events[event] === 'function' ?
[this._events[event]] : (this._events[event] || []).slice());
};
/**
* Static method; Return the number of listeners for a given event.
*
* @method listenerCount
* @static
* @param {EventEmitter} emitter The event emitter
* @param {String} event The event name
* @return {Number} The number of listener
*/
EventEmitter.listenerCount = function (emitter, event) {
return emitter.listeners(event).length;
};
exports.EventEmitter = EventEmitter;
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global EventEmitter, deepClone, inherits, exports */
/*global enableRestAPI, enableRestParamChecker, enableJobMaker, enableJobRetry,
enableJobReference, enableJobChecker, enableJobQueue, enableJobRecovery,
enableJobTimeout, enableJobExecuter */
function JIO(storage_spec, options) {
JIO.super_.call(this);
var shared = new EventEmitter();
shared.storage_spec = deepClone(storage_spec);
if (options === undefined) {
options = {};
} else if (typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError("JIO(): Optional argument 2 is not of type 'object'");
}
enableRestAPI(this, shared, options);
enableRestParamChecker(this, shared, options);
enableJobMaker(this, shared, options);
enableJobReference(this, shared, options);
enableJobRetry(this, shared, options);
enableJobTimeout(this, shared, options);
enableJobChecker(this, shared, options);
enableJobQueue(this, shared, options);
enableJobRecovery(this, shared, options);
enableJobExecuter(this, shared, options);
shared.emit('load');
}
inherits(JIO, EventEmitter);
JIO.createInstance = function (storage_spec, options) {
return new JIO(storage_spec, options);
};
exports.JIO = JIO;
exports.createJIO = JIO.createInstance;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global deepClone, dictFilter, uniqueJSONStringify */
/**
* Tool to manipulate a list of object containing at least one property: 'id'.
* Id must be a number > 0.
*
* @class JobQueue
* @constructor
* @param {Workspace} workspace The workspace where to store
* @param {String} namespace The namespace to use in the workspace
* @param {Array} job_keys An array of job keys to store
* @param {Array} [array] An array of object
*/
function JobQueue(workspace, namespace, job_keys, array) {
this._workspace = workspace;
this._namespace = namespace;
this._job_keys = job_keys;
if (Array.isArray(array)) {
this._array = array;
} else {
this._array = [];
}
}
/**
* Store the job queue into the workspace.
*
* @method save
*/
JobQueue.prototype.save = function () {
var i, job_queue = deepClone(this._array);
for (i = 0; i < job_queue.length; i += 1) {
dictFilter(job_queue[i], this._job_keys);
}
if (this._array.length === 0) {
this._workspace.removeItem(this._namespace);
} else {
this._workspace.setItem(
this._namespace,
uniqueJSONStringify(job_queue)
);
}
return this;
};
/**
* Loads the job queue from the workspace.
*
* @method load
*/
JobQueue.prototype.load = function () {
var job_list;
try {
job_list = JSON.parse(this._workspace.getItem(this._namespace));
} catch (ignore) {}
if (!Array.isArray(job_list)) {
job_list = [];
}
this.clear();
new JobQueue(job_list).repair();
this.update(job_list);
return this;
};
/**
* Returns the array version of the job queue
*
* @method asArray
* @return {Array} The job queue as array
*/
JobQueue.prototype.asArray = function () {
return this._array;
};
/**
* Removes elements which are not objects containing at least 'id' property.
*
* @method repair
*/
JobQueue.prototype.repair = function () {
var i, job;
for (i = 0; i < this._array.length; i += 1) {
job = this._array[i];
if (typeof job !== 'object' || Array.isArray(job) ||
typeof job.id !== 'number' || job.id <= 0) {
this._array.splice(i, 1);
i -= 1;
}
}
};
/**
* Post an object and generate an id
*
* @method post
* @param {Object} job The job object
* @return {Number} The generated id
*/
JobQueue.prototype.post = function (job) {
var i, next = 1;
// get next id
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id >= next) {
next = this._array[i].id + 1;
}
}
job.id = next;
this._array[this._array.length] = deepClone(job);
return this;
};
/**
* Put an object to the list. If an object contains the same id, it is replaced
* by the new one.
*
* @method put
* @param {Object} job The job object with an id
*/
JobQueue.prototype.put = function (job) {
var i;
if (typeof job.id !== 'number' || job.id <= 0) {
throw new TypeError("JobQueue().put(): Job id should be a positive number");
}
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id === job.id) {
break;
}
}
this._array[i] = deepClone(job);
return this;
};
/**
* Puts some object into the list. Update object with the same id, and add
* unreferenced one.
*
* @method update
* @param {Array} job_list A list of new jobs
*/
JobQueue.prototype.update = function (job_list) {
var i, j = 0, j_max, index = {}, next = 1, job, post_list = [];
j_max = this._array.length;
for (i = 0; i < job_list.length; i += 1) {
if (typeof job_list[i].id !== 'number' || job_list[i].id <= 0) {
// this job has no id, it has to be post
post_list[post_list.length] = job_list[i];
} else {
job = deepClone(job_list[i]);
if (index[job.id] !== undefined) {
// this job is on the list, update
this._array[index[job.id]] = job;
} else if (j === j_max) {
// this job is not on the list, update
this._array[this._array.length] = job;
} else {
// don't if the job is there or not
// searching same job in the original list
while (j < j_max) {
// references visited job
index[this._array[j].id] = j;
if (this._array[j].id >= next) {
next = this._array[j].id + 1;
}
if (this._array[j].id === job.id) {
// found on the list, just update
this._array[j] = job;
break;
}
j += 1;
}
if (j === j_max) {
// not found on the list, add to the end
this._array[this._array.length] = job;
} else {
// found on the list, already updated
j += 1;
}
}
if (job.id >= next) {
next = job.id + 1;
}
}
}
for (i = 0; i < post_list.length; i += 1) {
// adding job without id
post_list[i].id = next;
next += 1;
this._array[this._array.length] = deepClone(post_list[i]);
}
return this;
};
/**
* Get an object from an id. Returns undefined if not found
*
* @method get
* @param {Number} id The job id
* @return {Object} The job or undefined
*/
JobQueue.prototype.get = function (id) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id === id) {
return deepClone(this._array[i]);
}
}
};
/**
* Removes an object from an id
*
* @method remove
* @param {Number} id The job id
*/
JobQueue.prototype.remove = function (id) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id === id) {
this._array.splice(i, 1);
return true;
}
}
return false;
};
/**
* Clears the list.
*
* @method clear
*/
JobQueue.prototype.clear = function () {
this._array.length = 0;
return this;
};
/*jslint indent: 2, maxlen: 80, sloppy: true */
/*global localStorage */
// keywords: js, javascript, store on local storage as array
function LocalStorageArray(namespace) {
var index, next;
function nextId() {
var i = next;
next += 1;
return i;
}
this.length = function () {
return index.length;
};
this.truncate = function (length) {
var i;
if (length === index.length) {
return this;
}
if (length > index.length) {
index.length = length;
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
}
while (length < index.length) {
i = index.pop();
if (i !== undefined && i !== null) {
delete localStorage[namespace + '.' + i];
}
}
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
};
this.get = function (i) {
return JSON.parse(localStorage[namespace + '.' + index[i]] || 'null');
};
this.set = function (i, value) {
if (index[i] === undefined || index[i] === null) {
index[i] = nextId();
localStorage[namespace + '.' + index[i]] = JSON.stringify(value);
localStorage[namespace + '.index'] = JSON.stringify(index);
} else {
localStorage[namespace + '.' + index[i]] = JSON.stringify(value);
}
return this;
};
this.append = function (value) {
index[index.length] = nextId();
localStorage[namespace + '.' + index[index.length - 1]] =
JSON.stringify(value);
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
};
this.pop = function (i) {
var value, key;
if (i === undefined || i === null) {
key = namespace + '.' + index[index.length - 1];
index.pop();
} else {
if (i < 0 || i >= index.length) {
return null;
}
key = namespace + '.' + i;
index.splice(i, 1);
}
value = localStorage[key];
if (index.length === 0) {
delete localStorage[namespace + '.index'];
} else {
localStorage[namespace + '.index'] = JSON.stringify(index);
}
delete localStorage[key];
return JSON.parse(value || 'null');
};
this.clear = function () {
var i;
for (i = 0; i < index.length; i += 1) {
delete localStorage[namespace + '.' + index[i]];
}
index = [];
delete localStorage[namespace + '.index'];
return this;
};
this.reload = function () {
var i;
index = JSON.parse(localStorage[namespace + '.index'] || '[]');
next = 0;
for (i = 0; i < index.length; i += 1) {
if (next < index[i]) {
next = index[i];
}
}
return this;
};
this.toArray = function () {
var i, list = [];
for (i = 0; i < index.length; i += 1) {
list[list.length] = this.get(i);
}
return list;
};
this.update = function (list) {
if (!Array.isArray(list)) {
throw new TypeError("LocalStorageArray().saveArray(): " +
"Argument 1 is not of type 'array'");
}
var i, location;
// update previous values
for (i = 0; i < list.length; i += 1) {
location = index[i];
if (location === undefined || location === null) {
location = nextId();
index[i] = location;
}
localStorage[namespace + '.' + location] =
JSON.stringify(list[i]);
}
// remove last ones
while (list.length < index.length) {
location = index.pop();
if (location !== undefined && location !== null) {
delete localStorage[namespace + '.' + location];
}
}
// store index
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
};
this.reload();
}
LocalStorageArray.saveArray = function (namespace, list) {
if (!Array.isArray(list)) {
throw new TypeError("LocalStorageArray.saveArray(): " +
"Argument 2 is not of type 'array'");
}
var local_storage_array = new LocalStorageArray(namespace).clear(), i;
for (i = 0; i < list.length; i += 1) {
local_storage_array.append(list[i]);
}
};
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global exports, deepClone, jsonDeepClone */
/**
* A class to manipulate metadata
*
* @class Metadata
* @constructor
*/
function Metadata(metadata) {
if (arguments.length > 0) {
if (metadata === null || typeof metadata !== 'object' ||
Array.isArray(metadata)) {
throw new TypeError("Metadata(): Optional argument 1 is not an object");
}
this._dict = metadata;
} else {
this._dict = {};
}
}
Metadata.prototype.format = function () {
return this.update(this._dict);
};
Metadata.prototype.check = function () {
var k;
for (k in this._dict) {
if (this._dict.hasOwnProperty(k)) {
if (k[0] !== '_') {
if (!Metadata.checkValue(this._dict[k])) {
return false;
}
}
}
}
return true;
};
Metadata.prototype.update = function (metadata) {
var k;
for (k in metadata) {
if (metadata.hasOwnProperty(k)) {
if (k[0] === '_') {
this._dict[k] = jsonDeepClone(metadata[k]);
} else {
this._dict[k] = Metadata.normalizeValue(metadata[k]);
}
if (this._dict[k] === undefined) {
delete this._dict[k];
}
}
}
return this;
};
Metadata.prototype.get = function (key) {
return this._dict[key];
};
Metadata.prototype.add = function (key, value) {
var i;
if (key[0] === '_') {
return this;
}
if (this._dict[key] === undefined) {
this._dict[key] = Metadata.normalizeValue(value);
if (this._dict[key] === undefined) {
delete this._dict[key];
}
return this;
}
if (!Array.isArray(this._dict[key])) {
this._dict[key] = [this._dict[key]];
}
value = Metadata.normalizeValue(value);
if (value === undefined) {
return this;
}
if (!Array.isArray(value)) {
value = [value];
}
for (i = 0; i < value.length; i += 1) {
this._dict[key][this._dict[key].length] = value[i];
}
return this;
};
Metadata.prototype.set = function (key, value) {
if (key[0] === '_') {
this._dict[key] = JSON.parse(JSON.stringify(value));
} else {
this._dict[key] = Metadata.normalizeValue(value);
}
if (this._dict[key] === undefined) {
delete this._dict[key];
}
return this;
};
Metadata.prototype.remove = function (key) {
delete this._dict[key];
return this;
};
Metadata.prototype.forEach = function (key, fun) {
var k, i, value, that = this;
if (typeof key === 'function') {
fun = key;
key = undefined;
}
function forEach(key, fun) {
value = that._dict[key];
if (!Array.isArray(that._dict[key])) {
value = [value];
}
for (i = 0; i < value.length; i += 1) {
if (typeof value[i] === 'object') {
fun.call(that, key, deepClone(value[i]), i);
} else {
fun.call(that, key, {'content': value[i]}, i);
}
}
}
if (key === undefined) {
for (k in this._dict) {
if (this._dict.hasOwnProperty(k)) {
forEach(k, fun);
}
}
} else {
forEach(key, fun);
}
return this;
};
Metadata.prototype.toFullDict = function () {
var dict = {};
this.forEach(function (key, value, index) {
dict[key] = dict[key] || [];
dict[key][index] = value;
});
return dict;
};
Metadata.asJsonableValue = function (value) {
switch (typeof value) {
case 'string':
case 'boolean':
return value;
case 'number':
if (isFinite(value)) {
return value;
}
return null;
case 'object':
if (value === null) {
return null;
}
if (value instanceof Date) {
// XXX this block is to enable phantomjs and browsers compatibility with
// Date.prototype.toJSON when it is a invalid date. In phantomjs, it
// returns `"Invalid Date"` but in browsers it returns `null`. Here, the
// result will always be `null`.
if (isNaN(value.getTime())) {
return null;
}
}
if (typeof value.toJSON === 'function') {
return Metadata.asJsonableValue(value.toJSON());
}
return value; // dict, array
// case 'undefined':
default:
return null;
}
};
Metadata.isDict = function (o) {
return typeof o === 'object' &&
Object.getPrototypeOf(o || []) === Object.prototype;
};
Metadata.isContent = function (c) {
return typeof c === 'string' ||
(typeof c === 'number' && isFinite(c)) ||
typeof c === 'boolean';
};
Metadata.contentValue = function (value) {
if (Array.isArray(value)) {
return Metadata.contentValue(value[0]);
}
if (Metadata.isDict(value)) {
return value.content;
}
return value;
};
Metadata.normalizeArray = function (value) {
var i;
value = value.slice();
i = 0;
while (i < value.length) {
value[i] = Metadata.asJsonableValue(value[i]);
if (Metadata.isDict(value[i])) {
value[i] = Metadata.normalizeObject(value[i]);
if (value[i] === undefined) {
value.splice(i, 1);
} else {
i += 1;
}
} else if (Metadata.isContent(value[i])) {
i += 1;
} else {
value.splice(i, 1);
}
}
if (value.length === 0) {
return;
}
if (value.length === 1) {
return value[0];
}
return value;
};
Metadata.normalizeObject = function (value) {
var i, count = 0, ok = false, new_value = {};
for (i in value) {
if (value.hasOwnProperty(i)) {
value[i] = Metadata.asJsonableValue(value[i]);
if (Metadata.isContent(value[i])) {
new_value[i] = value[i];
if (new_value[i] === undefined) {
delete new_value[i];
}
count += 1;
if (i === 'content') {
ok = true;
}
}
}
}
if (ok === false) {
return;
}
if (count === 1) {
return new_value.content;
}
return new_value;
};
Metadata.normalizeValue = function (value) {
value = Metadata.asJsonableValue(value);
if (Metadata.isContent(value)) {
return value;
}
if (Array.isArray(value)) {
return Metadata.normalizeArray(value);
}
if (Metadata.isDict(value)) {
return Metadata.normalizeObject(value);
}
};
Metadata.checkArray = function (value) {
var i;
for (i = 0; i < value.length; i += 1) {
if (Metadata.isDict(value[i])) {
if (!Metadata.checkObject(value[i])) {
return false;
}
} else if (!Metadata.isContent(value[i])) {
return false;
}
}
return true;
};
Metadata.checkObject = function (value) {
var i, ok = false;
for (i in value) {
if (value.hasOwnProperty(i)) {
if (Metadata.isContent(value[i])) {
if (i === 'content') {
ok = true;
}
} else {
return false;
}
}
}
if (ok === false) {
return false;
}
return true;
};
Metadata.checkValue = function (value) {
if (Metadata.isContent(value)) {
return true;
}
if (Array.isArray(value)) {
return Metadata.checkArray(value);
}
if (Metadata.isDict(value)) {
return Metadata.checkObject(value);
}
return false;
};
exports.Metadata = Metadata;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global */
/**
* An array that contain object (or array) references.
*
* @class ReferenceArray
* @constructor
* @param {array} [array] The array where to work on
*/
function ReferenceArray(array) {
if (Array.isArray(array)) {
this._array = array;
} else {
this._array = [];
}
}
/**
* Returns the array version of the job queue
*
* @method asArray
* @return {Array} The job queue as array
*/
ReferenceArray.prototype.asArray = function () {
return this._array;
};
/**
* Returns the index of the object
*
* @method indexOf
* @param {Object} object The object to search
*/
ReferenceArray.prototype.indexOf = function (object) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i] === object) {
return i;
}
}
return -1;
};
/**
* Put an object to the list. If an object already exists, do nothing.
*
* @method put
* @param {Object} object The object to add
*/
ReferenceArray.prototype.put = function (object) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i] === object) {
return false;
}
}
this._array[i] = object;
return true;
};
/**
* Removes an object from the list
*
* @method remove
* @param {Object} object The object to remove
*/
ReferenceArray.prototype.remove = function (object) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i] === object) {
this._array.splice(i, 1);
return true;
}
}
return false;
};
/**
* Clears the list.
*
* @method clear
*/
ReferenceArray.prototype.clear = function () {
this._array.length = 0;
return this;
};
/*jslint indent: 2, maxlen: 80, sloppy: true */
/*global exports, defaults */
function Storage() { // (storage_spec, util)
return undefined; // this is a constructor
}
// end Storage
function createStorage(storage_spec, util) {
if (typeof storage_spec.type !== 'string') {
throw new TypeError("Invalid storage description");
}
if (!defaults.storage_types[storage_spec.type]) {
throw new TypeError("Unknown storage '" + storage_spec.type + "'");
}
return new defaults.storage_types[storage_spec.type](storage_spec, util);
}
function addStorage(type, Constructor) {
// var proto = {};
if (typeof type !== 'string') {
throw new TypeError("jIO.addStorage(): Argument 1 is not of type 'string'");
}
if (typeof Constructor !== 'function') {
throw new TypeError("jIO.addStorage(): " +
"Argument 2 is not of type 'function'");
}
if (defaults.storage_types[type]) {
throw new TypeError("jIO.addStorage(): Storage type already exists");
}
// dictUpdate(proto, Constructor.prototype);
// inherits(Constructor, Storage);
// dictUpdate(Constructor.prototype, proto);
defaults.storage_types[type] = Constructor;
}
exports.addStorage = addStorage;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global */
/**
* A class that acts like localStorage on a simple object.
*
* Like localStorage, the object will contain only strings.
*
* @class Workspace
* @constructor
*/
function Workspace(object) {
this._object = object;
}
// // Too dangerous, never use it
// /**
// * Empty the entire space.
// *
// * @method clear
// */
// Workspace.prototype.clear = function () {
// var k;
// for (k in this._object) {
// if (this._object.hasOwnProperty(k)) {
// delete this._object;
// }
// }
// return undefined;
// };
/**
* Get an item from the space. If the value does not exists, it returns
* null. Else, it returns the string value.
*
* @method getItem
* @param {String} key The location where to get the item
* @return {String} The item
*/
Workspace.prototype.getItem = function (key) {
return this._object[key] === undefined ? null : this._object[key];
};
/**
* Set an item into the space. The value to store is converted to string before.
*
* @method setItem
* @param {String} key The location where to set the item
* @param {Any} value The value to store
*/
Workspace.prototype.setItem = function (key, value) {
if (value === undefined) {
this._object[key] = 'undefined';
} else if (value === null) {
this._object[key] = 'null';
} else {
this._object[key] = value.toString();
}
return undefined;
};
/**
* Removes an item from the space.
*
* @method removeItem
* @param {String} key The location where to remove the item
*/
Workspace.prototype.removeItem = function (key) {
delete this._object[key];
return undefined;
};
/*jslint indent: 2, maxlen: 80, sloppy: true */
/*global exports, defaults */
// adds
// - jIO.addJobRuleCondition(name, function)
function addJobRuleCondition(name, method) {
if (typeof name !== 'string') {
throw new TypeError("jIO.addJobRuleAction(): " +
"Argument 1 is not of type 'string'");
}
if (typeof method !== 'function') {
throw new TypeError("jIO.addJobRuleAction(): " +
"Argument 2 is not of type 'function'");
}
if (defaults.job_rule_conditions[name]) {
throw new TypeError("jIO.addJobRuleAction(): Action already exists");
}
defaults.job_rule_conditions[name] = method;
}
exports.addJobRuleCondition = addJobRuleCondition;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global constants, dictUpdate, deepClone */
function restCommandRejecter(param, args) {
// reject(status, reason, message, {"custom": "value"});
// reject(status, reason, {..});
// reject(status, {..});
var arg, current_priority, priority = [
// 0 - custom parameter values
{},
// 1 - default values
{
"status": constants.http_status.unknown,
"statusText": constants.http_status_text.unknown,
"message": "Command failed",
"reason": "unknown"
},
// 2 - status, reason, message properties
{},
// 3 - status, reason, message parameters
{},
// 4 - never change
{"result": "error", "method": param.method}
];
args = Array.prototype.slice.call(args);
arg = args.shift();
// priority 4 - never change
current_priority = priority[4];
if (param.kwargs._id) {
current_priority.id = param.kwargs._id;
}
if (/Attachment$/.test(param.method)) {
current_priority.attachment = param.kwargs._attachment;
}
// priority 3 - status, reason, message parameters
current_priority = priority[3];
// parsing first parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
// first parameter is mandatory
current_priority.status = arg;
arg = args.shift();
}
// parsing second parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
if (arg !== undefined) {
current_priority.reason = arg;
}
arg = args.shift();
}
// parsing third parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
if (arg !== undefined) {
current_priority.message = arg;
}
arg = args.shift();
}
// parsing fourth parameter if is an object
if (typeof arg === 'object' && arg !== null && !Array.isArray(arg)) {
// priority 0 - custom values
dictUpdate(priority[0], arg);
// priority 2 - status, reason, message properties
current_priority = priority[2];
if (arg.hasOwnProperty('reason')) {
current_priority.reason = arg.reason;
}
if (arg.hasOwnProperty('message')) {
current_priority.message = arg.message;
}
if ((arg.statusText || arg.status >= 0)) {
current_priority.status = arg.statusText || arg.status;
}
if (arg instanceof Error) {
current_priority.reason = arg.message || "";
current_priority.error = arg.name;
}
}
// merge priority dicts
for (current_priority = priority.length - 1;
current_priority > 0;
current_priority -= 1) {
dictUpdate(priority[current_priority - 1], priority[current_priority]);
}
priority = priority[0];
// check status
priority.statusText = constants.http_status_text[priority.status];
if (priority.statusText === undefined) {
return restCommandRejecter(param, [
// can create infernal loop if 'internal_storage_error' is not defined in
// the constants
'internal_storage_error',
'invalid response',
'Unknown status "' + priority.status + '"'
]);
}
priority.status = constants.http_status[priority.statusText];
// set default priority error if not already set
if (priority.error === undefined) {
priority.error = priority.statusText.toLowerCase().replace(/ /g, '_').
replace(/[^_a-z]/g, '');
}
return param.solver.reject(deepClone(priority));
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global constants, methodType, dictUpdate, Blob, deepClone,
restCommandRejecter */
function restCommandResolver(param, args) {
// resolve('ok', {"custom": "value"});
// resolve(200, {...});
// resolve({...});
var arg, current_priority, priority = [
// 0 - custom parameter values
{},
// 1 - default values
{},
// 2 - status property
{},
// 3 - status parameter
{},
// 4 - never change
{"result": "success", "method": param.method}
];
args = Array.prototype.slice.call(args);
arg = args.shift();
// priority 3 - never change
current_priority = priority[3];
if (param.kwargs._id) {
current_priority.id = param.kwargs._id;
}
if (/Attachment$/.test(param.method)) {
current_priority.attachment = param.kwargs._attachment;
}
// priority 1 - default values
current_priority = priority[1];
if (param.method === 'post') {
current_priority.status = constants.http_status.created;
current_priority.statusText = constants.http_status_text.created;
} else if (methodType(param.method) === "writer" ||
param.method === "check") {
current_priority.status = constants.http_status.no_content;
current_priority.statusText = constants.http_status_text.no_content;
} else {
current_priority.status = constants.http_status.ok;
current_priority.statusText = constants.http_status_text.ok;
}
// priority 3 - status parameter
current_priority = priority[3];
// parsing first parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
if (arg !== undefined) {
current_priority.status = arg;
}
arg = args.shift();
}
// parsing second parameter if is an object
if (typeof arg === 'object' && arg !== null && !Array.isArray(arg)) {
// priority 0 - custom values
dictUpdate(current_priority, arg);
// priority 2 - status property
if (arg.hasOwnProperty("status") || arg.hasOwnProperty("statusText")) {
priority[2].status = arg.statusText || arg.status;
}
}
// merge priority dicts
for (current_priority = priority.length - 1;
current_priority > 0;
current_priority -= 1) {
dictUpdate(priority[current_priority - 1], priority[current_priority]);
}
priority = priority[0];
// check document id if post method
if (param.method === 'post' &&
(typeof priority.id !== 'string' || !priority.id)) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
'New document id have to be specified'
]);
}
// check status
priority.statusText = constants.http_status_text[priority.status];
if (priority.statusText === undefined) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
'Unknown status "' + priority.status + '"'
]);
}
priority.status = constants.http_status[priority.statusText];
// check data for get Attachment
if (param.method === 'getAttachment') {
if (typeof priority.data === 'string') {
priority.data = new Blob([priority.data], {
"type": priority.content_type || priority.mimetype || ""
});
delete priority.content_type;
delete priority.mimetype;
}
if (!(priority.data instanceof Blob)) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
'getAttachment method needs a Blob as returned "data".'
]);
}
// check data for readers (except check method)
} else if (methodType(param.method) === 'reader' &&
param.method !== 'check' &&
(typeof priority.data !== 'object' ||
priority.data === null ||
Object.getPrototypeOf(priority.data) !== Object.prototype)) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
param.method + ' method needs a dict as returned "data".'
]);
}
return param.solver.resolve(deepClone(priority));
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayInsert, deepClone, defaults */
// creates
// - some defaults job rule actions
function enableJobChecker(jio, shared, options) {
// dependencies
// - shared.jobs Object Array
// - param.promise Object
// creates
// - shared.job_rules Array
// uses 'job:new' event
// emits 'job:modified', 'job:start', 'job:resolved',
// 'job:end', 'job:reject' events
var i;
shared.job_rule_action_names = [undefined, "ok", "wait", "update", "deny"];
shared.job_rule_actions = {
wait: function (original_job, new_job) {
original_job.promise.always(function () {
new_job.state = 'ready';
new_job.modified = new Date();
shared.emit('job:modified', new_job);
shared.emit('job:start', new_job);
});
new_job.state = 'waiting';
new_job.modified = new Date();
shared.emit('job:modified', new_job);
},
update: function (original_job, new_job) {
if (!new_job.solver) {
// promise associated to the job
new_job.state = 'done';
shared.emit('job:resolved', new_job, []); // XXX why resolve?
shared.emit('job:end', new_job);
} else {
if (!original_job.solver) {
original_job.solver = new_job.solver;
} else {
original_job.promise.then(
new_job.command.resolve,
new_job.command.reject,
new_job.command.notify
);
}
}
new_job.state = 'running';
new_job.modified = new Date();
shared.emit('job:modified', new_job);
},
deny: function (original_job, new_job) {
new_job.state = "running";
shared.emit('job:reject', new_job, [
'precondition_failed',
'command denied',
'Command rejected by the job checker.'
]);
}
};
function addJobRule(job_rule) {
var i, old_position, before_position, after_position;
// job_rule = {
// code_name: string
// conditions: [string, ...]
// action: 'wait',
// after: code_name
// before: code_name
// }
if (typeof job_rule !== 'object' || job_rule === null) {
// wrong job rule
return;
}
if (typeof job_rule.code_name !== 'string') {
// wrong code name
return;
}
if (!Array.isArray(job_rule.conditions)) {
// wrong conditions
return;
}
if (job_rule.single !== undefined && typeof job_rule.single !== 'boolean') {
// wrong single property
return;
}
if (shared.job_rule_action_names.indexOf(job_rule.action) === -1) {
// wrong action
return;
}
if (job_rule.action !== 'deny' && job_rule.single === true) {
// only 'deny' action doesn't require original_job parameter
return;
}
if (typeof job_rule.after !== 'string') {
job_rule.after = '';
}
if (typeof job_rule.before !== 'string') {
job_rule.before = '';
}
for (i = 0; i < shared.job_rules.length; i += 1) {
if (shared.job_rules[i].code_name === job_rule.after) {
after_position = i + 1;
}
if (shared.job_rules[i].code_name === job_rule.before) {
before_position = i;
}
if (shared.job_rules[i].code_name === job_rule.code_name) {
old_position = i;
}
}
job_rule = {
"code_name": job_rule.code_name,
"conditions": job_rule.conditions,
"single": job_rule.single || false,
"action": job_rule.action || "ok"
};
if (before_position === undefined) {
before_position = shared.job_rules.length;
}
if (after_position > before_position) {
before_position = undefined;
}
if (job_rule.action !== "ok" && before_position !== undefined) {
arrayInsert(shared.job_rules, before_position, job_rule);
}
if (old_position !== undefined) {
if (old_position >= before_position) {
old_position += 1;
}
shared.job_rules.splice(old_position, 1);
}
}
function jobsRespectConditions(original_job, new_job, conditions) {
var j;
// browsing conditions
for (j = 0; j < conditions.length; j += 1) {
if (defaults.job_rule_conditions[conditions[j]]) {
if (
!defaults.job_rule_conditions[conditions[j]](original_job, new_job)
) {
return false;
}
}
}
return true;
}
function checkJob(job) {
var i, j;
if (job.state === 'ready') {
// browsing rules
for (i = 0; i < shared.job_rules.length; i += 1) {
if (shared.job_rules[i].single) {
// no browse
if (
jobsRespectConditions(
job,
undefined,
shared.job_rules[i].conditions
)
) {
shared.job_rule_actions[shared.job_rules[i].action](
undefined,
job
);
return;
}
} else {
// browsing jobs
for (j = shared.jobs.length - 1; j >= 0; j -= 1) {
if (shared.jobs[j] !== job) {
if (
jobsRespectConditions(
shared.jobs[j],
job,
shared.job_rules[i].conditions
)
) {
shared.job_rule_actions[shared.job_rules[i].action](
shared.jobs[j],
job
);
return;
}
}
}
}
}
}
}
if (options.job_management !== false) {
shared.job_rules = [{
"code_name": "readers update",
"conditions": [
"sameStorageDescription",
"areReaders",
"sameMethod",
"sameParameters",
"sameOptions"
],
"action": "update"
}, {
"code_name": "metadata writers update",
"conditions": [
"sameStorageDescription",
"areWriters",
"useMetadataOnly",
"sameMethod",
"haveDocumentIds",
"sameParameters"
],
"action": "update"
}, {
"code_name": "writers wait",
"conditions": [
"sameStorageDescription",
"areWriters",
"haveDocumentIds",
"sameDocumentId"
],
"action": "wait"
}];
if (options.clear_job_rules === true) {
shared.job_rules.length = 0;
}
if (Array.isArray(options.job_rules)) {
for (i = 0; i < options.job_rules.length; i += 1) {
addJobRule(deepClone(options.job_rules[i]));
}
}
shared.on('job:new', checkJob);
}
jio.jobRules = function () {
return deepClone(shared.job_rules);
};
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global setTimeout, Job, createStorage, deepClone, restCommandResolver,
restCommandRejecter */
function enableJobExecuter(jio, shared) { // , options) {
// uses 'job:new' events
// uses actions 'job:resolve', 'job:reject' and 'job:notify'
// emits 'job:modified', 'job:started', 'job:resolved',
// 'job:rejected', 'job:notified' and 'job:end' events
// emits action 'job:start'
function startJobIfReady(job) {
if (job.state === 'ready') {
shared.emit('job:start', job);
}
}
function executeJobIfReady(param) {
var storage;
if (param.state === 'ready') {
param.tried += 1;
param.started = new Date();
param.state = 'running';
param.modified = new Date();
shared.emit('job:modified', param);
shared.emit('job:started', param);
try {
storage = createStorage(deepClone(param.storage_spec));
} catch (e) {
return param.command.reject(
'internal_storage_error',
'invalid description',
'Check if the storage description respects the ' +
'constraints provided by the storage designer. (' +
e.name + ": " + e.message + ')'
);
}
if (typeof storage[param.method] !== 'function') {
return param.command.reject(
'not_implemented',
'method missing',
'Storage "' + param.storage_spec.type + '", "' +
param.method + '" method is missing.'
);
}
setTimeout(function () {
storage[param.method](
deepClone(param.command),
deepClone(param.kwargs),
deepClone(param.options)
);
});
}
}
function endAndResolveIfRunning(job, args) {
if (job.state === 'running') {
job.state = 'done';
job.modified = new Date();
shared.emit('job:modified', job);
if (job.solver) {
restCommandResolver(job, args);
}
shared.emit('job:resolved', job, args);
shared.emit('job:end', job);
}
}
function endAndRejectIfRunning(job, args) {
if (job.state === 'running') {
job.state = 'fail';
job.modified = new Date();
shared.emit('job:modified', job);
if (job.solver) {
restCommandRejecter(job, args);
}
shared.emit('job:rejected', job, args);
shared.emit('job:end', job);
}
}
function notifyJobIfRunning(job, args) {
if (job.state === 'running' && job.solver) {
job.solver.notify(args[0]);
shared.emit('job:notified', job, args);
}
}
// listeners
shared.on('job:new', startJobIfReady);
shared.on('job:start', executeJobIfReady);
shared.on('job:resolve', endAndResolveIfRunning);
shared.on('job:reject', endAndRejectIfRunning);
shared.on('job:notify', notifyJobIfRunning);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend */
function enableJobMaker(jio, shared, options) {
// dependencies
// - param.method
// - param.storage_spec
// - param.kwargs
// - param.options
// uses (Job)
// - param.created date
// - param.modified date
// - param.tried number >= 0
// - param.state string 'ready'
// - param.method string
// - param.storage_spec object
// - param.kwargs object
// - param.options object
// - param.command object
// list of job events:
// - Job existence -> new, end
// - Job execution -> started, stopped
// - Job resolution -> resolved, rejected, notified, cancelled
// - Job modification -> modified
// emits actions 'job:resolve', 'job:reject' and 'job:notify'
// uses `rest method` events
// emits 'job:new' event
shared.job_keys = arrayExtend(shared.job_keys || [], [
"created",
"modified",
"tried",
"state",
"method",
"storage_spec",
"kwargs",
"options"
]);
function addCommandToJob(job) {
job.command = {};
job.command.resolve = function () {
shared.emit('job:resolve', job, arguments);
};
job.command.success = job.command.resolve;
job.command.reject = function () {
shared.emit('job:reject', job, arguments);
};
job.command.error = job.command.reject;
job.command.notify = function () {
shared.emit('job:notify', job, arguments);
};
job.command.storage = function () {
return shared.createRestApi.apply(null, arguments);
};
}
function createJobFromRest(param) {
if (param.solver) {
// rest parameters are good
shared.emit('job:new', param);
}
}
function initJob(job) {
job.state = 'ready';
if (typeof job.tried !== 'number' || !isFinite(job.tried)) {
job.tried = 0;
}
if (!job.created) {
job.created = new Date();
}
addCommandToJob(job);
job.modified = new Date();
}
// listeners
shared.rest_method_names.forEach(function (method) {
shared.on(method, createJobFromRest);
});
shared.on('job:new', initJob);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, localStorage, Workspace, uniqueJSONStringify, JobQueue,
constants, setTimeout, clearTimeout */
function enableJobQueue(jio, shared, options) {
// dependencies
// - shared.storage_spec Object
// uses
// - options.workspace Workspace
// - shared.job_keys String Array
// creates
// - shared.storage_spec_str String
// - shared.workspace Workspace
// - shared.job_queue JobQueue
// uses 'job:new', 'job:started', 'job:stopped', 'job:modified',
// 'job:notified', 'job:end' events
// emits 'job:end' event
function postJobIfReady(param) {
if (!param.stored && param.state === 'ready') {
clearTimeout(param.queue_ident);
delete param.queue_ident;
shared.job_queue.load();
shared.job_queue.post(param);
shared.job_queue.save();
param.stored = true;
}
}
function deferredPutJob(param) {
if (param.queue_ident === undefined) {
param.queue_ident = setTimeout(function () {
delete param.queue_ident;
if (param.stored) {
shared.job_queue.load();
shared.job_queue.put(param);
shared.job_queue.save();
}
});
}
}
function removeJob(param) {
clearTimeout(param.queue_ident);
delete param.queue_ident;
if (param.stored) {
shared.job_queue.load();
shared.job_queue.remove(param.id);
shared.job_queue.save();
delete param.stored;
delete param.id;
}
}
function initJob(param) {
if (!param.command.end) {
param.command.end = function () {
shared.emit('job:end', param);
};
}
}
shared.on('job:new', initJob);
if (options.job_management !== false) {
shared.job_keys = arrayExtend(shared.job_keys || [], ["id"]);
if (typeof options.workspace !== 'object') {
shared.workspace = localStorage;
} else {
shared.workspace = new Workspace(options.workspace);
}
if (!shared.storage_spec_str) {
shared.storage_spec_str = uniqueJSONStringify(shared.storage_spec);
}
shared.job_queue = new JobQueue(
shared.workspace,
'jio/jobs/' + shared.storage_spec_str,
shared.job_keys
);
// Listeners
shared.on('job:new', postJobIfReady);
shared.on('job:started', deferredPutJob);
shared.on('job:stopped', deferredPutJob);
shared.on('job:modified', deferredPutJob);
shared.on('job:notified', deferredPutJob);
shared.on('job:end', removeJob);
}
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global setTimeout, methodType */
function enableJobRecovery(jio, shared, options) {
// dependencies
// - JobQueue enabled and before this
// uses
// - shared.job_queue JobQueue
// emits 'job:new' event
function numberOrDefault(number, default_value) {
return (typeof number === 'number' &&
isFinite(number) ? number : default_value);
}
function recoverJob(param) {
shared.job_queue.load();
shared.job_queue.remove(param.id);
delete param.id;
if (methodType(param.method) === 'writer' &&
(param.state === 'ready' ||
param.state === 'running' ||
param.state === 'waiting')) {
shared.job_queue.save();
shared.emit('job:new', param);
}
}
function jobWaiter(id, modified) {
return function () {
var job;
shared.job_queue.load();
job = shared.job_queue.get(id);
if (job && job.modified === modified) {
// job not modified, no one takes care of it
recoverJob(job);
}
};
}
var i, job_array, delay, deadline, recovery_delay;
recovery_delay = numberOrDefault(options.recovery_delay, 10000);
if (recovery_delay < 0) {
recovery_delay = 10000;
}
if (options.job_management !== false && options.job_recovery !== false) {
shared.job_queue.load();
job_array = shared.job_queue.asArray();
for (i = 0; i < job_array.length; i += 1) {
delay = numberOrDefault(job_array[i].timeout + recovery_delay,
recovery_delay);
deadline = new Date(job_array[i].modified).getTime() + delay;
if (!isFinite(delay)) {
// 'modified' date is broken
recoverJob(job_array[i]);
} else if (deadline <= Date.now()) {
// deadline reached
recoverJob(job_array[i]);
} else {
// deadline not reached yet
// wait until deadline is reached then check job again
setTimeout(jobWaiter(job_array[i].id, job_array[i].modified),
deadline - Date.now());
}
}
}
}
/*jslint indent: 2, maxlen: 80, sloppy: true, unparam: true */
/*global ReferenceArray */
function enableJobReference(jio, shared, options) {
// creates
// - shared.jobs Object Array
// uses 'job:new' and 'job:end' events
shared.jobs = [];
var job_references = new ReferenceArray(shared.jobs);
shared.on('job:new', function (param) {
job_references.put(param);
});
shared.on('job:end', function (param) {
job_references.remove(param);
});
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, setTimeout, methodType, constants */
function enableJobRetry(jio, shared, options) {
// dependencies
// - param.method
// - param.storage_spec
// - param.kwargs
// - param.options
// - param.command
// uses
// - options.default_writers_max_retry number >= 0 or null
// - options.default_readers_max_retry number >= 0 or null
// - options.default_max_retry number >= 0 or null
// - options.writers_max_retry number >= 0 or null
// - options.readers_max_retry number >= 0 or null
// - options.max_retry number >= 0 or null
// - param.modified date
// - param.tried number >= 0
// - param.max_retry >= 0 or undefined
// - param.state string 'ready' 'waiting'
// - param.method string
// - param.storage_spec object
// - param.kwargs object
// - param.options object
// - param.command object
// uses 'job:new' and 'job:retry' events
// emits action 'job:start' event
// emits 'job:retry', 'job:reject', 'job:modified' and 'job:stopped' events
shared.job_keys = arrayExtend(shared.job_keys || [], ["max_retry"]);
var writers_max_retry, readers_max_retry, max_retry;
function defaultMaxRetry(param) {
if (methodType(param.method) === 'writers') {
if (max_retry === undefined) {
return writers_max_retry;
}
return max_retry;
}
if (max_retry === undefined) {
return readers_max_retry;
}
return max_retry;
}
function positiveNumberOrDefault(number, default_value) {
return (typeof number === 'number' &&
number >= 0 ?
number : default_value);
}
function positiveNumberNullOrDefault(number, default_value) {
return ((typeof number === 'number' &&
number >= 0) || number === null ?
number : default_value);
}
max_retry = positiveNumberNullOrDefault(
options.max_retry || options.default_max_retry,
undefined
);
writers_max_retry = positiveNumberNullOrDefault(
options.writers_max_retry || options.default_writers_max_retry,
null
);
readers_max_retry = positiveNumberNullOrDefault(
options.readers_max_retry || options.default_readers_max_retry,
2
);
function initJob(param) {
if (typeof param.max_retry !== 'number' || param.max_retry < 0) {
param.max_retry = positiveNumberOrDefault(
param.options.max_retry,
defaultMaxRetry(param)
);
}
param.command.reject = function (status) {
if (constants.http_action[status || 0] === "retry") {
shared.emit('job:retry', param, arguments);
} else {
shared.emit('job:reject', param, arguments);
}
};
param.command.retry = function () {
shared.emit('job:retry', param, arguments);
};
}
function retryIfRunning(param, args) {
if (param.state === 'running') {
if (param.max_retry === undefined ||
param.max_retry === null ||
param.max_retry >= param.tried) {
param.state = 'waiting';
param.modified = new Date();
shared.emit('job:modified', param);
shared.emit('job:stopped', param);
setTimeout(function () {
param.state = 'ready';
param.modified = new Date();
shared.emit('job:modified', param);
shared.emit('job:start', param);
}, Math.min(10000, param.tried * 2000));
} else {
shared.emit('job:reject', param, args);
}
}
}
// listeners
shared.on('job:new', initJob);
shared.on('job:retry', retryIfRunning);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, setTimeout, clearTimeout */
function enableJobTimeout(jio, shared, options) {
// dependencies
// - param.tried number > 0
// - param.state string 'running'
// uses
// - param.tried number > 0
// - param.timeout number >= 0
// - param.timeout_ident Timeout
// - param.state string 'running'
// uses 'job:new', 'job:stopped', 'job:started',
// 'job:notified' and 'job:end' events
// emits 'job:modified' event
shared.job_keys = arrayExtend(shared.job_keys || [], ["timeout"]);
function positiveNumberOrDefault(number, default_value) {
return (typeof number === 'number' &&
number >= 0 ?
number : default_value);
}
// 10 seconds by default
var default_timeout = positiveNumberOrDefault(options.default_timeout, 10000);
function timeoutReject(param) {
return function () {
param.command.reject(
'request_timeout',
'timeout',
'Operation canceled after around ' + (
Date.now() - param.modified.getTime()
) + ' milliseconds of inactivity.'
);
};
}
function initJob(job) {
if (typeof job.timeout !== 'number' || job.timeout < 0) {
job.timeout = positiveNumberOrDefault(
job.options.timeout,
default_timeout
);
}
job.modified = new Date();
shared.emit('job:modified', job);
}
function clearJobTimeout(job) {
clearTimeout(job.timeout_ident);
delete job.timeout_ident;
}
function restartJobTimeoutIfRunning(job) {
clearTimeout(job.timeout_ident);
if (job.state === 'running' && job.timeout > 0) {
job.timeout_ident = setTimeout(timeoutReject(job), job.timeout);
job.modified = new Date();
} else {
delete job.timeout_ident;
}
}
// listeners
shared.on('job:new', initJob);
shared.on("job:stopped", clearJobTimeout);
shared.on("job:end", clearJobTimeout);
shared.on("job:started", restartJobTimeoutIfRunning);
shared.on("job:notified", restartJobTimeoutIfRunning);
}
/*jslint indent: 2, maxlen: 80, sloppy: true */
/*global arrayValuesToTypeDict, dictClear, RSVP, deepClone */
// adds methods to JIO
// - post
// - put
// - get
// - remove
// - allDocs
// - putAttachment
// - getAttachment
// - removeAttachment
// - check
// - repair
// event shared objet
// - storage_spec object
// - method string
// - kwargs object
// - options object
// - solver object
// - solver.resolve function
// - solver.reject function
// - solver.notify function
// - cancellers object
// - promise object
function enableRestAPI(jio, shared) { // (jio, shared, options)
shared.rest_method_names = [
"post",
"put",
"get",
"remove",
"allDocs",
"putAttachment",
"getAttachment",
"removeAttachment",
"check",
"repair"
];
function prepareParamAndEmit(method, storage_spec, args) {
var callback, type_dict, param = {};
type_dict = arrayValuesToTypeDict(Array.prototype.slice.call(args));
type_dict.object = type_dict.object || [];
if (method !== 'allDocs') {
param.kwargs = type_dict.object.shift();
if (param.kwargs === undefined) {
throw new TypeError("JIO()." + method +
"(): Argument 1 is not of type 'object'");
}
param.kwargs = deepClone(param.kwargs);
} else {
param.kwargs = {};
}
param.solver = {};
param.options = deepClone(type_dict.object.shift()) || {};
param.promise = new RSVP.Promise(function (resolve, reject, notify) {
param.solver.resolve = resolve;
param.solver.reject = reject;
param.solver.notify = notify;
}, function () {
var k;
for (k in param.cancellers) {
if (param.cancellers.hasOwnProperty(k)) {
param.cancellers[k]();
}
}
});
type_dict['function'] = type_dict['function'] || [];
if (type_dict['function'].length === 1) {
callback = type_dict['function'][0];
param.promise.then(function (answer) {
callback(undefined, answer);
}, function (answer) {
callback(answer, undefined);
});
} else if (type_dict['function'].length > 1) {
param.promise.then(type_dict['function'][0],
type_dict['function'][1],
type_dict['function'][2]);
}
type_dict = dictClear(type_dict);
param.storage_spec = storage_spec;
param.method = method;
shared.emit(method, param);
return param.promise;
}
shared.createRestApi = function (storage_spec, that) {
if (that === undefined) {
that = {};
}
shared.rest_method_names.forEach(function (method) {
that[method] = function () {
return prepareParamAndEmit(method, storage_spec, arguments);
};
});
return that;
};
shared.createRestApi(shared.storage_spec, jio);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global Blob, restCommandRejecter, Metadata */
function enableRestParamChecker(jio, shared) {
// dependencies
// - param.solver
// - param.kwargs
// checks the kwargs and convert value if necessary
// which is a dict of method to use to announce that
// the command is finished
// tools
function checkId(param) {
if (typeof param.kwargs._id !== 'string' || param.kwargs._id === '') {
restCommandRejecter(param, [
'bad_request',
'wrong document id',
'Document id must be a non empty string.'
]);
delete param.solver;
return false;
}
return true;
}
function checkAttachmentId(param) {
if (typeof param.kwargs._attachment !== 'string' ||
param.kwargs._attachment === '') {
restCommandRejecter(param, [
'bad_request',
'wrong attachment id',
'Attachment id must be a non empty string.'
]);
delete param.solver;
return false;
}
return true;
}
// listeners
shared.on('post', function (param) {
if (param.kwargs._id !== undefined) {
if (!checkId(param)) {
return;
}
}
new Metadata(param.kwargs).format();
});
["put", "get", "remove"].forEach(function (method) {
shared.on(method, function (param) {
if (!checkId(param)) {
return;
}
new Metadata(param.kwargs).format();
});
});
shared.on('putAttachment', function (param) {
if (!checkId(param) || !checkAttachmentId(param)) {
return;
}
if (!(param.kwargs._blob instanceof Blob) &&
typeof param.kwargs._data === 'string') {
param.kwargs._blob = new Blob([param.kwargs._data], {
"type": param.kwargs._content_type || param.kwargs._mimetype || ""
});
delete param.kwargs._data;
delete param.kwargs._mimetype;
delete param.kwargs._content_type;
} else if (param.kwargs._blob instanceof Blob) {
delete param.kwargs._data;
delete param.kwargs._mimetype;
delete param.kwargs._content_type;
} else if (param.kwargs._data instanceof Blob) {
param.kwargs._blob = param.kwargs._data;
delete param.kwargs._data;
delete param.kwargs._mimetype;
delete param.kwargs._content_type;
} else {
restCommandRejecter(param, [
'bad_request',
'wrong attachment',
'Attachment information must be like {"_id": document id, ' +
'"_attachment": attachment name, "_data": string, ["_mimetype": ' +
'content type]} or {"_id": document id, "_attachment": ' +
'attachment name, "_blob": Blob}'
]);
delete param.solver;
}
});
["getAttachment", "removeAttachment"].forEach(function (method) {
shared.on(method, function (param) {
if (!checkId(param)) {
checkAttachmentId(param);
}
});
});
["check", "repair"].forEach(function (method) {
shared.on(method, function (param) {
if (param.kwargs._id !== undefined) {
if (!checkId(param)) {
return;
}
}
});
});
}
}));
/*
* Copyright 2013, Nexedi SA
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global jIO, localStorage, setTimeout, complex_queries, window, define,
exports, require */
/**
* JIO Local Storage. Type = 'local'.
* Local browser "database" storage.
*
* Storage Description:
*
* {
* "type": "local",
* "mode": <string>,
* // - "localStorage" // default
* // - "memory"
* "username": <non empty string>, // to define user space
* "application_name": <string> // default 'untitled'
* }
*
* Document are stored in path
* 'jio/localstorage/username/application_name/document_id' like this:
*
* {
* "_id": "document_id",
* "_attachments": {
* "attachment_name": {
* "length": data_length,
* "digest": "md5-XXX",
* "content_type": "mime/type"
* },
* "attachment_name2": {..}, ...
* },
* "metadata_name": "metadata_value"
* "metadata_name2": ...
* ...
* }
*
* Only "_id" and "_attachments" are specific metadata keys, other one can be
* added without loss.
*
* @class LocalStorage
*/
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports, require('jio'), require('complex_queries'));
}
window.local_storage = {};
module(window.local_storage, jIO, complex_queries);
}([
'exports',
'jio',
'complex_queries'
], function (exports, jIO, complex_queries) {
"use strict";
/**
* Checks if an object has no enumerable keys
*
* @param {Object} obj The object
* @return {Boolean} true if empty, else false
*/
function objectIsEmpty(obj) {
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
return false;
}
}
return true;
}
var ram = {}, memorystorage, localstorage;
/*
* Wrapper for the localStorage used to simplify instion of any kind of
* values
*/
localstorage = {
getItem: function (item) {
var value = localStorage.getItem(item);
return value === null ? null : JSON.parse(value);
},
setItem: function (item, value) {
return localStorage.setItem(item, JSON.stringify(value));
},
removeItem: function (item) {
return localStorage.removeItem(item);
}
};
/*
* Wrapper for the localStorage used to simplify instion of any kind of
* values
*/
memorystorage = {
getItem: function (item) {
var value = ram[item];
return value === undefined ? null : JSON.parse(value);
},
setItem: function (item, value) {
ram[item] = JSON.stringify(value);
},
removeItem: function (item) {
delete ram[item];
}
};
/**
* The JIO LocalStorage extension
*
* @class LocalStorage
* @constructor
*/
function LocalStorage(spec) {
if (typeof spec.username !== 'string' && !spec.username) {
throw new TypeError("LocalStorage 'username' must be a string " +
"which contains more than one character.");
}
this._localpath = 'jio/localstorage/' + spec.username + '/' + (
spec.application_name === null || spec.application_name ===
undefined ? 'untitled' : spec.application_name.toString()
);
switch (spec.mode) {
case "memory":
this._database = ram;
this._storage = memorystorage;
this._mode = "memory";
break;
default:
this._database = localStorage;
this._storage = localstorage;
this._mode = "localStorage";
break;
}
}
/**
* Create a document in local storage.
*
* @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
LocalStorage.prototype.post = function (command, metadata) {
var doc, doc_id = metadata._id;
if (!doc_id) {
doc_id = jIO.util.generateUuid();
}
doc = this._storage.getItem(this._localpath + "/" + doc_id);
if (doc === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
doc._id = doc_id;
delete doc._attachments;
this._storage.setItem(this._localpath + "/" + doc_id, doc);
command.success({"id": doc_id});
} else {
// the document already exists
command.error(
"conflict",
"document exists",
"Cannot create a new document"
);
}
};
/**
* Create or update a document in local storage.
*
* @method put
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
LocalStorage.prototype.put = function (command, metadata) {
var doc, tmp, status;
doc = this._storage.getItem(this._localpath + "/" + metadata._id);
if (doc === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
delete doc._attachments;
status = "created";
} else {
// the document already exists
tmp = jIO.util.deepClone(metadata);
tmp._attachments = doc._attachments;
doc = tmp;
status = "no_content";
}
// write
this._storage.setItem(this._localpath + "/" + metadata._id, doc);
command.success(status);
};
/**
* Add an attachment to a document
*
* @method putAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.putAttachment = function (command, param) {
var that = this, doc, status = "created";
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
// the document does not exist
return command.error(
"not_found",
"missing",
"Impossible to add attachment"
);
}
// the document already exists
// download data
jIO.util.readBlobAsBinaryString(param._blob).then(function (e) {
doc._attachments = doc._attachments || {};
if (doc._attachments[param._attachment]) {
status = "no_content";
}
doc._attachments[param._attachment] = {
"content_type": param._blob.type,
"digest": jIO.util.makeBinaryStringDigest(e.target.result),
"length": param._blob.size
};
that._storage.setItem(that._localpath + "/" + param._id + "/" +
param._attachment, e.target.result);
that._storage.setItem(that._localpath + "/" + param._id, doc);
command.success(status,
{"digest": doc._attachments[param._attachment].digest});
}, function (e) {
command.error(
"request_timeout",
"blob error",
"Error " + e.status + ", unable to get blob content"
);
}, function (e) {
command.notify((e.loaded / e.total) * 100);
});
};
/**
* Get a document
*
* @method get
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.get = function (command, param) {
var doc = this._storage.getItem(
this._localpath + "/" + param._id
);
if (doc !== null) {
command.success({"data": doc});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
};
/**
* Get an attachment
*
* @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.getAttachment = function (command, param) {
var doc;
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
return command.error(
"not_found",
"missing document",
"Cannot find document"
);
}
if (typeof doc._attachments !== 'object' ||
typeof doc._attachments[param._attachment] !== 'object') {
return command.error(
"not_found",
"missing attachment",
"Cannot find attachment"
);
}
command.success({
"data": this._storage.getItem(
this._localpath + "/" + param._id +
"/" + param._attachment
) || "",
"digest": doc._attachments[param._attachment].digest,
"content_type": doc._attachments[param._attachment].content_type || ""
});
};
/**
* Remove a document
*
* @method remove
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.remove = function (command, param) {
var doc, i, attachment_list;
doc = this._storage.getItem(this._localpath + "/" + param._id);
attachment_list = [];
if (doc !== null && typeof doc === "object") {
if (typeof doc._attachments === "object") {
// prepare list of attachments
for (i in doc._attachments) {
if (doc._attachments.hasOwnProperty(i)) {
attachment_list.push(i);
}
}
}
} else {
return command.error(
"not_found",
"missing",
"Document not found"
);
}
this._storage.removeItem(this._localpath + "/" + param._id);
// delete all attachments
for (i = 0; i < attachment_list.length; i += 1) {
this._storage.removeItem(this._localpath + "/" + param._id +
"/" + attachment_list[i]);
}
command.success();
};
/**
* Remove an attachment
*
* @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.removeAttachment = function (command, param) {
var doc = this._storage.getItem(this._localpath + "/" + param._id);
if (typeof doc !== 'object') {
return command.error(
"not_found",
"missing document",
"Document not found"
);
}
if (typeof doc._attachments !== "object" ||
typeof doc._attachments[param._attachment] !== "object") {
return command.error(
"not_found",
"missing attachment",
"Attachment not found"
);
}
delete doc._attachments[param._attachment];
if (objectIsEmpty(doc._attachments)) {
delete doc._attachments;
}
this._storage.setItem(this._localpath + "/" + param._id, doc);
this._storage.removeItem(this._localpath + "/" + param._id +
"/" + param._attachment);
command.success();
};
/**
* Get all filenames belonging to a user from the document index
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.allDocs = function (command, param, options) {
var i, row, path_re, rows, document_list, document_object, delete_id;
param.unused = true;
rows = [];
document_list = [];
path_re = new RegExp(
"^" + complex_queries.stringEscapeRegexpCharacters(this._localpath) +
"/[^/]+$"
);
if (options.query === undefined && options.sort_on === undefined &&
options.select_list === undefined &&
options.include_docs === undefined) {
rows = [];
for (i in this._database) {
if (this._database.hasOwnProperty(i)) {
// filter non-documents
if (path_re.test(i)) {
row = { value: {} };
row.id = i.split('/').slice(-1)[0];
row.key = row.id;
if (options.include_docs) {
row.doc = JSON.parse(this._storage.getItem(i));
}
rows.push(row);
}
}
}
command.success({"data": {"rows": rows, "total_rows": rows.length}});
} else {
// create complex query object from returned results
for (i in this._database) {
if (this._database.hasOwnProperty(i)) {
if (path_re.test(i)) {
document_list.push(this._storage.getItem(i));
}
}
}
options.select_list = options.select_list || [];
if (options.select_list.indexOf("_id") === -1) {
options.select_list.push("_id");
delete_id = true;
}
if (options.include_docs === true) {
document_object = {};
document_list.forEach(function (meta) {
document_object[meta._id] = meta;
});
}
complex_queries.QueryFactory.create(options.query || "").
exec(document_list, options);
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
"key": value._id
};
if (options.include_docs === true) {
o.doc = document_object[value._id];
delete document_object[value._id];
}
if (delete_id) {
delete value._id;
}
o.value = value;
return o;
});
command.success({"data": {
"total_rows": document_list.length,
"rows": document_list
}});
}
};
/**
* Check the storage or a specific document
*
* @method check
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.check = function (command, param) {
this.genericRepair(command, param, false);
};
/**
* Repair the storage or a specific document
*
* @method repair
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.repair = function (command, param) {
this.genericRepair(command, param, true);
};
/**
* A generic method that manage check or repair command
*
* @method genericRepair
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Boolean} repair If true then repair else just check
*/
LocalStorage.prototype.genericRepair = function (command, param, repair) {
var that = this, result;
function referenceAttachment(param, attachment) {
if (param.referenced_attachments.indexOf(attachment) !== -1) {
return;
}
var i = param.unreferenced_attachments.indexOf(attachment);
if (i !== -1) {
param.unreferenced_attachments.splice(i, 1);
}
param.referenced_attachments[param.referenced_attachments.length] =
attachment;
}
function attachmentFound(param, attachment) {
if (param.referenced_attachments.indexOf(attachment) !== -1) {
return;
}
if (param.unreferenced_attachments.indexOf(attachment) !== -1) {
return;
}
param.unreferenced_attachments[param.unreferenced_attachments.length] =
attachment;
}
function repairOne(param, repair) {
var i, doc, modified;
doc = that._storage.getItem(that._localpath + "/" + param._id);
if (doc === null) {
return; // OK
}
// check document type
if (typeof doc !== 'object') {
// wrong document
if (!repair) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Document is unrecoverable"
]};
}
// delete the document
that._storage.removeItem(that._localpath + "/" + param._id);
return; // OK
}
// good document type
// repair json document
if (!repair) {
if (!(new jIO.Metadata(doc).check())) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Some metadata might be lost"
]};
}
} else {
modified = jIO.util.uniqueJSONStringify(doc) !==
jIO.util.uniqueJSONStringify(new jIO.Metadata(doc).format()._dict);
}
if (doc._attachments !== undefined) {
if (typeof doc._attachments !== 'object') {
if (!repair) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Attachments are unrecoverable"
]};
}
delete doc._attachments;
that._storage.setItem(that._localpath + "/" + param._id, doc);
return; // OK
}
for (i in doc._attachments) {
if (doc._attachments.hasOwnProperty(i)) {
// check attachment existence
if (that._storage.getItem(that._localpath + "/" + param._id + "/" +
i) !== 'string') {
if (!repair) {
return {"error": true, "answers": [
"conflict",
"missing attachment",
"Attachment \"" + i + "\" of \"" + param._id + "\" is missing"
]};
}
delete doc._attachments[i];
if (objectIsEmpty(doc._attachments)) {
delete doc._attachments;
}
modified = true;
} else {
// attachment exists
// check attachment metadata
// check length
referenceAttachment(param, param._id + "/" + doc._attachments[i]);
if (doc._attachments[i].length !== undefined &&
typeof doc._attachments[i].length !== 'number') {
if (!repair) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Attachment metadata length corrupted"
]};
}
// It could take a long time to get the length, no repair.
// length can be omited
delete doc._attachments[i].length;
}
// It could take a long time to regenerate the hash, no check.
// Impossible to discover the attachment content type.
}
}
}
}
if (modified) {
that._storage.setItem(that._localpath + "/" + param._id, doc);
}
// OK
}
function repairAll(param, repair) {
var i, result;
for (i in that._database) {
if (that._database.hasOwnProperty(i)) {
// browsing every entry
if (i.slice(0, that._localpath.length) === that._localpath) {
// is part of the user space
if (/^[^\/]+\/[^\/]+$/.test(i.slice(that._localpath.length + 1))) {
// this is an attachment
attachmentFound(param, i.slice(that._localpath.length + 1));
} else if (/^[^\/]+$/.test(i.slice(that._localpath.length + 1))) {
// this is a document
param._id = i.slice(that._localpath.length + 1);
result = repairOne(param, repair);
if (result) {
return result;
}
} else {
// this is pollution
that._storage.removeItem(i);
}
}
}
}
// remove unreferenced attachments
for (i = 0; i < param.unreferenced_attachments.length; i += 1) {
that._storage.removeItem(that._localpath + "/" +
param.unreferenced_attachments[i]);
}
}
param.referenced_attachments = [];
param.unreferenced_attachments = [];
if (typeof param._id === 'string') {
result = repairOne(param, repair) || {};
} else {
result = repairAll(param, repair) || {};
}
if (result.error) {
return command.error.apply(command, result.answers || []);
}
command.success.apply(command, result.answers || []);
};
jIO.addStorage('local', LocalStorage);
//////////////////////////////////////////////////////////////////////
// Tools
function createLocalDescription(username, application_name) {
if (typeof username !== 'string') {
throw new TypeError("LocalStorage username must be a string");
}
var description = {
"type": "local",
"username": username
};
if (typeof application_name === 'string') {
description.application_name = application_name;
}
return description;
}
function createMemoryDescription(username, application_name) {
var description = createLocalDescription(username, application_name);
description.mode = "memory";
return description;
}
/**
* Tool to help users to create local storage description for JIO
*
* @param {String} username The username
* @param {String} [application_name] The application_name
* @param {String} [mode="localStorage"] Use localStorage or memory
* @return {Object} The storage description
*/
function createDescription(username, application_name, mode) {
if (mode === undefined || mode.toString() === 'localStorage') {
return createLocalDescription(username, application_name);
}
if (mode.toString() === 'memory') {
return createMemoryDescription(username, application_name);
}
throw new TypeError("Unknown LocalStorage '" + mode.toString() + "' mode");
}
exports.createDescription = createDescription;
exports.createLocalDescription = createLocalDescription;
exports.createMemoryDescription = createMemoryDescription;
function clearLocalStorage() {
var k;
for (k in localStorage) {
if (localStorage.hasOwnProperty(k)) {
if (/^jio\/localstorage\//.test(k)) {
localStorage.removeItem(k);
}
}
}
}
function clearMemoryStorage() {
jIO.util.dictClear(ram);
}
exports.clear = clearLocalStorage;
exports.clearLocalStorage = clearLocalStorage;
exports.clearMemoryStorage = clearMemoryStorage;
}));
(function (dependencies, module) {
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports);
}
module(window);
}(['exports'], function (exports) {
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
/*
* Perform a simple self-test to see if the VM is working
*/
function md5_vm_test()
{
return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s)
{
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data)
{
var bkey = rstr2binl(key);
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input)
{
try { hexcase } catch(e) { hexcase=0; }
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var output = "";
var x;
for(var i = 0; i < input.length; i++)
{
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F)
+ hex_tab.charAt( x & 0x0F);
}
return output;
}
/*
* Convert a raw string to a base-64 string
*/
function rstr2b64(input)
{
try { b64pad } catch(e) { b64pad=''; }
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var output = "";
var len = input.length;
for(var i = 0; i < len; i += 3)
{
var triplet = (input.charCodeAt(i) << 16)
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
}
}
return output;
}
/*
* Convert a raw string to an arbitrary string encoding
*/
function rstr2any(input, encoding)
{
var divisor = encoding.length;
var i, j, q, x, quotient;
/* Convert to an array of 16-bit big-endian values, forming the dividend */
var dividend = Array(Math.ceil(input.length / 2));
for(i = 0; i < dividend.length; i++)
{
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
}
/*
* Repeatedly perform a long division. The binary array forms the dividend,
* the length of the encoding is the divisor. Once computed, the quotient
* forms the dividend for the next step. All remainders are stored for later
* use.
*/
var full_length = Math.ceil(input.length * 8 /
(Math.log(encoding.length) / Math.log(2)));
var remainders = Array(full_length);
for(j = 0; j < full_length; j++)
{
quotient = Array();
x = 0;
for(i = 0; i < dividend.length; i++)
{
x = (x << 16) + dividend[i];
q = Math.floor(x / divisor);
x -= q * divisor;
if(quotient.length > 0 || q > 0)
quotient[quotient.length] = q;
}
remainders[j] = x;
dividend = quotient;
}
/* Convert the remainders to the output string */
var output = "";
for(i = remainders.length - 1; i >= 0; i--)
output += encoding.charAt(remainders[i]);
return output;
}
/*
* Encode a string as utf-8.
* For efficiency, this assumes the input is valid utf-16.
*/
function str2rstr_utf8(input)
{
var output = "";
var i = -1;
var x, y;
while(++i < input.length)
{
/* Decode utf-16 surrogate pairs */
x = input.charCodeAt(i);
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
{
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
i++;
}
/* Encode output as utf-8 */
if(x <= 0x7F)
output += String.fromCharCode(x);
else if(x <= 0x7FF)
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F));
else if(x <= 0xFFFF)
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
else if(x <= 0x1FFFFF)
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
}
return output;
}
/*
* Encode a string as utf-16
*/
function str2rstr_utf16le(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
(input.charCodeAt(i) >>> 8) & 0xFF);
return output;
}
function str2rstr_utf16be(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
input.charCodeAt(i) & 0xFF);
return output;
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input)
{
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
return output;
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input)
{
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
return output;
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}
exports.hex_md5 = hex_md5;
exports.b64_md5 = b64_md5;
exports.any_md5 = any_md5;
exports.hex_hmac_md5 = hex_hmac_md5;
exports.b64_hmac_md5 = b64_hmac_md5;
exports.any_hmac_md5 = any_hmac_md5;
}));
(function (dependencies, module) {
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports);
}
module(window);
}(['exports'], function (window) {
/* A JavaScript implementation of the Secure Hash Algorithm, SHA-256
* Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/
* Distributed under the BSD License
* Some bits taken from Paul Johnston's SHA-1 implementation
*/
(function () {
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
function safe_add (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
function R (X, n) {return ( X >>> n );}
function Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
function Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
function Sigma0256(x) {return (S(x, 2) ^ S(x, 13) ^ S(x, 22));}
function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}
function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));}
function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));}
function newArray (n) {
var a = [];
for (;n>0;n--) {
a.push(undefined);
}
return a;
}
function core_sha256 (m, l) {
var K = [0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2];
var HASH = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];
var W = newArray(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
/* append padding */
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for ( var i = 0; i<m.length; i+=16 ) {
a = HASH[0]; b = HASH[1]; c = HASH[2]; d = HASH[3];
e = HASH[4]; f = HASH[5]; g = HASH[6]; h = HASH[7];
for ( var j = 0; j<64; j++) {
if (j < 16) {
W[j] = m[j + i];
} else {
W[j] = safe_add(safe_add(safe_add(Gamma1256(
W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
}
T1 = safe_add(safe_add(safe_add(
safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
T2 = safe_add(Sigma0256(a), Maj(a, b, c));
h = g; g = f; f = e; e = safe_add(d, T1);
d = c; c = b; b = a; a = safe_add(T1, T2);
}
HASH[0] = safe_add(a, HASH[0]); HASH[1] = safe_add(b, HASH[1]);
HASH[2] = safe_add(c, HASH[2]); HASH[3] = safe_add(d, HASH[3]);
HASH[4] = safe_add(e, HASH[4]); HASH[5] = safe_add(f, HASH[5]);
HASH[6] = safe_add(g, HASH[6]); HASH[7] = safe_add(h, HASH[7]);
}
return HASH;
}
function str2binb (str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
return bin;
}
function binb2hex (binarray) {
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for (var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
}
return str;
}
function hex_sha256(s){
return binb2hex(core_sha256(str2binb(s),s.length * chrsz));
}
window.hex_sha256 = hex_sha256;
}());
}));
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*! jQuery JSON plugin 2.4.0 | code.google.com/p/jquery-json */
(function($){'use strict';var escape=/["\\\x00-\x1f\x7f-\x9f]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},hasOwn=Object.prototype.hasOwnProperty;$.toJSON=typeof JSON==='object'&&JSON.stringify?JSON.stringify:function(o){if(o===null){return'null';}
var pairs,k,name,val,type=$.type(o);if(type==='undefined'){return undefined;}
if(type==='number'||type==='boolean'){return String(o);}
if(type==='string'){return $.quoteString(o);}
if(typeof o.toJSON==='function'){return $.toJSON(o.toJSON());}
if(type==='date'){var month=o.getUTCMonth()+1,day=o.getUTCDate(),year=o.getUTCFullYear(),hours=o.getUTCHours(),minutes=o.getUTCMinutes(),seconds=o.getUTCSeconds(),milli=o.getUTCMilliseconds();if(month<10){month='0'+month;}
if(day<10){day='0'+day;}
if(hours<10){hours='0'+hours;}
if(minutes<10){minutes='0'+minutes;}
if(seconds<10){seconds='0'+seconds;}
if(milli<100){milli='0'+milli;}
if(milli<10){milli='0'+milli;}
return'"'+year+'-'+month+'-'+day+'T'+
hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
pairs=[];if($.isArray(o)){for(k=0;k<o.length;k++){pairs.push($.toJSON(o[k])||'null');}
return'['+pairs.join(',')+']';}
if(typeof o==='object'){for(k in o){if(hasOwn.call(o,k)){type=typeof k;if(type==='number'){name='"'+k+'"';}else if(type==='string'){name=$.quoteString(k);}else{continue;}
type=typeof o[k];if(type!=='function'&&type!=='undefined'){val=$.toJSON(o[k]);pairs.push(name+':'+val);}}}
return'{'+pairs.join(',')+'}';}};$.evalJSON=typeof JSON==='object'&&JSON.parse?JSON.parse:function(str){return eval('('+str+')');};$.secureEvalJSON=typeof JSON==='object'&&JSON.parse?JSON.parse:function(str){var filtered=str.replace(/\\["\\\/bfnrtu]/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered)){return eval('('+str+')');}
throw new SyntaxError('Error parsing JSON, source is not valid.');};$.quoteString=function(str){if(str.match(escape)){return'"'+str.replace(escape,function(a){var c=meta[a];if(typeof c==='string'){return c;}
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
return'"'+str+'"';};}(jQuery));
\ No newline at end of file
/*global window, jQuery */
/*!
* route.js v1.0.0
*
* Copyright 2012, Romain Courteaud
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Date: Mon Jul 16 2012
*/
"use strict";
(function (window, $) {
$.extend({
StatelessDeferred: function () {
var doneList = $.Callbacks("memory"),
promise = {
done: doneList.add,
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function (obj) {
var i,
keys = ['done', 'promise'];
if (obj === undefined) {
obj = promise;
} else {
for (i = 0; i < keys.length; i += 1) {
obj[keys[i]] = promise[keys[i]];
}
}
return obj;
}
},
deferred = promise.promise({});
deferred.resolveWith = doneList.fireWith;
// All done!
return deferred;
}
});
var routes = [],
current_priority = 0,
methods = {
add: function (pattern, priority) {
var i = 0,
inserted = false,
length = routes.length,
dfr = $.StatelessDeferred(),
context = $(this),
escapepattern,
matchingpattern;
if (priority === undefined) {
priority = 0;
}
if (pattern !== undefined) {
// http://simonwillison.net/2006/Jan/20/escape/
escapepattern = pattern.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
matchingpattern = escapepattern
.replace(/<int:\w+>/g, "(\\d+)")
.replace(/<path:\w+>/g, "(.+)")
.replace(/<\w+>/g, "([^/]+)");
while (!inserted) {
if ((i === length) || (priority >= routes[i][2])) {
routes.splice(i, 0, [new RegExp('^' + matchingpattern + '$'), dfr, priority, context]);
inserted = true;
} else {
i += 1;
}
}
}
return dfr.promise();
},
go: function (path, min_priority) {
var dfr = $.Deferred(),
context = $(this),
result;
if (min_priority === undefined) {
min_priority = 0;
}
setTimeout(function () {
var i = 0,
found = false,
slice_index = -1,
slice_priority = -1;
for (i = 0; i < routes.length; i += 1) {
if (slice_priority !== routes[i][2]) {
slice_priority = routes[i][2];
slice_index = i;
}
if (routes[i][2] < min_priority) {
break;
} else if (routes[i][0].test(path)) {
result = routes[i][0].exec(path);
dfr = routes[i][1];
context = routes[i][3];
current_priority = routes[i][2];
found = true;
break;
}
}
if (i === routes.length) {
slice_index = i;
}
if (slice_index > -1) {
routes = routes.slice(slice_index);
}
if (found) {
dfr.resolveWith(
context,
result.slice(1)
);
} else {
dfr.rejectWith(context);
}
});
return dfr.promise();
},
};
$.routereset = function () {
routes = [];
current_priority = 0;
};
$.routepriority = function () {
return current_priority;
};
$.fn.route = function (method) {
var result;
if (methods.hasOwnProperty(method)) {
result = methods[method].apply(
this,
Array.prototype.slice.call(arguments, 1)
);
} else {
$.error('Method ' + method +
' does not exist on jQuery.route');
}
return result;
};
}(window, jQuery));
/*!
* url.js v1.0.0
*
* Copyright 2012, Romain Courteaud
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Date: Mon Jul 16 2012
*/
"use strict";
(function (window, $) {
var hashchangeinitialized = false,
previousurl,
currentcallback,
getRawHash = function () {
return window.location.toString().split('#')[1];
},
callbackwrapper = function () {
if (previousurl !== window.location.hash) {
previousurl = window.location.hash;
if (currentcallback !== undefined) {
currentcallback();
}
}
},
timeoutwrapper = function () {
callbackwrapper();
window.setTimeout(timeoutwrapper, 500);
};
function UrlHandler() {}
UrlHandler.prototype = {
'generateUrl': function (path, options) {
var pathhash,
hash = '#',
key;
if (path !== undefined) {
hash += encodeURIComponent(path);
}
hash = hash.replace(/%2F/g, '/');
pathhash = hash;
for (key in options) {
if (options.hasOwnProperty(key)) {
if (hash === pathhash) {
hash = hash + '?';
} else {
hash = hash + '&';
}
hash += encodeURIComponent(key) +
'=' + encodeURIComponent(options[key]);
}
}
return hash;
},
'go': function (path, options) {
window.location.hash = this.generateUrl(path, options);
},
'redirect': function (path, options) {
var host = window.location.protocol + '//' +
window.location.host +
window.location.pathname +
window.location.search;
window.location.replace(host + this.generateUrl(path, options));
// window.location.replace(window.location.href.replace(/#.*/, ""));
},
'getPath': function () {
var hash = getRawHash(),
result = '';
if (hash !== undefined) {
result = decodeURIComponent(hash.split('?')[0]);
}
return result;
},
'getOptions': function () {
var options = {},
hash = getRawHash(),
subhashes,
subhash,
index,
keyvalue;
if (hash !== undefined) {
hash = hash.split('?')[1];
if (hash !== undefined) {
subhashes = hash.split('&');
for (index in subhashes) {
if (subhashes.hasOwnProperty(index)) {
subhash = subhashes[index];
if (subhash !== '') {
keyvalue = subhash.split('=');
if (keyvalue.length === 2) {
options[decodeURIComponent(keyvalue[0])] =
decodeURIComponent(keyvalue[1]);
}
}
}
}
}
}
return options;
},
'onhashchange': function (callback) {
previousurl = undefined;
currentcallback = callback;
if (!hashchangeinitialized) {
if (window.onhashchange !== undefined) {
$(window).bind('hashchange', callbackwrapper);
window.setTimeout(callbackwrapper);
} else {
timeoutwrapper();
}
hashchangeinitialized = true;
}
},
};
// Expose to the global object
$.url = new UrlHandler();
}(window, jQuery));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment