Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
jio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Aurélien Vermylen
jio
Commits
01783ead
Commit
01783ead
authored
Oct 04, 2013
by
Tristan Cavelier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
built complex_queries.js and jio.js added
parent
9ee199f8
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
4920 additions
and
0 deletions
+4920
-0
complex_queries.js
complex_queries.js
+1645
-0
jio.js
jio.js
+3275
-0
No files found.
complex_queries.js
0 → 100644
View file @
01783ead
/*
* 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
(
"
\n
State
"
+
sstack
[
sstack
.
length
-
1
]
+
"
\n
"
+
"
\t
Lookahead:
"
+
labels
[
la
]
+
"
(
\"
"
+
info
.
att
+
"
\"
)
\n
"
+
"
\t
Action:
"
+
act
+
"
\n
"
+
"
\t
Source:
\"
"
+
info
.
src
.
substr
(
info
.
offset
,
30
)
+
(
(
info
.
offset
+
30
<
info
.
src
.
length
)
?
"
...
"
:
""
)
+
"
\"\n
"
+
"
\t
Stack:
"
+
sstack
.
join
()
+
"
\n
"
+
"
\t
Value 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
(
"
\t
Error 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
(
"
\t
Error recovery failed, terminating parse process...
"
);
break
;
}
if
(
NODEJS__dbg_withtrace
)
__NODEJS_dbg_print
(
"
\t
Error 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
(
"
\t
New 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
(
"
\t
Performing 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
(
"
\t
Popping
"
+
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
(
"
\t
Pushing 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
(
"
\n
Parse 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
(
!
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
)
{
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
;
}));
jio.js
0 → 100644
View file @
01783ead
(
function
(
dependencies
,
module
)
{
"
use strict
"
;
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
return
define
(
dependencies
,
module
);
}
if
(
typeof
exports
===
'
object
'
)
{
return
module
(
exports
,
require
(
'
rsvp
'
),
require
(
'
sha256
'
));
}
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
"
,
"
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
,
"
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
"
,
"
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 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
;
}
/**
* 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
,
"
sameMethod
"
:
sameMethod
,
"
sameDocumentId
"
:
sameDocumentId
,
"
sameParameters
"
:
sameParameters
,
"
sameOptions
"
:
sameOptions
};
}());
/*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
;
/**
* A faster version of `array.indexOf(value)` -> `indexOf(value, array)`
*
* @param {Any} value The value to search for
* @param {Array} array The array to browse
* @return {Number} index of value, -1 otherwise
*/
function
indexOf
(
value
,
array
)
{
var
i
;
for
(
i
=
0
;
i
<
array
.
length
;
i
+=
1
)
{
if
(
array
[
i
]
===
value
)
{
return
i
;
}
}
return
-
1
;
}
exports
.
util
.
indexOf
=
indexOf
;
/**
* 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
;
/**
* Returns the number with the lowest value
*
* @param {Number} *values The values to compare
* @return {Number} The minimum
*/
function
min
()
{
var
i
,
val
;
for
(
i
=
1
;
i
<
arguments
.
length
;
i
+=
1
)
{
if
(
val
===
undefined
||
val
>
arguments
[
i
])
{
val
=
arguments
[
i
];
}
}
return
val
;
}
exports
.
util
.
min
=
min
;
/**
* Returns the number with the greatest value
*
* @param {Number} *values The values to compare
* @return {Number} The maximum
*/
function
max
()
{
var
i
,
val
;
for
(
i
=
1
;
i
<
arguments
.
length
;
i
+=
1
)
{
if
(
val
===
undefined
||
val
<
arguments
[
i
])
{
val
=
arguments
[
i
];
}
}
return
val
;
}
exports
.
util
.
max
=
max
;
/**
* 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
);
enableJobChecker
(
this
,
shared
,
options
);
enableJobQueue
(
this
,
shared
,
options
);
enableJobRecovery
(
this
,
shared
,
options
);
enableJobTimeout
(
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
(
typeof
metadata
!==
'
object
'
||
Object
.
getPrototypeOf
(
metadata
||
[])
!==
Object
.
prototype
)
{
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
a
=
args
[
0
],
b
=
args
[
1
],
c
=
args
[
2
],
d
=
args
[
3
],
weak
,
strong
;
weak
=
{
"
result
"
:
"
error
"
};
strong
=
{};
weak
.
status
=
constants
.
http_status
.
unknown
;
weak
.
statusText
=
constants
.
http_status_text
.
unknown
;
weak
.
message
=
'
Command failed
'
;
weak
.
reason
=
'
fail
'
;
weak
.
method
=
param
.
method
;
if
(
param
.
kwargs
.
_id
)
{
weak
.
id
=
param
.
kwargs
.
_id
;
}
if
(
/Attachment$/
.
test
(
param
.
method
))
{
weak
.
attachment
=
param
.
kwargs
.
_attachment
;
}
if
(
typeof
a
!==
'
object
'
||
Array
.
isArray
(
a
))
{
strong
.
status
=
constants
.
http_status
[
a
];
strong
.
statusText
=
constants
.
http_status_text
[
a
];
if
(
strong
.
status
===
undefined
||
strong
.
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 "
'
+
a
+
'
"
'
]);
}
a
=
b
;
b
=
c
;
c
=
d
;
}
if
(
typeof
a
!==
'
object
'
||
Array
.
isArray
(
a
))
{
strong
.
reason
=
a
;
a
=
b
;
b
=
c
;
}
if
(
typeof
a
!==
'
object
'
||
Array
.
isArray
(
a
))
{
strong
.
message
=
a
;
a
=
b
;
}
if
(
typeof
a
===
'
object
'
&&
!
Array
.
isArray
(
a
))
{
dictUpdate
(
weak
,
a
);
}
dictUpdate
(
weak
,
strong
);
strong
=
undefined
;
if
(
weak
.
error
===
undefined
)
{
weak
.
error
=
weak
.
statusText
.
toLowerCase
().
replace
(
/ /g
,
'
_
'
).
replace
(
/
[^
_a-z
]
/g
,
''
);
}
if
(
typeof
weak
.
message
!==
'
string
'
)
{
weak
.
message
=
""
;
}
if
(
typeof
weak
.
reason
!==
'
string
'
)
{
weak
.
reason
=
"
unknown
"
;
}
return
param
.
solver
.
reject
(
deepClone
(
weak
));
}
/*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
a
=
args
[
0
],
b
=
args
[
1
],
weak
=
{
"
result
"
:
"
success
"
},
strong
=
{};
if
(
param
.
method
===
'
post
'
)
{
weak
.
status
=
constants
.
http_status
.
created
;
weak
.
statusText
=
constants
.
http_status_text
.
created
;
}
else
if
(
methodType
(
param
.
method
)
===
"
writer
"
||
param
.
method
===
"
check
"
)
{
weak
.
status
=
constants
.
http_status
.
no_content
;
weak
.
statusText
=
constants
.
http_status_text
.
no_content
;
}
else
{
weak
.
status
=
constants
.
http_status
.
ok
;
weak
.
statusText
=
constants
.
http_status_text
.
ok
;
}
if
(
param
.
kwargs
.
_id
)
{
weak
.
id
=
param
.
kwargs
.
_id
;
}
if
(
/Attachment$/
.
test
(
param
.
method
))
{
weak
.
attachment
=
param
.
kwargs
.
_attachment
;
}
weak
.
method
=
param
.
method
;
if
(
typeof
a
===
'
string
'
||
(
typeof
a
===
'
number
'
&&
isFinite
(
a
)))
{
strong
.
status
=
constants
.
http_status
[
a
];
strong
.
statusText
=
constants
.
http_status_text
[
a
];
if
(
strong
.
status
===
undefined
||
strong
.
statusText
===
undefined
)
{
return
restCommandRejecter
(
param
,
[
'
internal_storage_error
'
,
'
invalid response
'
,
'
Unknown status "
'
+
a
+
'
"
'
]);
}
a
=
b
;
}
if
(
typeof
a
===
'
object
'
&&
!
Array
.
isArray
(
a
))
{
dictUpdate
(
weak
,
a
);
}
dictUpdate
(
weak
,
strong
);
strong
=
undefined
;
// free memory
if
(
param
.
method
===
'
post
'
&&
(
typeof
weak
.
id
!==
'
string
'
||
!
weak
.
id
))
{
return
restCommandRejecter
(
param
,
[
'
internal_storage_error
'
,
'
invalid response
'
,
'
New document id have to be specified
'
]);
}
if
(
param
.
method
===
'
getAttachment
'
)
{
if
(
typeof
weak
.
data
===
'
string
'
)
{
weak
.
data
=
new
Blob
([
weak
.
data
],
{
"
type
"
:
weak
.
content_type
||
weak
.
mimetype
||
""
});
delete
weak
.
content_type
;
delete
weak
.
mimetype
;
}
if
(
!
(
weak
.
data
instanceof
Blob
))
{
return
restCommandRejecter
(
param
,
[
'
internal_storage_error
'
,
'
invalid response
'
,
'
getAttachment method needs a Blob as returned "data".
'
]);
}
}
else
if
(
methodType
(
param
.
method
)
===
'
reader
'
&&
param
.
method
!==
'
check
'
&&
(
typeof
weak
.
data
!==
'
object
'
||
Object
.
getPrototypeOf
(
weak
.
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
(
weak
));
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayInsert, indexOf, deepClone, defaults, restCommandRejecter */
// 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' event
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
()
{
shared
.
emit
(
'
job
'
,
new_job
);
});
new_job
.
state
=
'
waiting
'
;
new_job
.
modified
=
new
Date
();
},
update
:
function
(
original_job
,
new_job
)
{
if
(
!
new_job
.
solver
)
{
// promise associated to the job
new_job
.
state
=
'
done
'
;
shared
.
emit
(
'
jobDone
'
,
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
.
state
=
'
running
'
;
new_job
.
modified
=
new
Date
();
},
deny
:
function
(
original_job
,
new_job
)
{
new_job
.
state
=
'
fail
'
;
new_job
.
modified
=
new
Date
();
restCommandRejecter
(
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
(
indexOf
(
job_rule
.
action
,
shared
.
job_rule_action_names
)
===
-
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
=
0
;
j
<
shared
.
jobs
.
length
;
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
"
:
"
writers update
"
,
"
conditions
"
:
[
"
sameStorageDescription
"
,
"
areWriters
"
,
"
sameMethod
"
,
"
sameParameters
"
],
"
action
"
:
"
update
"
},
{
"
code_name
"
:
"
writers wait
"
,
"
conditions
"
:
[
"
sameStorageDescription
"
,
"
areWriters
"
,
"
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
'
,
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, min, restCommandResolver,
restCommandRejecter */
function
enableJobExecuter
(
jio
,
shared
)
{
// , options) {
// uses 'job', 'jobDone', 'jobFail' and 'jobNotify' events
// emits 'jobRun' and 'jobEnd' events
// listeners
shared
.
on
(
'
job
'
,
function
(
param
)
{
var
storage
;
if
(
param
.
state
===
'
ready
'
)
{
param
.
tried
+=
1
;
param
.
started
=
new
Date
();
param
.
state
=
'
running
'
;
param
.
modified
=
new
Date
();
shared
.
emit
(
'
jobRun
'
,
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
)
);
});
}
});
shared
.
on
(
'
jobDone
'
,
function
(
param
,
args
)
{
if
(
param
.
state
===
'
running
'
)
{
param
.
state
=
'
done
'
;
param
.
modified
=
new
Date
();
shared
.
emit
(
'
jobEnd
'
,
param
);
if
(
param
.
solver
)
{
restCommandResolver
(
param
,
args
);
}
}
});
shared
.
on
(
'
jobFail
'
,
function
(
param
,
args
)
{
if
(
param
.
state
===
'
running
'
)
{
param
.
state
=
'
fail
'
;
param
.
modified
=
new
Date
();
shared
.
emit
(
'
jobEnd
'
,
param
);
if
(
param
.
solver
)
{
restCommandRejecter
(
param
,
args
);
}
}
});
shared
.
on
(
'
jobNotify
'
,
function
(
param
,
args
)
{
if
(
param
.
state
===
'
running
'
&&
param
.
solver
)
{
param
.
solver
.
notify
(
args
[
0
]);
}
});
}
/*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
// uses method events
// add emits 'job' events
// the job can emit 'jobDone', 'jobFail' and 'jobNotify'
shared
.
job_keys
=
arrayExtend
(
shared
.
job_keys
||
[],
[
"
created
"
,
"
modified
"
,
"
tried
"
,
"
state
"
,
"
method
"
,
"
storage_spec
"
,
"
kwargs
"
,
"
options
"
]);
function
addCommandToJob
(
param
)
{
param
.
command
=
{};
param
.
command
.
resolve
=
function
()
{
shared
.
emit
(
'
jobDone
'
,
param
,
arguments
);
};
param
.
command
.
success
=
param
.
command
.
resolve
;
param
.
command
.
reject
=
function
()
{
shared
.
emit
(
'
jobFail
'
,
param
,
arguments
);
};
param
.
command
.
error
=
param
.
command
.
reject
;
param
.
command
.
notify
=
function
()
{
shared
.
emit
(
'
jobNotify
'
,
param
,
arguments
);
};
param
.
command
.
storage
=
function
()
{
return
shared
.
createRestApi
.
apply
(
null
,
arguments
);
};
}
// listeners
shared
.
rest_method_names
.
forEach
(
function
(
method
)
{
shared
.
on
(
method
,
function
(
param
)
{
if
(
param
.
solver
)
{
// params are good
shared
.
emit
(
'
job
'
,
param
);
}
});
});
shared
.
on
(
'
job
'
,
function
(
param
)
{
// new or recovered job
param
.
state
=
'
ready
'
;
if
(
typeof
param
.
tried
!==
'
number
'
||
!
isFinite
(
param
.
tried
))
{
param
.
tried
=
0
;
}
if
(
!
param
.
created
)
{
param
.
created
=
new
Date
();
}
if
(
!
param
.
command
)
{
addCommandToJob
(
param
);
}
param
.
modified
=
new
Date
();
});
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, localStorage, Workspace, uniqueJSONStringify, JobQueue,
constants, indexOf */
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', 'jobRun', 'jobStop', 'jobEnd' events
// emits 'jobEnd' events
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
);
shared
.
on
(
'
job
'
,
function
(
param
)
{
if
(
indexOf
(
param
.
state
,
[
'
fail
'
,
'
done
'
])
===
-
1
)
{
if
(
!
param
.
stored
)
{
shared
.
job_queue
.
load
();
shared
.
job_queue
.
post
(
param
);
shared
.
job_queue
.
save
();
param
.
stored
=
true
;
}
}
});
[
'
jobRun
'
,
'
jobStop
'
].
forEach
(
function
(
event
)
{
shared
.
on
(
event
,
function
(
param
)
{
if
(
param
.
stored
)
{
shared
.
job_queue
.
load
();
if
(
param
.
state
===
'
done
'
||
param
.
state
===
'
fail
'
)
{
if
(
shared
.
job_queue
.
remove
(
param
.
id
))
{
shared
.
job_queue
.
save
();
delete
param
.
storad
;
}
}
else
{
shared
.
job_queue
.
put
(
param
);
shared
.
job_queue
.
save
();
}
}
});
});
shared
.
on
(
'
jobEnd
'
,
function
(
param
)
{
if
(
param
.
stored
)
{
shared
.
job_queue
.
load
();
if
(
shared
.
job_queue
.
remove
(
param
.
id
))
{
shared
.
job_queue
.
save
();
}
}
});
}
shared
.
on
(
'
job
'
,
function
(
param
)
{
if
(
!
param
.
command
.
end
)
{
param
.
command
.
end
=
function
()
{
shared
.
emit
(
'
jobEnd
'
,
param
);
};
}
});
}
/*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
function
numberOrDefault
(
number
,
default_value
)
{
return
(
typeof
number
===
'
number
'
&&
isFinite
(
number
)
?
number
:
default_value
);
}
function
recoverJob
(
param
)
{
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
'
,
param
);
}
}
function
jobWaiter
(
id
,
modified
)
{
return
function
()
{
var
job
;
shared
.
job_queue
.
load
();
job
=
shared
.
job_queue
.
get
(
id
);
if
(
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', 'jobEnd' events
shared
.
jobs
=
[];
var
job_references
=
new
ReferenceArray
(
shared
.
jobs
);
shared
.
on
(
'
job
'
,
function
(
param
)
{
job_references
.
put
(
param
);
});
shared
.
on
(
'
jobEnd
'
,
function
(
param
)
{
job_references
.
remove
(
param
);
});
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, setTimeout, methodType, min, 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' and 'jobRetry' events
// emits 'job', 'jobFail' and 'jobStateChange' events
// job can emit 'jobRetry'
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
);
// listeners
shared
.
on
(
'
job
'
,
function
(
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
(
'
jobRetry
'
,
param
,
arguments
);
}
else
{
shared
.
emit
(
'
jobFail
'
,
param
,
arguments
);
}
};
param
.
command
.
retry
=
function
()
{
shared
.
emit
(
'
jobRetry
'
,
param
,
arguments
);
};
});
shared
.
on
(
'
jobRetry
'
,
function
(
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
(
'
jobStop
'
,
param
);
setTimeout
(
function
()
{
param
.
state
=
'
ready
'
;
param
.
modified
=
new
Date
();
shared
.
emit
(
'
job
'
,
param
);
},
min
(
10000
,
param
.
tried
*
2000
));
}
else
{
shared
.
emit
(
'
jobFail
'
,
param
,
args
);
}
}
});
}
/*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', 'jobDone', 'jobFail', 'jobRetry' and 'jobNotify' events
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.
'
);
};
}
// listeners
shared
.
on
(
'
job
'
,
function
(
param
)
{
if
(
typeof
param
.
timeout
!==
'
number
'
||
param
.
timeout
<
0
)
{
param
.
timeout
=
positiveNumberOrDefault
(
param
.
options
.
timeout
,
default_timeout
);
}
param
.
modified
=
new
Date
();
});
[
"
jobDone
"
,
"
jobFail
"
,
"
jobRetry
"
].
forEach
(
function
(
event
)
{
shared
.
on
(
event
,
function
(
param
)
{
clearTimeout
(
param
.
timeout_ident
);
delete
param
.
timeout_ident
;
});
});
[
"
jobRun
"
,
"
jobNotify
"
,
"
jobEnd
"
].
forEach
(
function
(
event
)
{
shared
.
on
(
event
,
function
(
param
)
{
clearTimeout
(
param
.
timeout_ident
);
if
(
param
.
state
===
'
running
'
&&
param
.
timeout
>
0
)
{
param
.
timeout_ident
=
setTimeout
(
timeoutReject
(
param
),
param
.
timeout
);
param
.
modified
=
new
Date
();
}
else
{
delete
param
.
timeout_ident
;
}
});
});
}
/*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
;
}
}
});
});
}
}));
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment