Commit 4b86c4a1 authored by Romain Courteaud's avatar Romain Courteaud

WIP Jio rewrite.

This is a work in progress commit and all storages are broken.

* Use as much as possible RSVP promise to simplify the code.
* simplify error management: use Error class in case of problem.
* Drop the job management at that point. Such functionnality could be reimplemented as a storage.
* Start to simplify the tests, to speed up debugging.
* Drop requireJS usage, as the goal for now is to make it usage on browser.
* allDocs has to be rewritten to ease storage tree capacity checking. A storage should only implement what it could really do.
* disable attachment management for now.
parent c74c465d
/*jslint indent: 2, maxlen: 80 */
/*global module */
module.exports = function (grunt) { module.exports = function (grunt) {
"use strict"; "use strict";
var LIVERELOAD_PORT, lrSnippet, livereloadMiddleware;
// This is the default port that livereload listens on;
// change it if you configure livereload to use another port.
LIVERELOAD_PORT = 35729;
// lrSnippet is just a function.
// It's a piece of Connect middleware that injects
// a script into the static served html.
lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
// All the middleware necessary to serve static files.
livereloadMiddleware = function (connect, options) {
return [
// Inject a livereloading script into static files.
lrSnippet,
// Serve static files.
connect.static(options.base),
// Make empty directories browsable.
connect.directory(options.base)
];
};
grunt.loadNpmTasks('grunt-jslint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-open');
// Project configuration. // Project configuration.
grunt.initConfig({ grunt.initConfig({
pkg: grunt.file.readJSON('package.json'), pkg: grunt.file.readJSON('package.json'),
jslint: { jslint: {
npm: {
src: ['package.json'],
directives: {
maxlen: 100,
indent: 2,
maxerr: 3,
predef: [
'module'
]
}
},
grunt: { grunt: {
src: ['Gruntfile.js'], src: ['Gruntfile.js'],
options: { directives: {
errorsOnly: true es5: true,
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'module',
'require'
]
} }
}, },
jio: { jio: {
src: ['src/jio/**/*.js'], src: ['src/jio.js', 'src/jio/**/*.js', 'src/jio/*.js'],
exclude: ['src/jio/intro.js', 'src/jio/outro.js'], exclude: ['src/jio/intro.js', 'src/jio/outro.js'],
options: { directives: {
errorsOnly: true maxlen: 80,
indent: 2,
maxerr: 3,
nomen: true
} }
}, },
jio_storages: { jio_storages: {
src: ['src/jio.storage/**/*.js'], src: ['src/jio.storage/**/*.js'],
options: { directives: {
errorsOnly: true maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'define',
'exports',
'require',
'window',
'jIO',
'complex_queries'
]
} }
}, },
jiodate: { jiodate: {
src: ['src/jio.date/jiodate.js'] src: ['src/jio.date/jiodate.js'],
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'jIO'
]
},
}, },
tests: { tests: {
src: ['test/**/*.js'], src: ['test/**/*.js'],
options: { directives: {
errorsOnly: true maxlen: 80,
} indent: 2,
maxerr: 3,
predef: [
'RSVP',
'QUnit',
'jIO'
]
},
}, },
queries: { queries: {
src: ['src/queries/core/**/*.js'], src: ['src/queries/core/**/*.js'],
...@@ -47,19 +120,40 @@ module.exports = function (grunt) { ...@@ -47,19 +120,40 @@ module.exports = function (grunt) {
options: { options: {
errorsOnly: true errorsOnly: true
} }
} },
examples: {
src: ['examples/*.js'],
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'window',
'RSVP',
'rJS',
'QUnit',
'jIO'
]
},
},
}, },
concat: { concat: {
options: {
separator: ';'
},
jio: { jio: {
// duplicate files are ignored // duplicate files are ignored
src: [ src: [
'src/jio/intro.js', // 'node_modules/moment/moment.js',
'lib/moment/moment-2.5.0.js',
// core // 'src/*.js',
'src/jio/core/globals.js', // 'src/jio/intro.js',
'src/jio/core/util.js', //
'src/jio/core/**/*.js', // // core
'src/jio/features/**/*.js', // 'src/jio/core/globals.js',
// 'src/jio/core/util.js',
// 'src/jio/core/**/*.js',
// 'src/jio/features/**/*.js',
// queries // queries
'src/queries/core/globals.js', 'src/queries/core/globals.js',
...@@ -70,9 +164,19 @@ module.exports = function (grunt) { ...@@ -70,9 +164,19 @@ module.exports = function (grunt) {
'src/queries/core/tools.js', 'src/queries/core/tools.js',
'src/queries/core/**/*.js', 'src/queries/core/**/*.js',
'src/jio/outro.js' 'src/jio.date/*.js',
// 'src/jio/outro.js',
'src/jio.js',
'src/jio.storage/localstorage.js',
'src/jio.storage/davstorage.js',
'src/jio.storage/indexeddbstorage.js',
'src/jio.storage/unionstorage.js',
'src/jio.storage/querystorage.js'
], ],
dest: 'jio.js' dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.js'
// dest: 'jio.js'
} }
}, },
uglify: { uglify: {
...@@ -81,37 +185,91 @@ module.exports = function (grunt) { ...@@ -81,37 +185,91 @@ module.exports = function (grunt) {
'<%= grunt.template.today("yyyy-mm-dd") %> */\n' '<%= grunt.template.today("yyyy-mm-dd") %> */\n'
}, },
jio: { jio: {
src: 'jio.js', // '<%= pkg.name %>.js' src: "<%= concat.jio.dest %>",
dest: 'jio.min.js', dest: "dist/<%= pkg.name %>-<%= pkg.version %>.min.js"
// },
// jio: {
// src: 'jio.js', // '<%= pkg.name %>.js'
// dest: 'jio.min.js',
// options: {
// sourceMap: "jio.min.map"
// }
// },
// jiodate: {
// src: 'src/jio.date/jiodate.js',
// dest: 'jiodate.min.js',
// options: {
// sourceMap: "jiodate.min.map"
// }
}
},
copy: {
latest: {
files: [{
src: '<%= uglify.jio.src %>',
dest: "dist/<%= pkg.name %>-latest.js"
}, {
src: '<%= uglify.jio.dest %>',
dest: "dist/<%= pkg.name %>-latest.min.js"
}]
}
},
qunit: {
// grunt doesn't like requirejs
files: ['test/tests.html']
},
watch: {
src: {
files: [
'<%= jslint.npm.src %>',
'<%= jslint.grunt.src %>',
'<%= jslint.jio.src %>',
'<%= jslint.jiodate.src %>',
'<%= jslint.jio_storages.src %>',
'<%= jslint.tests.src %>',
'<%= jslint.queries.src %>',
'<%= qunit.files %>',
'test/**/*.js',
'examples/*'
],
tasks: ['default'],
options: { options: {
sourceMap: "jio.min.map" livereload: LIVERELOAD_PORT
} }
}, }
jiodate: { },
src: 'src/jio.date/jiodate.js',
dest: 'jiodate.min.js',
connect: {
client: {
options: { options: {
sourceMap: "jiodate.min.map" port: 9000,
base: '.',
directory: '.',
middleware: livereloadMiddleware
} }
} }
}, },
qunit: {
// grunt doesn't like requirejs open: {
files: ['test/tests.html'], all: {
options: { // Gets the port from the connect configuration
timeout: 30000 // if no test occurs for 30 seconds, then timeout path: 'http://localhost:' +
'<%= connect.client.options.port%>/test/tests.html'
} }
} }
});
grunt.loadNpmTasks('grunt-jslint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.registerTask('default', ['jslint', 'concat', 'uglify', 'qunit']); });
grunt.registerTask('default', ['all']);
grunt.registerTask('all', ['lint', 'build']);
grunt.registerTask('lint', ['jslint']); grunt.registerTask('lint', ['jslint']);
grunt.registerTask('build', ['concat', 'uglify']); grunt.registerTask('test', ['qunit']);
grunt.registerTask('test', ['jslint', 'qunit']); grunt.registerTask('server', ['connect:client', 'watch']);
grunt.registerTask('build', ['concat', 'uglify', 'copy']);
}; };
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jIO Coverage Scenario</title>
<link rel="stylesheet" href="../node_modules/grunt-contrib-qunit/test/libs/qunit.css" type="text/css" media="screen"/>
<script src="../node_modules/grunt-contrib-qunit/test/libs/qunit.js" type="text/javascript"></script>
<script src="../node_modules/rsvp/dist/rsvp-2.0.4.js"></script>
<script src="../node_modules/renderjs/dist/renderjs-latest.js"></script>
<script src="../dist/jio-latest.js"></script>
<script src="scenario.js"></script>
</head>
<body>
<h1 id="qunit-header">jIO Coverage Scenario</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body>
</html>
/*global console, btoa*/
/*jslint nomen: true*/
(function (window, QUnit, jIO, rJS) {
"use strict";
var test = QUnit.test,
equal = QUnit.equal,
expect = QUnit.expect,
ok = QUnit.ok,
stop = QUnit.stop,
start = QUnit.start,
deepEqual = QUnit.deepEqual;
rJS(window)
.ready(function (g) {
return g.run({
"type": "indexeddb",
"database": "test"
});
})
.ready(function (g) {
return g.run({
"type": "local"
});
})
.ready(function (g) {
return g.run({
"type": "dav"
});
})
.declareMethod('run', function (jio_options) {
test('Test "' + jio_options.type + '"scenario', function () {
var jio;
stop();
expect(3);
try {
jio = jIO.createJIO(jio_options);
} catch (error) {
console.error(error.stack);
console.error(error);
throw error;
}
// Try to fetch inexistent document
jio.get({"_id": "inexistent"})
.fail(function (error) {
equal(error.status_code, 404, "404 if inexistent");
// Post a document without ID
return jio.post({"title": "I don't have ID"});
})
.then(function (doc_id) {
ok(doc_id, "Document without ID created");
// Fetch the newly created document
return jio.get({"_id": doc_id});
})
.then(function (doc) {
var doc_id = doc._id;
delete doc._id;
deepEqual(doc, {"title": "I don't have ID"},
"Document correctly fetched");
// Remove the doc
return jio.remove({"_id": doc_id});
})
.then(function (doc_id) {
ok(doc_id, "Document removed");
})
.fail(function (error) {
console.error(error.stack);
console.error(error);
ok(false, error);
})
.always(function () {
start();
});
});
});
}(window, QUnit, jIO, rJS));
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* QUnit v1.13.0pre-780a7a98935a907a2e4c57777b3bbe28ea0ea1ec 2013-07-26 - A JavaScript Unit Testing Framework
*
* http://qunitjs.com
*
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-testrunner-toolbar label {
display: inline-block;
padding: 0 .5em 0 .1em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
overflow: hidden;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3c510c;
background-color: #fff;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
font-weight: bold;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}
/**
* QUnit v1.13.0pre-780a7a98935a907a2e4c57777b3bbe28ea0ea1ec 2013-07-26 - A JavaScript Unit Testing Framework
*
* http://qunitjs.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* https://jquery.org/license/
*/
(function( window ) {
var QUnit,
assert,
config,
onErrorFnPrev,
testId = 0,
fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
// Keep a local reference to Date (GH-283)
Date = window.Date,
setTimeout = window.setTimeout,
defined = {
setTimeout: typeof window.setTimeout !== "undefined",
sessionStorage: (function() {
var x = "qunit-test-string";
try {
sessionStorage.setItem( x, x );
sessionStorage.removeItem( x );
return true;
} catch( e ) {
return false;
}
}())
},
/**
* Provides a normalized error string, correcting an issue
* with IE 7 (and prior) where Error.prototype.toString is
* not properly implemented
*
* Based on http://es5.github.com/#x15.11.4.4
*
* @param {String|Error} error
* @return {String} error message
*/
errorString = function( error ) {
var name, message,
errorString = error.toString();
if ( errorString.substring( 0, 7 ) === "[object" ) {
name = error.name ? error.name.toString() : "Error";
message = error.message ? error.message.toString() : "";
if ( name && message ) {
return name + ": " + message;
} else if ( name ) {
return name;
} else if ( message ) {
return message;
} else {
return "Error";
}
} else {
return errorString;
}
},
/**
* Makes a clone of an object using only Array or Object as base,
* and copies over the own enumerable properties.
*
* @param {Object} obj
* @return {Object} New object with only the own properties (recursively).
*/
objectValues = function( obj ) {
// Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
/*jshint newcap: false */
var key, val,
vals = QUnit.is( "array", obj ) ? [] : {};
for ( key in obj ) {
if ( hasOwn.call( obj, key ) ) {
val = obj[key];
vals[key] = val === Object(val) ? objectValues(val) : val;
}
}
return vals;
};
function Test( settings ) {
extend( this, settings );
this.assertions = [];
this.testNumber = ++Test.count;
}
Test.count = 0;
Test.prototype = {
init: function() {
var a, b, li,
tests = id( "qunit-tests" );
if ( tests ) {
b = document.createElement( "strong" );
b.innerHTML = this.nameHtml;
// `a` initialized at top of scope
a = document.createElement( "a" );
a.innerHTML = "Rerun";
a.href = QUnit.url({ testNumber: this.testNumber });
li = document.createElement( "li" );
li.appendChild( b );
li.appendChild( a );
li.className = "running";
li.id = this.id = "qunit-test-output" + testId++;
tests.appendChild( li );
}
},
setup: function() {
if (
// Emit moduleStart when we're switching from one module to another
this.module !== config.previousModule ||
// They could be equal (both undefined) but if the previousModule property doesn't
// yet exist it means this is the first test in a suite that isn't wrapped in a
// module, in which case we'll just emit a moduleStart event for 'undefined'.
// Without this, reporters can get testStart before moduleStart which is a problem.
!hasOwn.call( config, "previousModule" )
) {
if ( hasOwn.call( config, "previousModule" ) ) {
runLoggingCallbacks( "moduleDone", QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
});
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
runLoggingCallbacks( "moduleStart", QUnit, {
name: this.module
});
}
config.current = this;
this.testEnvironment = extend({
setup: function() {},
teardown: function() {}
}, this.moduleTestEnvironment );
this.started = +new Date();
runLoggingCallbacks( "testStart", QUnit, {
name: this.testName,
module: this.module
});
/*jshint camelcase:false */
/**
* Expose the current test environment.
*
* @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
*/
QUnit.current_testEnvironment = this.testEnvironment;
/*jshint camelcase:true */
if ( !config.pollution ) {
saveGlobal();
}
if ( config.notrycatch ) {
this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
return;
}
try {
this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
}
},
run: function() {
config.current = this;
var running = id( "qunit-testresult" );
if ( running ) {
running.innerHTML = "Running: <br/>" + this.nameHtml;
}
if ( this.async ) {
QUnit.stop();
}
this.callbackStarted = +new Date();
if ( config.notrycatch ) {
this.callback.call( this.testEnvironment, QUnit.assert );
this.callbackRuntime = +new Date() - this.callbackStarted;
return;
}
try {
this.callback.call( this.testEnvironment, QUnit.assert );
this.callbackRuntime = +new Date() - this.callbackStarted;
} catch( e ) {
this.callbackRuntime = +new Date() - this.callbackStarted;
QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
// else next test will carry the responsibility
saveGlobal();
// Restart the tests if they're blocking
if ( config.blocking ) {
QUnit.start();
}
}
},
teardown: function() {
config.current = this;
if ( config.notrycatch ) {
if ( typeof this.callbackRuntime === "undefined" ) {
this.callbackRuntime = +new Date() - this.callbackStarted;
}
this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
return;
} else {
try {
this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
}
}
checkPollution();
},
finish: function() {
config.current = this;
if ( config.requireExpects && this.expected === null ) {
QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
} else if ( this.expected !== null && this.expected !== this.assertions.length ) {
QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
} else if ( this.expected === null && !this.assertions.length ) {
QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
}
var i, assertion, a, b, time, li, ol,
test = this,
good = 0,
bad = 0,
tests = id( "qunit-tests" );
this.runtime = +new Date() - this.started;
config.stats.all += this.assertions.length;
config.moduleStats.all += this.assertions.length;
if ( tests ) {
ol = document.createElement( "ol" );
ol.className = "qunit-assert-list";
for ( i = 0; i < this.assertions.length; i++ ) {
assertion = this.assertions[i];
li = document.createElement( "li" );
li.className = assertion.result ? "pass" : "fail";
li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
ol.appendChild( li );
if ( assertion.result ) {
good++;
} else {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
// store result when possible
if ( QUnit.config.reorder && defined.sessionStorage ) {
if ( bad ) {
sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
} else {
sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
}
}
if ( bad === 0 ) {
addClass( ol, "qunit-collapsed" );
}
// `b` initialized at top of scope
b = document.createElement( "strong" );
b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
addEvent(b, "click", function() {
var next = b.parentNode.lastChild,
collapsed = hasClass( next, "qunit-collapsed" );
( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
});
addEvent(b, "dblclick", function( e ) {
var target = e && e.target ? e.target : window.event.srcElement;
if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
target = target.parentNode;
}
if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
window.location = QUnit.url({ testNumber: test.testNumber });
}
});
// `time` initialized at top of scope
time = document.createElement( "span" );
time.className = "runtime";
time.innerHTML = this.runtime + " ms";
// `li` initialized at top of scope
li = id( this.id );
li.className = bad ? "fail" : "pass";
li.removeChild( li.firstChild );
a = li.firstChild;
li.appendChild( b );
li.appendChild( a );
li.appendChild( time );
li.appendChild( ol );
} else {
for ( i = 0; i < this.assertions.length; i++ ) {
if ( !this.assertions[i].result ) {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
}
runLoggingCallbacks( "testDone", QUnit, {
name: this.testName,
module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length,
duration: this.runtime
});
QUnit.reset();
config.current = undefined;
},
queue: function() {
var bad,
test = this;
synchronize(function() {
test.init();
});
function run() {
// each of these can by async
synchronize(function() {
test.setup();
});
synchronize(function() {
test.run();
});
synchronize(function() {
test.teardown();
});
synchronize(function() {
test.finish();
});
}
// `bad` initialized at top of scope
// defer when previous test run passed, if storage is available
bad = QUnit.config.reorder && defined.sessionStorage &&
+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
if ( bad ) {
run();
} else {
synchronize( run, true );
}
}
};
// Root QUnit object.
// `QUnit` initialized at top of scope
QUnit = {
// call on start of module test to prepend name to all tests
module: function( name, testEnvironment ) {
config.currentModule = name;
config.currentModuleTestEnvironment = testEnvironment;
config.modules[name] = true;
},
asyncTest: function( testName, expected, callback ) {
if ( arguments.length === 2 ) {
callback = expected;
expected = null;
}
QUnit.test( testName, expected, callback, true );
},
test: function( testName, expected, callback, async ) {
var test,
nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
if ( arguments.length === 2 ) {
callback = expected;
expected = null;
}
if ( config.currentModule ) {
nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
}
test = new Test({
nameHtml: nameHtml,
testName: testName,
expected: expected,
async: async,
callback: callback,
module: config.currentModule,
moduleTestEnvironment: config.currentModuleTestEnvironment,
stack: sourceFromStacktrace( 2 )
});
if ( !validTest( test ) ) {
return;
}
test.queue();
},
// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
expect: function( asserts ) {
if (arguments.length === 1) {
config.current.expected = asserts;
} else {
return config.current.expected;
}
},
start: function( count ) {
// QUnit hasn't been initialized yet.
// Note: RequireJS (et al) may delay onLoad
if ( config.semaphore === undefined ) {
QUnit.begin(function() {
// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
setTimeout(function() {
QUnit.start( count );
});
});
return;
}
config.semaphore -= count || 1;
// don't start until equal number of stop-calls
if ( config.semaphore > 0 ) {
return;
}
// ignore if start is called more often then stop
if ( config.semaphore < 0 ) {
config.semaphore = 0;
QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
return;
}
// A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) {
setTimeout(function() {
if ( config.semaphore > 0 ) {
return;
}
if ( config.timeout ) {
clearTimeout( config.timeout );
}
config.blocking = false;
process( true );
}, 13);
} else {
config.blocking = false;
process( true );
}
},
stop: function( count ) {
config.semaphore += count || 1;
config.blocking = true;
if ( config.testTimeout && defined.setTimeout ) {
clearTimeout( config.timeout );
config.timeout = setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
}, config.testTimeout );
}
}
};
// `assert` initialized at top of scope
// Assert helpers
// All of these must either call QUnit.push() or manually do:
// - runLoggingCallbacks( "log", .. );
// - config.current.assertions.push({ .. });
// We attach it to the QUnit object *after* we expose the public API,
// otherwise `assert` will become a global variable in browsers (#341).
assert = {
/**
* Asserts rough true-ish result.
* @name ok
* @function
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function( result, msg ) {
if ( !config.current ) {
throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
}
result = !!result;
msg = msg || (result ? "okay" : "failed" );
var source,
details = {
module: config.current.module,
name: config.current.testName,
result: result,
message: msg
};
msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";
if ( !result ) {
source = sourceFromStacktrace( 2 );
if ( source ) {
details.source = source;
msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";
}
}
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
result: result,
message: msg
});
},
/**
* Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
* @name equal
* @function
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/
equal: function( actual, expected, message ) {
/*jshint eqeqeq:false */
QUnit.push( expected == actual, actual, expected, message );
},
/**
* @name notEqual
* @function
*/
notEqual: function( actual, expected, message ) {
/*jshint eqeqeq:false */
QUnit.push( expected != actual, actual, expected, message );
},
/**
* @name propEqual
* @function
*/
propEqual: function( actual, expected, message ) {
actual = objectValues(actual);
expected = objectValues(expected);
QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name notPropEqual
* @function
*/
notPropEqual: function( actual, expected, message ) {
actual = objectValues(actual);
expected = objectValues(expected);
QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name deepEqual
* @function
*/
deepEqual: function( actual, expected, message ) {
QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name notDeepEqual
* @function
*/
notDeepEqual: function( actual, expected, message ) {
QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name strictEqual
* @function
*/
strictEqual: function( actual, expected, message ) {
QUnit.push( expected === actual, actual, expected, message );
},
/**
* @name notStrictEqual
* @function
*/
notStrictEqual: function( actual, expected, message ) {
QUnit.push( expected !== actual, actual, expected, message );
},
"throws": function( block, expected, message ) {
var actual,
expectedOutput = expected,
ok = false;
// 'expected' is optional
if ( typeof expected === "string" ) {
message = expected;
expected = null;
}
config.current.ignoreGlobalErrors = true;
try {
block.call( config.current.testEnvironment );
} catch (e) {
actual = e;
}
config.current.ignoreGlobalErrors = false;
if ( actual ) {
// we don't want to validate thrown error
if ( !expected ) {
ok = true;
expectedOutput = null;
// expected is a regexp
} else if ( QUnit.objectType( expected ) === "regexp" ) {
ok = expected.test( errorString( actual ) );
// expected is a constructor
} else if ( actual instanceof expected ) {
ok = true;
// expected is a validation function which returns true is validation passed
} else if ( expected.call( {}, actual ) === true ) {
expectedOutput = null;
ok = true;
}
QUnit.push( ok, actual, expectedOutput, message );
} else {
QUnit.pushFailure( message, null, "No exception was thrown." );
}
}
};
/**
* @deprecated since 1.8.0
* Kept assertion helpers in root for backwards compatibility.
*/
extend( QUnit, assert );
/**
* @deprecated since 1.9.0
* Kept root "raises()" for backwards compatibility.
* (Note that we don't introduce assert.raises).
*/
QUnit.raises = assert[ "throws" ];
/**
* @deprecated since 1.0.0, replaced with error pushes since 1.3.0
* Kept to avoid TypeErrors for undefined methods.
*/
QUnit.equals = function() {
QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
};
QUnit.same = function() {
QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
};
// We want access to the constructor's prototype
(function() {
function F() {}
F.prototype = QUnit;
QUnit = new F();
// Make F QUnit's constructor so that we can add to the prototype later
QUnit.constructor = F;
}());
/**
* Config object: Maintain internal state
* Later exposed as QUnit.config
* `config` initialized at top of scope
*/
config = {
// The queue of tests to run
queue: [],
// block until document ready
blocking: true,
// when enabled, show only failing tests
// gets persisted through sessionStorage and can be changed in UI via checkbox
hidepassed: false,
// by default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked
reorder: true,
// by default, modify document.title when suite is done
altertitle: true,
// when enabled, all tests must call expect()
requireExpects: false,
// add checkboxes that are persisted in the query-string
// when enabled, the id is set to `true` as a `QUnit.config` property
urlConfig: [
{
id: "noglobals",
label: "Check for Globals",
tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
},
{
id: "notrycatch",
label: "No try-catch",
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
}
],
// Set of all modules.
modules: {},
// logging callback queues
begin: [],
done: [],
log: [],
testStart: [],
testDone: [],
moduleStart: [],
moduleDone: []
};
// Export global variables, unless an 'exports' object exists,
// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
if ( typeof exports === "undefined" ) {
extend( window, QUnit.constructor.prototype );
// Expose QUnit object
window.QUnit = QUnit;
}
// Initialize more QUnit.config and QUnit.urlParams
(function() {
var i,
location = window.location || { search: "", protocol: "file:" },
params = location.search.slice( 1 ).split( "&" ),
length = params.length,
urlParams = {},
current;
if ( params[ 0 ] ) {
for ( i = 0; i < length; i++ ) {
current = params[ i ].split( "=" );
current[ 0 ] = decodeURIComponent( current[ 0 ] );
// allow just a key to turn on a flag, e.g., test.html?noglobals
current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
urlParams[ current[ 0 ] ] = current[ 1 ];
}
}
QUnit.urlParams = urlParams;
// String search anywhere in moduleName+testName
config.filter = urlParams.filter;
// Exact match of the module name
config.module = urlParams.module;
config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
// Figure out if we're running the tests from a server or not
QUnit.isLocal = location.protocol === "file:";
}());
// Extend QUnit object,
// these after set here because they should not be exposed as global functions
extend( QUnit, {
assert: assert,
config: config,
// Initialize the configuration options
init: function() {
extend( config, {
stats: { all: 0, bad: 0 },
moduleStats: { all: 0, bad: 0 },
started: +new Date(),
updateRate: 1000,
blocking: false,
autostart: true,
autorun: false,
filter: "",
queue: [],
semaphore: 1
});
var tests, banner, result,
qunit = id( "qunit" );
if ( qunit ) {
qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
"<h2 id='qunit-banner'></h2>" +
"<div id='qunit-testrunner-toolbar'></div>" +
"<h2 id='qunit-userAgent'></h2>" +
"<ol id='qunit-tests'></ol>";
}
tests = id( "qunit-tests" );
banner = id( "qunit-banner" );
result = id( "qunit-testresult" );
if ( tests ) {
tests.innerHTML = "";
}
if ( banner ) {
banner.className = "";
}
if ( result ) {
result.parentNode.removeChild( result );
}
if ( tests ) {
result = document.createElement( "p" );
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests );
result.innerHTML = "Running...<br/>&nbsp;";
}
},
// Resets the test setup. Useful for tests that modify the DOM.
/*
DEPRECATED: Use multiple tests instead of resetting inside a test.
Use testStart or testDone for custom cleanup.
This method will throw an error in 2.0, and will be removed in 2.1
*/
reset: function() {
var fixture = id( "qunit-fixture" );
if ( fixture ) {
fixture.innerHTML = config.fixture;
}
},
// Trigger an event on an element.
// @example triggerEvent( document.body, "click" );
triggerEvent: function( elem, type, event ) {
if ( document.createEvent ) {
event = document.createEvent( "MouseEvents" );
event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
elem.dispatchEvent( event );
} else if ( elem.fireEvent ) {
elem.fireEvent( "on" + type );
}
},
// Safe object type checking
is: function( type, obj ) {
return QUnit.objectType( obj ) === type;
},
objectType: function( obj ) {
if ( typeof obj === "undefined" ) {
return "undefined";
// consider: typeof null === object
}
if ( obj === null ) {
return "null";
}
var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
type = match && match[1] || "";
switch ( type ) {
case "Number":
if ( isNaN(obj) ) {
return "nan";
}
return "number";
case "String":
case "Boolean":
case "Array":
case "Date":
case "RegExp":
case "Function":
return type.toLowerCase();
}
if ( typeof obj === "object" ) {
return "object";
}
return undefined;
},
push: function( result, actual, expected, message ) {
if ( !config.current ) {
throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
}
var output, source,
details = {
module: config.current.module,
name: config.current.testName,
result: result,
message: message,
actual: actual,
expected: expected
};
message = escapeText( message ) || ( result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>";
output = message;
if ( !result ) {
expected = escapeText( QUnit.jsDump.parse(expected) );
actual = escapeText( QUnit.jsDump.parse(actual) );
output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
if ( actual !== expected ) {
output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
}
source = sourceFromStacktrace();
if ( source ) {
details.source = source;
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
}
output += "</table>";
}
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
result: !!result,
message: output
});
},
pushFailure: function( message, source, actual ) {
if ( !config.current ) {
throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
}
var output,
details = {
module: config.current.module,
name: config.current.testName,
result: false,
message: message
};
message = escapeText( message ) || "error";
message = "<span class='test-message'>" + message + "</span>";
output = message;
output += "<table>";
if ( actual ) {
output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
}
if ( source ) {
details.source = source;
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
}
output += "</table>";
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
result: false,
message: output
});
},
url: function( params ) {
params = extend( extend( {}, QUnit.urlParams ), params );
var key,
querystring = "?";
for ( key in params ) {
if ( hasOwn.call( params, key ) ) {
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
}
return window.location.protocol + "//" + window.location.host +
window.location.pathname + querystring.slice( 0, -1 );
},
extend: extend,
id: id,
addEvent: addEvent,
addClass: addClass,
hasClass: hasClass,
removeClass: removeClass
// load, equiv, jsDump, diff: Attached later
});
/**
* @deprecated: Created for backwards compatibility with test runner that set the hook function
* into QUnit.{hook}, instead of invoking it and passing the hook function.
* QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
* Doing this allows us to tell if the following methods have been overwritten on the actual
* QUnit object.
*/
extend( QUnit.constructor.prototype, {
// Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes
begin: registerLoggingCallback( "begin" ),
// done: { failed, passed, total, runtime }
done: registerLoggingCallback( "done" ),
// log: { result, actual, expected, message }
log: registerLoggingCallback( "log" ),
// testStart: { name }
testStart: registerLoggingCallback( "testStart" ),
// testDone: { name, failed, passed, total, duration }
testDone: registerLoggingCallback( "testDone" ),
// moduleStart: { name }
moduleStart: registerLoggingCallback( "moduleStart" ),
// moduleDone: { name, failed, passed, total }
moduleDone: registerLoggingCallback( "moduleDone" )
});
if ( typeof document === "undefined" || document.readyState === "complete" ) {
config.autorun = true;
}
QUnit.load = function() {
runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue
var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
numModules = 0,
moduleNames = [],
moduleFilterHtml = "",
urlConfigHtml = "",
oldconfig = extend( {}, config );
QUnit.init();
extend(config, oldconfig);
config.blocking = false;
len = config.urlConfig.length;
for ( i = 0; i < len; i++ ) {
val = config.urlConfig[i];
if ( typeof val === "string" ) {
val = {
id: val,
label: val,
tooltip: "[no tooltip available]"
};
}
config[ val.id ] = QUnit.urlParams[ val.id ];
urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
"' name='" + escapeText( val.id ) +
"' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +
" title='" + escapeText( val.tooltip ) +
"'><label for='qunit-urlconfig-" + escapeText( val.id ) +
"' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
}
for ( i in config.modules ) {
if ( config.modules.hasOwnProperty( i ) ) {
moduleNames.push(i);
}
}
numModules = moduleNames.length;
moduleNames.sort( function( a, b ) {
return a.localeCompare( b );
});
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
( config.module === undefined ? "selected='selected'" : "" ) +
">< All Modules ></option>";
for ( i = 0; i < numModules; i++) {
moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
">" + escapeText(moduleNames[i]) + "</option>";
}
moduleFilterHtml += "</select>";
// `userAgent` initialized at top of scope
userAgent = id( "qunit-userAgent" );
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
// `banner` initialized at top of scope
banner = id( "qunit-header" );
if ( banner ) {
banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
}
// `toolbar` initialized at top of scope
toolbar = id( "qunit-testrunner-toolbar" );
if ( toolbar ) {
// `filter` initialized at top of scope
filter = document.createElement( "input" );
filter.type = "checkbox";
filter.id = "qunit-filter-pass";
addEvent( filter, "click", function() {
var tmp,
ol = id( "qunit-tests" );
if ( filter.checked ) {
ol.className = ol.className + " hidepass";
} else {
tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
ol.className = tmp.replace( / hidepass /, " " );
}
if ( defined.sessionStorage ) {
if (filter.checked) {
sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
} else {
sessionStorage.removeItem( "qunit-filter-passed-tests" );
}
}
});
if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
filter.checked = true;
// `ol` initialized at top of scope
ol = id( "qunit-tests" );
ol.className = ol.className + " hidepass";
}
toolbar.appendChild( filter );
// `label` initialized at top of scope
label = document.createElement( "label" );
label.setAttribute( "for", "qunit-filter-pass" );
label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
label.innerHTML = "Hide passed tests";
toolbar.appendChild( label );
urlConfigCheckboxesContainer = document.createElement("span");
urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
// For oldIE support:
// * Add handlers to the individual elements instead of the container
// * Use "click" instead of "change"
// * Fallback from event.target to event.srcElement
addEvents( urlConfigCheckboxes, "click", function( event ) {
var params = {},
target = event.target || event.srcElement;
params[ target.name ] = target.checked ? true : undefined;
window.location = QUnit.url( params );
});
toolbar.appendChild( urlConfigCheckboxesContainer );
if (numModules > 1) {
moduleFilter = document.createElement( "span" );
moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
moduleFilter.innerHTML = moduleFilterHtml;
addEvent( moduleFilter.lastChild, "change", function() {
var selectBox = moduleFilter.getElementsByTagName("select")[0],
selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
window.location = QUnit.url({
module: ( selectedModule === "" ) ? undefined : selectedModule,
// Remove any existing filters
filter: undefined,
testNumber: undefined
});
});
toolbar.appendChild(moduleFilter);
}
}
// `main` initialized at top of scope
main = id( "qunit-fixture" );
if ( main ) {
config.fixture = main.innerHTML;
}
if ( config.autostart ) {
QUnit.start();
}
};
addEvent( window, "load", QUnit.load );
// `onErrorFnPrev` initialized at top of scope
// Preserve other handlers
onErrorFnPrev = window.onerror;
// Cover uncaught exceptions
// Returning true will suppress the default browser handler,
// returning false will let it run.
window.onerror = function ( error, filePath, linerNr ) {
var ret = false;
if ( onErrorFnPrev ) {
ret = onErrorFnPrev( error, filePath, linerNr );
}
// Treat return value as window.onerror itself does,
// Only do our handling if not suppressed.
if ( ret !== true ) {
if ( QUnit.config.current ) {
if ( QUnit.config.current.ignoreGlobalErrors ) {
return true;
}
QUnit.pushFailure( error, filePath + ":" + linerNr );
} else {
QUnit.test( "global failure", extend( function() {
QUnit.pushFailure( error, filePath + ":" + linerNr );
}, { validTest: validTest } ) );
}
return false;
}
return ret;
};
function done() {
config.autorun = true;
// Log the last module results
if ( config.currentModule ) {
runLoggingCallbacks( "moduleDone", QUnit, {
name: config.currentModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
});
}
delete config.previousModule;
var i, key,
banner = id( "qunit-banner" ),
tests = id( "qunit-tests" ),
runtime = +new Date() - config.started,
passed = config.stats.all - config.stats.bad,
html = [
"Tests completed in ",
runtime,
" milliseconds.<br/>",
"<span class='passed'>",
passed,
"</span> assertions of <span class='total'>",
config.stats.all,
"</span> passed, <span class='failed'>",
config.stats.bad,
"</span> failed."
].join( "" );
if ( banner ) {
banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
}
if ( tests ) {
id( "qunit-testresult" ).innerHTML = html;
}
if ( config.altertitle && typeof document !== "undefined" && document.title ) {
// show ✖ for good, ✔ for bad suite result in title
// use escape sequences in case file gets loaded with non-utf-8-charset
document.title = [
( config.stats.bad ? "\u2716" : "\u2714" ),
document.title.replace( /^[\u2714\u2716] /i, "" )
].join( " " );
}
// clear own sessionStorage items if all tests passed
if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
// `key` & `i` initialized at top of scope
for ( i = 0; i < sessionStorage.length; i++ ) {
key = sessionStorage.key( i++ );
if ( key.indexOf( "qunit-test-" ) === 0 ) {
sessionStorage.removeItem( key );
}
}
}
// scroll back to top to show results
if ( window.scrollTo ) {
window.scrollTo(0, 0);
}
runLoggingCallbacks( "done", QUnit, {
failed: config.stats.bad,
passed: passed,
total: config.stats.all,
runtime: runtime
});
}
/** @return Boolean: true if this test should be ran */
function validTest( test ) {
var include,
filter = config.filter && config.filter.toLowerCase(),
module = config.module && config.module.toLowerCase(),
fullName = (test.module + ": " + test.testName).toLowerCase();
// Internally-generated tests are always valid
if ( test.callback && test.callback.validTest === validTest ) {
delete test.callback.validTest;
return true;
}
if ( config.testNumber ) {
return test.testNumber === config.testNumber;
}
if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
return false;
}
if ( !filter ) {
return true;
}
include = filter.charAt( 0 ) !== "!";
if ( !include ) {
filter = filter.slice( 1 );
}
// If the filter matches, we need to honour include
if ( fullName.indexOf( filter ) !== -1 ) {
return include;
}
// Otherwise, do the opposite
return !include;
}
// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
// Later Safari and IE10 are supposed to support error.stack as well
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
function extractStacktrace( e, offset ) {
offset = offset === undefined ? 3 : offset;
var stack, include, i;
if ( e.stacktrace ) {
// Opera
return e.stacktrace.split( "\n" )[ offset + 3 ];
} else if ( e.stack ) {
// Firefox, Chrome
stack = e.stack.split( "\n" );
if (/^error$/i.test( stack[0] ) ) {
stack.shift();
}
if ( fileName ) {
include = [];
for ( i = offset; i < stack.length; i++ ) {
if ( stack[ i ].indexOf( fileName ) !== -1 ) {
break;
}
include.push( stack[ i ] );
}
if ( include.length ) {
return include.join( "\n" );
}
}
return stack[ offset ];
} else if ( e.sourceURL ) {
// Safari, PhantomJS
// hopefully one day Safari provides actual stacktraces
// exclude useless self-reference for generated Error objects
if ( /qunit.js$/.test( e.sourceURL ) ) {
return;
}
// for actual exceptions, this is useful
return e.sourceURL + ":" + e.line;
}
}
function sourceFromStacktrace( offset ) {
try {
throw new Error();
} catch ( e ) {
return extractStacktrace( e, offset );
}
}
/**
* Escape text for attribute or text content.
*/
function escapeText( s ) {
if ( !s ) {
return "";
}
s = s + "";
// Both single quotes and double quotes (for attributes)
return s.replace( /['"<>&]/g, function( s ) {
switch( s ) {
case "'":
return "&#039;";
case "\"":
return "&quot;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case "&":
return "&amp;";
}
});
}
function synchronize( callback, last ) {
config.queue.push( callback );
if ( config.autorun && !config.blocking ) {
process( last );
}
}
function process( last ) {
function next() {
process( last );
}
var start = new Date().getTime();
config.depth = config.depth ? config.depth + 1 : 1;
while ( config.queue.length && !config.blocking ) {
if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
config.queue.shift()();
} else {
setTimeout( next, 13 );
break;
}
}
config.depth--;
if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
done();
}
}
function saveGlobal() {
config.pollution = [];
if ( config.noglobals ) {
for ( var key in window ) {
if ( hasOwn.call( window, key ) ) {
// in Opera sometimes DOM element ids show up here, ignore them
if ( /^qunit-test-output/.test( key ) ) {
continue;
}
config.pollution.push( key );
}
}
}
}
function checkPollution() {
var newGlobals,
deletedGlobals,
old = config.pollution;
saveGlobal();
newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) {
QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
}
deletedGlobals = diff( old, config.pollution );
if ( deletedGlobals.length > 0 ) {
QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
}
}
// returns a new Array with the elements that are in a but not in b
function diff( a, b ) {
var i, j,
result = a.slice();
for ( i = 0; i < result.length; i++ ) {
for ( j = 0; j < b.length; j++ ) {
if ( result[i] === b[j] ) {
result.splice( i, 1 );
i--;
break;
}
}
}
return result;
}
function extend( a, b ) {
for ( var prop in b ) {
if ( hasOwn.call( b, prop ) ) {
// Avoid "Member not found" error in IE8 caused by messing with window.constructor
if ( !( prop === "constructor" && a === window ) ) {
if ( b[ prop ] === undefined ) {
delete a[ prop ];
} else {
a[ prop ] = b[ prop ];
}
}
}
}
return a;
}
/**
* @param {HTMLElement} elem
* @param {string} type
* @param {Function} fn
*/
function addEvent( elem, type, fn ) {
// Standards-based browsers
if ( elem.addEventListener ) {
elem.addEventListener( type, fn, false );
// IE
} else {
elem.attachEvent( "on" + type, fn );
}
}
/**
* @param {Array|NodeList} elems
* @param {string} type
* @param {Function} fn
*/
function addEvents( elems, type, fn ) {
var i = elems.length;
while ( i-- ) {
addEvent( elems[i], type, fn );
}
}
function hasClass( elem, name ) {
return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
}
function addClass( elem, name ) {
if ( !hasClass( elem, name ) ) {
elem.className += (elem.className ? " " : "") + name;
}
}
function removeClass( elem, name ) {
var set = " " + elem.className + " ";
// Class name may appear multiple times
while ( set.indexOf(" " + name + " ") > -1 ) {
set = set.replace(" " + name + " " , " ");
}
// If possible, trim it for prettiness, but not necessarily
elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
}
function id( name ) {
return !!( typeof document !== "undefined" && document && document.getElementById ) &&
document.getElementById( name );
}
function registerLoggingCallback( key ) {
return function( callback ) {
config[key].push( callback );
};
}
// Supports deprecated method of completely overwriting logging callbacks
function runLoggingCallbacks( key, scope, args ) {
var i, callbacks;
if ( QUnit.hasOwnProperty( key ) ) {
QUnit[ key ].call(scope, args );
} else {
callbacks = config[ key ];
for ( i = 0; i < callbacks.length; i++ ) {
callbacks[ i ].call( scope, args );
}
}
}
// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = (function() {
// Call the o related callback with the given arguments.
function bindCallbacks( o, callbacks, args ) {
var prop = QUnit.objectType( o );
if ( prop ) {
if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
return callbacks[ prop ].apply( callbacks, args );
} else {
return callbacks[ prop ]; // or undefined
}
}
}
// the real equiv function
var innerEquiv,
// stack to decide between skip/abort functions
callers = [],
// stack to avoiding loops from circular referencing
parents = [],
parentsB = [],
getProto = Object.getPrototypeOf || function ( obj ) {
/*jshint camelcase:false */
return obj.__proto__;
},
callbacks = (function () {
// for string, boolean, number and null
function useStrictEquality( b, a ) {
/*jshint eqeqeq:false */
if ( b instanceof a.constructor || a instanceof b.constructor ) {
// to catch short annotation VS 'new' annotation of a
// declaration
// e.g. var i = 1;
// var j = new Number(1);
return a == b;
} else {
return a === b;
}
}
return {
"string": useStrictEquality,
"boolean": useStrictEquality,
"number": useStrictEquality,
"null": useStrictEquality,
"undefined": useStrictEquality,
"nan": function( b ) {
return isNaN( b );
},
"date": function( b, a ) {
return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
},
"regexp": function( b, a ) {
return QUnit.objectType( b ) === "regexp" &&
// the regex itself
a.source === b.source &&
// and its modifiers
a.global === b.global &&
// (gmi) ...
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline &&
a.sticky === b.sticky;
},
// - skip when the property is a method of an instance (OOP)
// - abort otherwise,
// initial === would have catch identical references anyway
"function": function() {
var caller = callers[callers.length - 1];
return caller !== Object && typeof caller !== "undefined";
},
"array": function( b, a ) {
var i, j, len, loop, aCircular, bCircular;
// b could be an object literal here
if ( QUnit.objectType( b ) !== "array" ) {
return false;
}
len = a.length;
if ( len !== b.length ) {
// safe and faster
return false;
}
// track reference to avoid circular references
parents.push( a );
parentsB.push( b );
for ( i = 0; i < len; i++ ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
aCircular = parents[j] === a[i];
bCircular = parentsB[j] === b[i];
if ( aCircular || bCircular ) {
if ( a[i] === b[i] || aCircular && bCircular ) {
loop = true;
} else {
parents.pop();
parentsB.pop();
return false;
}
}
}
if ( !loop && !innerEquiv(a[i], b[i]) ) {
parents.pop();
parentsB.pop();
return false;
}
}
parents.pop();
parentsB.pop();
return true;
},
"object": function( b, a ) {
/*jshint forin:false */
var i, j, loop, aCircular, bCircular,
// Default to true
eq = true,
aProperties = [],
bProperties = [];
// comparing constructors is more strict than using
// instanceof
if ( a.constructor !== b.constructor ) {
// Allow objects with no prototype to be equivalent to
// objects with Object as their constructor.
if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
return false;
}
}
// stack constructor before traversing properties
callers.push( a.constructor );
// track reference to avoid circular references
parents.push( a );
parentsB.push( b );
// be strict: don't ensure hasOwnProperty and go deep
for ( i in a ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
aCircular = parents[j] === a[i];
bCircular = parentsB[j] === b[i];
if ( aCircular || bCircular ) {
if ( a[i] === b[i] || aCircular && bCircular ) {
loop = true;
} else {
eq = false;
break;
}
}
}
aProperties.push(i);
if ( !loop && !innerEquiv(a[i], b[i]) ) {
eq = false;
break;
}
}
parents.pop();
parentsB.pop();
callers.pop(); // unstack, we are done
for ( i in b ) {
bProperties.push( i ); // collect b's properties
}
// Ensures identical properties name
return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
}
};
}());
innerEquiv = function() { // can take multiple arguments
var args = [].slice.apply( arguments );
if ( args.length < 2 ) {
return true; // end transition
}
return (function( a, b ) {
if ( a === b ) {
return true; // catch the most you can
} else if ( a === null || b === null || typeof a === "undefined" ||
typeof b === "undefined" ||
QUnit.objectType(a) !== QUnit.objectType(b) ) {
return false; // don't lose time with error prone cases
} else {
return bindCallbacks(a, callbacks, [ b, a ]);
}
// apply transition with (1..n) arguments
}( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
};
return innerEquiv;
}());
/**
* jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
* http://flesler.blogspot.com Licensed under BSD
* (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
*
* @projectDescription Advanced and extensible data dumping for Javascript.
* @version 1.0.0
* @author Ariel Flesler
* @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
*/
QUnit.jsDump = (function() {
function quote( str ) {
return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
}
function literal( o ) {
return o + "";
}
function join( pre, arr, post ) {
var s = jsDump.separator(),
base = jsDump.indent(),
inner = jsDump.indent(1);
if ( arr.join ) {
arr = arr.join( "," + s + inner );
}
if ( !arr ) {
return pre + post;
}
return [ pre, inner + arr, base + post ].join(s);
}
function array( arr, stack ) {
var i = arr.length, ret = new Array(i);
this.up();
while ( i-- ) {
ret[i] = this.parse( arr[i] , undefined , stack);
}
this.down();
return join( "[", ret, "]" );
}
var reName = /^function (\w+)/,
jsDump = {
// type is used mostly internally, you can fix a (custom)type in advance
parse: function( obj, type, stack ) {
stack = stack || [ ];
var inStack, res,
parser = this.parsers[ type || this.typeOf(obj) ];
type = typeof parser;
inStack = inArray( obj, stack );
if ( inStack !== -1 ) {
return "recursion(" + (inStack - stack.length) + ")";
}
if ( type === "function" ) {
stack.push( obj );
res = parser.call( this, obj, stack );
stack.pop();
return res;
}
return ( type === "string" ) ? parser : this.parsers.error;
},
typeOf: function( obj ) {
var type;
if ( obj === null ) {
type = "null";
} else if ( typeof obj === "undefined" ) {
type = "undefined";
} else if ( QUnit.is( "regexp", obj) ) {
type = "regexp";
} else if ( QUnit.is( "date", obj) ) {
type = "date";
} else if ( QUnit.is( "function", obj) ) {
type = "function";
} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
type = "window";
} else if ( obj.nodeType === 9 ) {
type = "document";
} else if ( obj.nodeType ) {
type = "node";
} else if (
// native arrays
toString.call( obj ) === "[object Array]" ||
// NodeList objects
( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
) {
type = "array";
} else if ( obj.constructor === Error.prototype.constructor ) {
type = "error";
} else {
type = typeof obj;
}
return type;
},
separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
},
// extra can be a number, shortcut for increasing-calling-decreasing
indent: function( extra ) {
if ( !this.multiline ) {
return "";
}
var chr = this.indentChar;
if ( this.HTML ) {
chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
}
return new Array( this.depth + ( extra || 0 ) ).join(chr);
},
up: function( a ) {
this.depth += a || 1;
},
down: function( a ) {
this.depth -= a || 1;
},
setParser: function( name, parser ) {
this.parsers[name] = parser;
},
// The next 3 are exposed so you can use them
quote: quote,
literal: literal,
join: join,
//
depth: 1,
// This is the list of parsers, to modify them, use jsDump.setParser
parsers: {
window: "[Window]",
document: "[Document]",
error: function(error) {
return "Error(\"" + error.message + "\")";
},
unknown: "[Unknown]",
"null": "null",
"undefined": "undefined",
"function": function( fn ) {
var ret = "function",
// functions never have name in IE
name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
if ( name ) {
ret += " " + name;
}
ret += "( ";
ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
},
array: array,
nodelist: array,
"arguments": array,
object: function( map, stack ) {
/*jshint forin:false */
var ret = [ ], keys, key, val, i;
QUnit.jsDump.up();
keys = [];
for ( key in map ) {
keys.push( key );
}
keys.sort();
for ( i = 0; i < keys.length; i++ ) {
key = keys[ i ];
val = map[ key ];
ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
}
QUnit.jsDump.down();
return join( "{", ret, "}" );
},
node: function( node ) {
var len, i, val,
open = QUnit.jsDump.HTML ? "&lt;" : "<",
close = QUnit.jsDump.HTML ? "&gt;" : ">",
tag = node.nodeName.toLowerCase(),
ret = open + tag,
attrs = node.attributes;
if ( attrs ) {
for ( i = 0, len = attrs.length; i < len; i++ ) {
val = attrs[i].nodeValue;
// IE6 includes all attributes in .attributes, even ones not explicitly set.
// Those have values like undefined, null, 0, false, "" or "inherit".
if ( val && val !== "inherit" ) {
ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
}
}
}
ret += close;
// Show content of TextNode or CDATASection
if ( node.nodeType === 3 || node.nodeType === 4 ) {
ret += node.nodeValue;
}
return ret + open + "/" + tag + close;
},
// function calls it internally, it's the arguments part of the function
functionArgs: function( fn ) {
var args,
l = fn.length;
if ( !l ) {
return "";
}
args = new Array(l);
while ( l-- ) {
// 97 is 'a'
args[l] = String.fromCharCode(97+l);
}
return " " + args.join( ", " ) + " ";
},
// object calls it internally, the key part of an item in a map
key: quote,
// function calls it internally, it's the content of the function
functionCode: "[code]",
// node calls it internally, it's an html attribute value
attribute: quote,
string: quote,
date: quote,
regexp: literal,
number: literal,
"boolean": literal
},
// if true, entities are escaped ( <, >, \t, space and \n )
HTML: false,
// indentation unit
indentChar: " ",
// if true, items in a collection, are separated by a \n, else just a space.
multiline: true
};
return jsDump;
}());
// from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
}
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/)
* Modified by Chu Alan "sprite"
*
* Released under the MIT license.
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*
* Usage: QUnit.diff(expected, actual)
*
* QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
*/
QUnit.diff = (function() {
/*jshint eqeqeq:false, eqnull:true */
function diff( o, n ) {
var i,
ns = {},
os = {};
for ( i = 0; i < n.length; i++ ) {
if ( !hasOwn.call( ns, n[i] ) ) {
ns[ n[i] ] = {
rows: [],
o: null
};
}
ns[ n[i] ].rows.push( i );
}
for ( i = 0; i < o.length; i++ ) {
if ( !hasOwn.call( os, o[i] ) ) {
os[ o[i] ] = {
rows: [],
n: null
};
}
os[ o[i] ].rows.push( i );
}
for ( i in ns ) {
if ( hasOwn.call( ns, i ) ) {
if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
n[ ns[i].rows[0] ] = {
text: n[ ns[i].rows[0] ],
row: os[i].rows[0]
};
o[ os[i].rows[0] ] = {
text: o[ os[i].rows[0] ],
row: ns[i].rows[0]
};
}
}
}
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
n[ i + 1 ] == o[ n[i].row + 1 ] ) {
n[ i + 1 ] = {
text: n[ i + 1 ],
row: n[i].row + 1
};
o[ n[i].row + 1 ] = {
text: o[ n[i].row + 1 ],
row: i + 1
};
}
}
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
n[ i - 1 ] == o[ n[i].row - 1 ]) {
n[ i - 1 ] = {
text: n[ i - 1 ],
row: n[i].row - 1
};
o[ n[i].row - 1 ] = {
text: o[ n[i].row - 1 ],
row: i - 1
};
}
}
return {
o: o,
n: n
};
}
return function( o, n ) {
o = o.replace( /\s+$/, "" );
n = n.replace( /\s+$/, "" );
var i, pre,
str = "",
out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
oSpace = o.match(/\s+/g),
nSpace = n.match(/\s+/g);
if ( oSpace == null ) {
oSpace = [ " " ];
}
else {
oSpace.push( " " );
}
if ( nSpace == null ) {
nSpace = [ " " ];
}
else {
nSpace.push( " " );
}
if ( out.n.length === 0 ) {
for ( i = 0; i < out.o.length; i++ ) {
str += "<del>" + out.o[i] + oSpace[i] + "</del>";
}
}
else {
if ( out.n[0].text == null ) {
for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
str += "<del>" + out.o[n] + oSpace[n] + "</del>";
}
}
for ( i = 0; i < out.n.length; i++ ) {
if (out.n[i].text == null) {
str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
}
else {
// `pre` initialized at top of scope
pre = "";
for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
}
str += " " + out.n[i].text + nSpace[i] + pre;
}
}
}
return str;
};
}());
// for CommonJS environments, export everything
if ( typeof exports !== "undefined" ) {
extend( exports, QUnit.constructor.prototype );
}
// get at whatever the global object is, like window in browsers
}( (function() {return this;}.call()) ));
define("rsvp/all",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
/* global toString */
function promiseAtLeast(expected_count, promises) {
if (Object.prototype.toString.call(promises) !== "[object Array]") {
throw new TypeError('You must pass an array to all.');
}
function canceller() {
var promise;
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function' &&
typeof promise.cancel === 'function') {
promise.cancel();
}
}
}
return new Promise(function(resolve, reject, notify) {
var results = [], remaining = promises.length,
promise, remaining_count = promises.length - expected_count;
if (remaining === 0) {
if (expected_count === 1) {
resolve();
} else {
resolve([]);
}
}
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function resolveAll(index, value) {
results[index] = value;
if (--remaining === remaining_count) {
if (remaining_count === 0) {
resolve(results);
} else {
resolve(value);
canceller();
}
}
}
function notifier(index) {
return function(value) {
notify({"index": index, "value": value});
};
}
function cancelAll(rejectionValue) {
reject(rejectionValue);
canceller();
}
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function') {
promise.then(resolver(i), cancelAll, notifier(i));
} else {
resolveAll(i, promise);
}
}
}, canceller
);
}
function all(promises) {
return promiseAtLeast(promises.length, promises);
}
function any(promises) {
return promiseAtLeast(1, promises);
}
__exports__.all = all;
__exports__.any = any;
});
define("rsvp/async",
["exports"],
function(__exports__) {
"use strict";
var browserGlobal = (typeof window !== 'undefined') ? window : {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var async;
var local = (typeof global !== 'undefined') ? global : this;
// old node
function useNextTick() {
return function(callback, arg) {
process.nextTick(function() {
callback(arg);
});
};
}
// node >= 0.10.x
function useSetImmediate() {
return function(callback, arg) {
/* global setImmediate */
setImmediate(function(){
callback(arg);
});
};
}
function useMutationObserver() {
var queue = [];
var observer = new BrowserMutationObserver(function() {
var toProcess = queue.slice();
queue = [];
toProcess.forEach(function(tuple) {
var callback = tuple[0], arg= tuple[1];
callback(arg);
});
});
var element = document.createElement('div');
observer.observe(element, { attributes: true });
// Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
window.addEventListener('unload', function(){
observer.disconnect();
observer = null;
}, false);
return function(callback, arg) {
queue.push([callback, arg]);
element.setAttribute('drainQueue', 'drainQueue');
};
}
function useSetTimeout() {
return function(callback, arg) {
local.setTimeout(function() {
callback(arg);
}, 1);
};
}
if (typeof setImmediate === 'function') {
async = useSetImmediate();
} else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
async = useNextTick();
} else if (BrowserMutationObserver) {
async = useMutationObserver();
} else {
async = useSetTimeout();
}
__exports__.async = async;
});
define("rsvp/cancellation_error",
["exports"],
function(__exports__) {
"use strict";
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function CancellationError(message) {
this.name = "cancel";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
CancellationError.prototype = new Error();
CancellationError.prototype.constructor = CancellationError;
__exports__.CancellationError = CancellationError;
});
define("rsvp/config",
["rsvp/async","exports"],
function(__dependency1__, __exports__) {
"use strict";
var async = __dependency1__.async;
var config = {};
config.async = async;
__exports__.config = config;
});
define("rsvp/defer",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function defer() {
var deferred = {
// pre-allocate shape
resolve: undefined,
reject: undefined,
promise: undefined
};
deferred.promise = new Promise(function(resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
__exports__.defer = defer;
});
define("rsvp/events",
["exports"],
function(__exports__) {
"use strict";
var Event = function(type, options) {
this.type = type;
for (var option in options) {
if (!options.hasOwnProperty(option)) { continue; }
this[option] = options[option];
}
};
var indexOf = function(callbacks, callback) {
for (var i=0, l=callbacks.length; i<l; i++) {
if (callbacks[i][0] === callback) { return i; }
}
return -1;
};
var callbacksFor = function(object) {
var callbacks = object._promiseCallbacks;
if (!callbacks) {
callbacks = object._promiseCallbacks = {};
}
return callbacks;
};
var EventTarget = {
mixin: function(object) {
object.on = this.on;
object.off = this.off;
object.trigger = this.trigger;
return object;
},
on: function(eventNames, callback, binding) {
var allCallbacks = callbacksFor(this), callbacks, eventName;
eventNames = eventNames.split(/\s+/);
binding = binding || this;
while (eventName = eventNames.shift()) {
callbacks = allCallbacks[eventName];
if (!callbacks) {
callbacks = allCallbacks[eventName] = [];
}
if (indexOf(callbacks, callback) === -1) {
callbacks.push([callback, binding]);
}
}
},
off: function(eventNames, callback) {
var allCallbacks = callbacksFor(this), callbacks, eventName, index;
eventNames = eventNames.split(/\s+/);
while (eventName = eventNames.shift()) {
if (!callback) {
allCallbacks[eventName] = [];
continue;
}
callbacks = allCallbacks[eventName];
index = indexOf(callbacks, callback);
if (index !== -1) { callbacks.splice(index, 1); }
}
},
trigger: function(eventName, options) {
var allCallbacks = callbacksFor(this),
callbacks, callbackTuple, callback, binding, event;
if (callbacks = allCallbacks[eventName]) {
// Don't cache the callbacks.length since it may grow
for (var i=0; i<callbacks.length; i++) {
callbackTuple = callbacks[i];
callback = callbackTuple[0];
binding = callbackTuple[1];
if (typeof options !== 'object') {
options = { detail: options };
}
event = new Event(eventName, options);
callback.call(binding, event);
}
}
}
};
__exports__.EventTarget = EventTarget;
});
define("rsvp/hash",
["rsvp/defer","exports"],
function(__dependency1__, __exports__) {
"use strict";
var defer = __dependency1__.defer;
function size(object) {
var s = 0;
for (var prop in object) {
s++;
}
return s;
}
function hash(promises) {
var results = {}, deferred = defer(), remaining = size(promises);
if (remaining === 0) {
deferred.resolve({});
}
var resolver = function(prop) {
return function(value) {
resolveAll(prop, value);
};
};
var resolveAll = function(prop, value) {
results[prop] = value;
if (--remaining === 0) {
deferred.resolve(results);
}
};
var rejectAll = function(error) {
deferred.reject(error);
};
for (var prop in promises) {
if (promises[prop] && typeof promises[prop].then === 'function') {
promises[prop].then(resolver(prop), rejectAll);
} else {
resolveAll(prop, promises[prop]);
}
}
return deferred.promise;
}
__exports__.hash = hash;
});
define("rsvp/node",
["rsvp/promise","rsvp/all","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var all = __dependency2__.all;
function makeNodeCallbackFor(resolve, reject) {
return function (error, value) {
if (error) {
reject(error);
} else if (arguments.length > 2) {
resolve(Array.prototype.slice.call(arguments, 1));
} else {
resolve(value);
}
};
}
function denodeify(nodeFunc) {
return function() {
var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;
var thisArg = this;
var promise = new Promise(function(nodeResolve, nodeReject) {
resolve = nodeResolve;
reject = nodeReject;
});
all(nodeArgs).then(function(nodeArgs) {
nodeArgs.push(makeNodeCallbackFor(resolve, reject));
try {
nodeFunc.apply(thisArg, nodeArgs);
} catch(e) {
reject(e);
}
});
return promise;
};
}
__exports__.denodeify = denodeify;
});
define("rsvp/promise",
["rsvp/config","rsvp/events","rsvp/cancellation_error","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict";
var config = __dependency1__.config;
var EventTarget = __dependency2__.EventTarget;
var CancellationError = __dependency3__.CancellationError;
function objectOrFunction(x) {
return isFunction(x) || (typeof x === "object" && x !== null);
}
function isFunction(x){
return typeof x === "function";
}
var Promise = function(resolver, canceller) {
var promise = this,
resolved = false;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if ((canceller !== undefined) && (typeof canceller !== 'function')) {
throw new TypeError('You can only pass a canceller function' +
' as the second argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver, canceller);
}
var resolvePromise = function(value) {
if (resolved) { return; }
resolved = true;
resolve(promise, value);
};
var rejectPromise = function(value) {
if (resolved) { return; }
resolved = true;
reject(promise, value);
};
var notifyPromise = function(value) {
if (resolved) { return; }
notify(promise, value);
};
this.on('promise:failed', function(event) {
this.trigger('error', { detail: event.detail });
}, this);
this.on('error', onerror);
this.cancel = function () {
// For now, simply reject the promise and does not propagate the cancel
// to parent or children
if (resolved) { return; }
if (canceller !== undefined) {
try {
canceller();
} catch (e) {
rejectPromise(e);
return;
}
}
// Trigger cancel?
rejectPromise(new CancellationError());
};
try {
resolver(resolvePromise, rejectPromise, notifyPromise);
} catch(e) {
rejectPromise(e);
}
};
function onerror(event) {
if (config.onerror) {
config.onerror(event.detail);
}
}
var invokeCallback = function(type, promise, callback, event) {
var hasCallback = isFunction(callback),
value, error, succeeded, failed;
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
if (hasCallback) {
try {
value = callback(event.detail);
succeeded = true;
} catch(e) {
failed = true;
error = e;
}
} else {
value = event.detail;
succeeded = true;
}
if (handleThenable(promise, value)) {
return;
} else if (hasCallback && succeeded) {
resolve(promise, value);
} else if (failed) {
reject(promise, error);
} else if (type === 'resolve') {
resolve(promise, value);
} else if (type === 'reject') {
reject(promise, value);
}
};
var invokeNotifyCallback = function(promise, callback, event) {
var value;
if (typeof callback === 'function') {
try {
value = callback(event.detail);
} catch (e) {
// stop propagating
return;
}
notify(promise, value);
} else {
notify(promise, event.detail);
}
};
Promise.prototype = {
constructor: Promise,
isRejected: undefined,
isFulfilled: undefined,
rejectedReason: undefined,
fulfillmentValue: undefined,
then: function(done, fail, progress) {
this.off('error', onerror);
var thenPromise = new this.constructor(function() {},
function () {
thenPromise.trigger('promise:cancelled', {});
});
if (this.isFulfilled) {
config.async(function(promise) {
invokeCallback('resolve', thenPromise, done, { detail: promise.fulfillmentValue });
}, this);
}
if (this.isRejected) {
config.async(function(promise) {
invokeCallback('reject', thenPromise, fail, { detail: promise.rejectedReason });
}, this);
}
this.on('promise:resolved', function(event) {
invokeCallback('resolve', thenPromise, done, event);
});
this.on('promise:failed', function(event) {
invokeCallback('reject', thenPromise, fail, event);
});
this.on('promise:notified', function (event) {
invokeNotifyCallback(thenPromise, progress, event);
});
return thenPromise;
},
fail: function(fail) {
return this.then(null, fail);
},
always: function(fail) {
return this.then(fail, fail);
}
};
EventTarget.mixin(Promise.prototype);
function resolve(promise, value) {
if (promise === value) {
fulfill(promise, value);
} else if (!handleThenable(promise, value)) {
fulfill(promise, value);
}
}
function handleThenable(promise, value) {
var then = null,
resolved;
try {
if (promise === value) {
throw new TypeError("A promises callback cannot return that same promise.");
}
if (objectOrFunction(value)) {
then = value.then;
if (isFunction(then)) {
if (isFunction(value.on)) {
value.on('promise:notified', function (event) {
notify(promise, event.detail);
});
}
promise.on('promise:cancelled', function(event) {
if (isFunction(value.cancel)) {
value.cancel();
}
});
then.call(value, function(val) {
if (resolved) { return true; }
resolved = true;
if (value !== val) {
resolve(promise, val);
} else {
fulfill(promise, val);
}
}, function(val) {
if (resolved) { return true; }
resolved = true;
reject(promise, val);
});
return true;
}
}
} catch (error) {
reject(promise, error);
return true;
}
return false;
}
function fulfill(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true;
promise.fulfillmentValue = value;
});
}
function reject(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:failed', { detail: value });
promise.isRejected = true;
promise.rejectedReason = value;
});
}
function notify(promise, value) {
config.async(function() {
promise.trigger('promise:notified', { detail: value });
});
}
__exports__.Promise = Promise;
});
define("rsvp/queue",
["rsvp/promise","rsvp/timeout","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var delay = __dependency2__.delay;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function ResolvedQueueError(message) {
this.name = "resolved";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
ResolvedQueueError.prototype = new Error();
ResolvedQueueError.prototype.constructor = ResolvedQueueError;
var Queue = function() {
var queue = this,
promise_list = [],
promise,
fulfill,
reject,
notify,
resolved;
if (!(this instanceof Queue)) {
return new Queue();
}
function canceller() {
for (var i = 0; i < 2; i++) {
promise_list[i].cancel();
}
}
promise = new Promise(function(done, fail, progress) {
fulfill = function (fulfillmentValue) {
if (resolved) {return;}
queue.isFulfilled = true;
queue.fulfillmentValue = fulfillmentValue;
resolved = true;
return done(fulfillmentValue);
};
reject = function (rejectedReason) {
if (resolved) {return;}
queue.isRejected = true;
queue.rejectedReason = rejectedReason ;
resolved = true;
return fail(rejectedReason);
};
notify = progress;
}, canceller);
promise_list.push(delay());
promise_list.push(promise_list[0].then(function () {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill();
}
}));
queue.cancel = function () {
if (resolved) {return;}
resolved = true;
promise.cancel();
promise.fail(function (rejectedReason) {
queue.isRejected = true;
queue.rejectedReason = rejectedReason;
});
};
queue.then = function () {
return promise.then.apply(promise, arguments);
};
queue.push = function(done, fail, progress) {
var last_promise = promise_list[promise_list.length - 1],
next_promise;
if (resolved) {
throw new ResolvedQueueError();
}
next_promise = last_promise.then(done, fail, progress);
promise_list.push(next_promise);
// Handle pop
last_promise = next_promise.then(function (fulfillmentValue) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill(fulfillmentValue);
} else {
return fulfillmentValue;
}
}, function (rejectedReason) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
reject(rejectedReason);
} else {
throw rejectedReason;
}
}, function (notificationValue) {
if (promise_list[promise_list.length - 1] === last_promise) {
notify(notificationValue);
}
return notificationValue;
});
promise_list.push(last_promise);
return this;
};
};
Queue.prototype = Object.create(Promise.prototype);
Queue.prototype.constructor = Queue;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
});
define("rsvp/reject",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function reject(reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
}
__exports__.reject = reject;
});
define("rsvp/resolve",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function resolve(thenable) {
return new Promise(function(resolve, reject) {
if (typeof thenable === "object" && thenable !== null) {
var then = thenable.then;
if ((then !== undefined) && (typeof then === "function")) {
return then.apply(thenable, [resolve, reject]);
}
}
return resolve(thenable);
}, function () {
if ((thenable !== undefined) && (thenable.cancel !== undefined)) {
thenable.cancel();
}
});
}
__exports__.resolve = resolve;
});
define("rsvp/rethrow",
["exports"],
function(__exports__) {
"use strict";
var local = (typeof global === "undefined") ? this : global;
function rethrow(reason) {
local.setTimeout(function() {
throw reason;
});
throw reason;
}
__exports__.rethrow = rethrow;
});
define("rsvp/timeout",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function promiseSetTimeout(millisecond, should_reject, message) {
var timeout_id;
function resolver(resolve, reject) {
timeout_id = setTimeout(function () {
if (should_reject) {
reject(message);
} else {
resolve(message);
}
}, millisecond);
}
function canceller() {
clearTimeout(timeout_id);
}
return new Promise(resolver, canceller);
}
function delay(millisecond, message) {
return promiseSetTimeout(millisecond, false, message);
}
function timeout(millisecond) {
return promiseSetTimeout(millisecond, true,
"Timed out after " + millisecond + " ms");
}
Promise.prototype.delay = function(millisecond) {
return this.then(function (fulfillmentValue) {
return delay(millisecond, fulfillmentValue);
});
};
__exports__.delay = delay;
__exports__.timeout = timeout;
});
define("rsvp",
["rsvp/events","rsvp/cancellation_error","rsvp/promise","rsvp/node","rsvp/all","rsvp/queue","rsvp/timeout","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
"use strict";
var EventTarget = __dependency1__.EventTarget;
var CancellationError = __dependency2__.CancellationError;
var Promise = __dependency3__.Promise;
var denodeify = __dependency4__.denodeify;
var all = __dependency5__.all;
var any = __dependency5__.any;
var Queue = __dependency6__.Queue;
var ResolvedQueueError = __dependency6__.ResolvedQueueError;
var delay = __dependency7__.delay;
var timeout = __dependency7__.timeout;
var hash = __dependency8__.hash;
var rethrow = __dependency9__.rethrow;
var defer = __dependency10__.defer;
var config = __dependency11__.config;
var resolve = __dependency12__.resolve;
var reject = __dependency13__.reject;
function configure(name, value) {
config[name] = value;
}
__exports__.CancellationError = CancellationError;
__exports__.Promise = Promise;
__exports__.EventTarget = EventTarget;
__exports__.all = all;
__exports__.any = any;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
__exports__.delay = delay;
__exports__.timeout = timeout;
__exports__.hash = hash;
__exports__.rethrow = rethrow;
__exports__.defer = defer;
__exports__.denodeify = denodeify;
__exports__.configure = configure;
__exports__.resolve = resolve;
__exports__.reject = reject;
});
\ No newline at end of file
(function(globals) {
var define, requireModule;
(function() {
var registry = {}, seen = {};
define = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
requireModule = function(name) {
if (seen[name]) { return seen[name]; }
seen[name] = {};
var mod = registry[name];
if (!mod) {
throw new Error("Module '" + name + "' not found.");
}
var deps = mod.deps,
callback = mod.callback,
reified = [],
exports;
for (var i=0, l=deps.length; i<l; i++) {
if (deps[i] === 'exports') {
reified.push(exports = {});
} else {
reified.push(requireModule(deps[i]));
}
}
var value = callback.apply(this, reified);
return seen[name] = exports || value;
};
})();
define("rsvp/all",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
/* global toString */
function promiseAtLeast(expected_count, promises) {
if (Object.prototype.toString.call(promises) !== "[object Array]") {
throw new TypeError('You must pass an array to all.');
}
function canceller() {
var promise;
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function' &&
typeof promise.cancel === 'function') {
promise.cancel();
}
}
}
return new Promise(function(resolve, reject, notify) {
var results = [], remaining = promises.length,
promise, remaining_count = promises.length - expected_count;
if (remaining === 0) {
if (expected_count === 1) {
resolve();
} else {
resolve([]);
}
}
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function resolveAll(index, value) {
results[index] = value;
if (--remaining === remaining_count) {
if (remaining_count === 0) {
resolve(results);
} else {
resolve(value);
canceller();
}
}
}
function notifier(index) {
return function(value) {
notify({"index": index, "value": value});
};
}
function cancelAll(rejectionValue) {
reject(rejectionValue);
canceller();
}
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function') {
promise.then(resolver(i), cancelAll, notifier(i));
} else {
resolveAll(i, promise);
}
}
}, canceller
);
}
function all(promises) {
return promiseAtLeast(promises.length, promises);
}
function any(promises) {
return promiseAtLeast(1, promises);
}
__exports__.all = all;
__exports__.any = any;
});
define("rsvp/async",
["exports"],
function(__exports__) {
"use strict";
var browserGlobal = (typeof window !== 'undefined') ? window : {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var async;
var local = (typeof global !== 'undefined') ? global : this;
// old node
function useNextTick() {
return function(callback, arg) {
process.nextTick(function() {
callback(arg);
});
};
}
// node >= 0.10.x
function useSetImmediate() {
return function(callback, arg) {
/* global setImmediate */
setImmediate(function(){
callback(arg);
});
};
}
function useMutationObserver() {
var queue = [];
var observer = new BrowserMutationObserver(function() {
var toProcess = queue.slice();
queue = [];
toProcess.forEach(function(tuple) {
var callback = tuple[0], arg= tuple[1];
callback(arg);
});
});
var element = document.createElement('div');
observer.observe(element, { attributes: true });
// Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
window.addEventListener('unload', function(){
observer.disconnect();
observer = null;
}, false);
return function(callback, arg) {
queue.push([callback, arg]);
element.setAttribute('drainQueue', 'drainQueue');
};
}
function useSetTimeout() {
return function(callback, arg) {
local.setTimeout(function() {
callback(arg);
}, 1);
};
}
if (typeof setImmediate === 'function') {
async = useSetImmediate();
} else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
async = useNextTick();
} else if (BrowserMutationObserver) {
async = useMutationObserver();
} else {
async = useSetTimeout();
}
__exports__.async = async;
});
define("rsvp/cancellation_error",
["exports"],
function(__exports__) {
"use strict";
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function CancellationError(message) {
this.name = "cancel";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
CancellationError.prototype = new Error();
CancellationError.prototype.constructor = CancellationError;
__exports__.CancellationError = CancellationError;
});
define("rsvp/config",
["rsvp/async","exports"],
function(__dependency1__, __exports__) {
"use strict";
var async = __dependency1__.async;
var config = {};
config.async = async;
__exports__.config = config;
});
define("rsvp/defer",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function defer() {
var deferred = {
// pre-allocate shape
resolve: undefined,
reject: undefined,
promise: undefined
};
deferred.promise = new Promise(function(resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
__exports__.defer = defer;
});
define("rsvp/events",
["exports"],
function(__exports__) {
"use strict";
var Event = function(type, options) {
this.type = type;
for (var option in options) {
if (!options.hasOwnProperty(option)) { continue; }
this[option] = options[option];
}
};
var indexOf = function(callbacks, callback) {
for (var i=0, l=callbacks.length; i<l; i++) {
if (callbacks[i][0] === callback) { return i; }
}
return -1;
};
var callbacksFor = function(object) {
var callbacks = object._promiseCallbacks;
if (!callbacks) {
callbacks = object._promiseCallbacks = {};
}
return callbacks;
};
var EventTarget = {
mixin: function(object) {
object.on = this.on;
object.off = this.off;
object.trigger = this.trigger;
return object;
},
on: function(eventNames, callback, binding) {
var allCallbacks = callbacksFor(this), callbacks, eventName;
eventNames = eventNames.split(/\s+/);
binding = binding || this;
while (eventName = eventNames.shift()) {
callbacks = allCallbacks[eventName];
if (!callbacks) {
callbacks = allCallbacks[eventName] = [];
}
if (indexOf(callbacks, callback) === -1) {
callbacks.push([callback, binding]);
}
}
},
off: function(eventNames, callback) {
var allCallbacks = callbacksFor(this), callbacks, eventName, index;
eventNames = eventNames.split(/\s+/);
while (eventName = eventNames.shift()) {
if (!callback) {
allCallbacks[eventName] = [];
continue;
}
callbacks = allCallbacks[eventName];
index = indexOf(callbacks, callback);
if (index !== -1) { callbacks.splice(index, 1); }
}
},
trigger: function(eventName, options) {
var allCallbacks = callbacksFor(this),
callbacks, callbackTuple, callback, binding, event;
if (callbacks = allCallbacks[eventName]) {
// Don't cache the callbacks.length since it may grow
for (var i=0; i<callbacks.length; i++) {
callbackTuple = callbacks[i];
callback = callbackTuple[0];
binding = callbackTuple[1];
if (typeof options !== 'object') {
options = { detail: options };
}
event = new Event(eventName, options);
callback.call(binding, event);
}
}
}
};
__exports__.EventTarget = EventTarget;
});
define("rsvp/hash",
["rsvp/defer","exports"],
function(__dependency1__, __exports__) {
"use strict";
var defer = __dependency1__.defer;
function size(object) {
var s = 0;
for (var prop in object) {
s++;
}
return s;
}
function hash(promises) {
var results = {}, deferred = defer(), remaining = size(promises);
if (remaining === 0) {
deferred.resolve({});
}
var resolver = function(prop) {
return function(value) {
resolveAll(prop, value);
};
};
var resolveAll = function(prop, value) {
results[prop] = value;
if (--remaining === 0) {
deferred.resolve(results);
}
};
var rejectAll = function(error) {
deferred.reject(error);
};
for (var prop in promises) {
if (promises[prop] && typeof promises[prop].then === 'function') {
promises[prop].then(resolver(prop), rejectAll);
} else {
resolveAll(prop, promises[prop]);
}
}
return deferred.promise;
}
__exports__.hash = hash;
});
define("rsvp/node",
["rsvp/promise","rsvp/all","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var all = __dependency2__.all;
function makeNodeCallbackFor(resolve, reject) {
return function (error, value) {
if (error) {
reject(error);
} else if (arguments.length > 2) {
resolve(Array.prototype.slice.call(arguments, 1));
} else {
resolve(value);
}
};
}
function denodeify(nodeFunc) {
return function() {
var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;
var thisArg = this;
var promise = new Promise(function(nodeResolve, nodeReject) {
resolve = nodeResolve;
reject = nodeReject;
});
all(nodeArgs).then(function(nodeArgs) {
nodeArgs.push(makeNodeCallbackFor(resolve, reject));
try {
nodeFunc.apply(thisArg, nodeArgs);
} catch(e) {
reject(e);
}
});
return promise;
};
}
__exports__.denodeify = denodeify;
});
define("rsvp/promise",
["rsvp/config","rsvp/events","rsvp/cancellation_error","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict";
var config = __dependency1__.config;
var EventTarget = __dependency2__.EventTarget;
var CancellationError = __dependency3__.CancellationError;
function objectOrFunction(x) {
return isFunction(x) || (typeof x === "object" && x !== null);
}
function isFunction(x){
return typeof x === "function";
}
var Promise = function(resolver, canceller) {
var promise = this,
resolved = false;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if ((canceller !== undefined) && (typeof canceller !== 'function')) {
throw new TypeError('You can only pass a canceller function' +
' as the second argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver, canceller);
}
var resolvePromise = function(value) {
if (resolved) { return; }
resolved = true;
resolve(promise, value);
};
var rejectPromise = function(value) {
if (resolved) { return; }
resolved = true;
reject(promise, value);
};
var notifyPromise = function(value) {
if (resolved) { return; }
notify(promise, value);
};
this.on('promise:failed', function(event) {
this.trigger('error', { detail: event.detail });
}, this);
this.on('error', onerror);
this.cancel = function () {
// For now, simply reject the promise and does not propagate the cancel
// to parent or children
if (resolved) { return; }
if (canceller !== undefined) {
try {
canceller();
} catch (e) {
rejectPromise(e);
return;
}
}
// Trigger cancel?
rejectPromise(new CancellationError());
};
try {
resolver(resolvePromise, rejectPromise, notifyPromise);
} catch(e) {
rejectPromise(e);
}
};
function onerror(event) {
if (config.onerror) {
config.onerror(event.detail);
}
}
var invokeCallback = function(type, promise, callback, event) {
var hasCallback = isFunction(callback),
value, error, succeeded, failed;
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
if (hasCallback) {
try {
value = callback(event.detail);
succeeded = true;
} catch(e) {
failed = true;
error = e;
}
} else {
value = event.detail;
succeeded = true;
}
if (handleThenable(promise, value)) {
return;
} else if (hasCallback && succeeded) {
resolve(promise, value);
} else if (failed) {
reject(promise, error);
} else if (type === 'resolve') {
resolve(promise, value);
} else if (type === 'reject') {
reject(promise, value);
}
};
var invokeNotifyCallback = function(promise, callback, event) {
var value;
if (typeof callback === 'function') {
try {
value = callback(event.detail);
} catch (e) {
// stop propagating
return;
}
notify(promise, value);
} else {
notify(promise, event.detail);
}
};
Promise.prototype = {
constructor: Promise,
isRejected: undefined,
isFulfilled: undefined,
rejectedReason: undefined,
fulfillmentValue: undefined,
then: function(done, fail, progress) {
this.off('error', onerror);
var thenPromise = new this.constructor(function() {},
function () {
thenPromise.trigger('promise:cancelled', {});
});
if (this.isFulfilled) {
config.async(function(promise) {
invokeCallback('resolve', thenPromise, done, { detail: promise.fulfillmentValue });
}, this);
}
if (this.isRejected) {
config.async(function(promise) {
invokeCallback('reject', thenPromise, fail, { detail: promise.rejectedReason });
}, this);
}
this.on('promise:resolved', function(event) {
invokeCallback('resolve', thenPromise, done, event);
});
this.on('promise:failed', function(event) {
invokeCallback('reject', thenPromise, fail, event);
});
this.on('promise:notified', function (event) {
invokeNotifyCallback(thenPromise, progress, event);
});
return thenPromise;
},
fail: function(fail) {
return this.then(null, fail);
},
always: function(fail) {
return this.then(fail, fail);
}
};
EventTarget.mixin(Promise.prototype);
function resolve(promise, value) {
if (promise === value) {
fulfill(promise, value);
} else if (!handleThenable(promise, value)) {
fulfill(promise, value);
}
}
function handleThenable(promise, value) {
var then = null,
resolved;
try {
if (promise === value) {
throw new TypeError("A promises callback cannot return that same promise.");
}
if (objectOrFunction(value)) {
then = value.then;
if (isFunction(then)) {
if (isFunction(value.on)) {
value.on('promise:notified', function (event) {
notify(promise, event.detail);
});
}
promise.on('promise:cancelled', function(event) {
if (isFunction(value.cancel)) {
value.cancel();
}
});
then.call(value, function(val) {
if (resolved) { return true; }
resolved = true;
if (value !== val) {
resolve(promise, val);
} else {
fulfill(promise, val);
}
}, function(val) {
if (resolved) { return true; }
resolved = true;
reject(promise, val);
});
return true;
}
}
} catch (error) {
reject(promise, error);
return true;
}
return false;
}
function fulfill(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true;
promise.fulfillmentValue = value;
});
}
function reject(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:failed', { detail: value });
promise.isRejected = true;
promise.rejectedReason = value;
});
}
function notify(promise, value) {
config.async(function() {
promise.trigger('promise:notified', { detail: value });
});
}
__exports__.Promise = Promise;
});
define("rsvp/queue",
["rsvp/promise","rsvp/timeout","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var delay = __dependency2__.delay;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function ResolvedQueueError(message) {
this.name = "resolved";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
ResolvedQueueError.prototype = new Error();
ResolvedQueueError.prototype.constructor = ResolvedQueueError;
var Queue = function() {
var queue = this,
promise_list = [],
promise,
fulfill,
reject,
notify,
resolved;
if (!(this instanceof Queue)) {
return new Queue();
}
function canceller() {
for (var i = 0; i < 2; i++) {
promise_list[i].cancel();
}
}
promise = new Promise(function(done, fail, progress) {
fulfill = function (fulfillmentValue) {
if (resolved) {return;}
queue.isFulfilled = true;
queue.fulfillmentValue = fulfillmentValue;
resolved = true;
return done(fulfillmentValue);
};
reject = function (rejectedReason) {
if (resolved) {return;}
queue.isRejected = true;
queue.rejectedReason = rejectedReason ;
resolved = true;
return fail(rejectedReason);
};
notify = progress;
}, canceller);
promise_list.push(delay());
promise_list.push(promise_list[0].then(function () {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill();
}
}));
queue.cancel = function () {
if (resolved) {return;}
resolved = true;
promise.cancel();
promise.fail(function (rejectedReason) {
queue.isRejected = true;
queue.rejectedReason = rejectedReason;
});
};
queue.then = function () {
return promise.then.apply(promise, arguments);
};
queue.push = function(done, fail, progress) {
var last_promise = promise_list[promise_list.length - 1],
next_promise;
if (resolved) {
throw new ResolvedQueueError();
}
next_promise = last_promise.then(done, fail, progress);
promise_list.push(next_promise);
// Handle pop
last_promise = next_promise.then(function (fulfillmentValue) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill(fulfillmentValue);
} else {
return fulfillmentValue;
}
}, function (rejectedReason) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
reject(rejectedReason);
} else {
throw rejectedReason;
}
}, function (notificationValue) {
if (promise_list[promise_list.length - 1] === last_promise) {
notify(notificationValue);
}
return notificationValue;
});
promise_list.push(last_promise);
return this;
};
};
Queue.prototype = Object.create(Promise.prototype);
Queue.prototype.constructor = Queue;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
});
define("rsvp/reject",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function reject(reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
}
__exports__.reject = reject;
});
define("rsvp/resolve",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function resolve(thenable) {
return new Promise(function(resolve, reject) {
if (typeof thenable === "object" && thenable !== null) {
var then = thenable.then;
if ((then !== undefined) && (typeof then === "function")) {
return then.apply(thenable, [resolve, reject]);
}
}
return resolve(thenable);
}, function () {
if ((thenable !== undefined) && (thenable.cancel !== undefined)) {
thenable.cancel();
}
});
}
__exports__.resolve = resolve;
});
define("rsvp/rethrow",
["exports"],
function(__exports__) {
"use strict";
var local = (typeof global === "undefined") ? this : global;
function rethrow(reason) {
local.setTimeout(function() {
throw reason;
});
throw reason;
}
__exports__.rethrow = rethrow;
});
define("rsvp/timeout",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function promiseSetTimeout(millisecond, should_reject, message) {
var timeout_id;
function resolver(resolve, reject) {
timeout_id = setTimeout(function () {
if (should_reject) {
reject(message);
} else {
resolve(message);
}
}, millisecond);
}
function canceller() {
clearTimeout(timeout_id);
}
return new Promise(resolver, canceller);
}
function delay(millisecond, message) {
return promiseSetTimeout(millisecond, false, message);
}
function timeout(millisecond) {
return promiseSetTimeout(millisecond, true,
"Timed out after " + millisecond + " ms");
}
Promise.prototype.delay = function(millisecond) {
return this.then(function (fulfillmentValue) {
return delay(millisecond, fulfillmentValue);
});
};
__exports__.delay = delay;
__exports__.timeout = timeout;
});
define("rsvp",
["rsvp/events","rsvp/cancellation_error","rsvp/promise","rsvp/node","rsvp/all","rsvp/queue","rsvp/timeout","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
"use strict";
var EventTarget = __dependency1__.EventTarget;
var CancellationError = __dependency2__.CancellationError;
var Promise = __dependency3__.Promise;
var denodeify = __dependency4__.denodeify;
var all = __dependency5__.all;
var any = __dependency5__.any;
var Queue = __dependency6__.Queue;
var ResolvedQueueError = __dependency6__.ResolvedQueueError;
var delay = __dependency7__.delay;
var timeout = __dependency7__.timeout;
var hash = __dependency8__.hash;
var rethrow = __dependency9__.rethrow;
var defer = __dependency10__.defer;
var config = __dependency11__.config;
var resolve = __dependency12__.resolve;
var reject = __dependency13__.reject;
function configure(name, value) {
config[name] = value;
}
__exports__.CancellationError = CancellationError;
__exports__.Promise = Promise;
__exports__.EventTarget = EventTarget;
__exports__.all = all;
__exports__.any = any;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
__exports__.delay = delay;
__exports__.timeout = timeout;
__exports__.hash = hash;
__exports__.rethrow = rethrow;
__exports__.defer = defer;
__exports__.denodeify = denodeify;
__exports__.configure = configure;
__exports__.resolve = resolve;
__exports__.reject = reject;
});
window.RSVP = requireModule("rsvp");
})(window);
\ No newline at end of file
/**
* sinon-qunit 1.0.0, 2010/12/09
*
* @author Christian Johansen (christian@cjohansen.no)
*
* (The BSD License)
*
* Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Christian Johansen nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*global sinon, QUnit, test*/
sinon.assert.fail = function (msg) {
QUnit.ok(false, msg);
};
sinon.assert.pass = function (assertion) {
QUnit.ok(true, assertion);
};
sinon.config = {
injectIntoThis: true,
injectInto: null,
properties: ["spy", "stub", "mock", "clock", "sandbox"],
useFakeTimers: false,
useFakeServer: false
};
(function (global) {
var qTest = QUnit.test;
QUnit.test = global.test = function (testName, expected, callback, async) {
if (arguments.length === 2) {
callback = expected;
expected = null;
}
return qTest(testName, expected, sinon.test(callback), async);
};
}(this));
This source diff could not be displayed because it is too large. You can view the blob instead.
{ {
"name": "jio", "name": "jio",
"version": "v2.0.0", "version": "v3.0.0",
"license": "LGPLv3", "license": "LGPLv3",
"author": "Nexedi SA", "author": "Nexedi SA",
"contributors": [ "contributors": [
...@@ -14,7 +14,9 @@ ...@@ -14,7 +14,9 @@
"test": "test" "test": "test"
}, },
"scripts": { "scripts": {
"test": "grunt test" "test": "./node_modules/.bin/grunt test",
"lint": "./node_modules/.bin/grunt lint",
"prepublish": "./node_modules/.bin/grunt build"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -26,13 +28,25 @@ ...@@ -26,13 +28,25 @@
"output", "output",
"cloud" "cloud"
], ],
"dependencies": {
"rsvp": "git+http://git.erp5.org/repos/rsvp.js.git",
"moment": "2.8.3"
},
"devDependencies": { "devDependencies": {
"renderjs": "git+http://git.erp5.org/repos/renderjs.git",
"jscc-node": "0.3.x", "jscc-node": "0.3.x",
"grunt": "0.4.x", "grunt": "0.4.x",
"grunt-cli": "~0.1.11",
"grunt-contrib-concat": "0.3.x", "grunt-contrib-concat": "0.3.x",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-uglify": "0.2.x", "grunt-contrib-uglify": "0.2.x",
"grunt-contrib-qunit": "0.2.x", "grunt-contrib-qunit": "~0.3.0",
"grunt-jslint": "1.0.x" "grunt-contrib-watch": "~0.5.3",
"grunt-jslint": "~1.0.0",
"sinon": "~1.7.3",
"connect-livereload": "~0.3.0",
"grunt-open": "~0.2.2",
"grunt-contrib-connect": "~0.5.0"
}, },
"engines": { "engines": {
"npm": ">=1.3" "npm": ">=1.3"
......
/*jslint indent: 2, nomen: true */ /*global window, moment */
/*global module, define, exports, window, moment */ /*jslint nomen: true, maxlen: 200*/
(function (window, moment) {
// 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, moment);
}
window.jiodate = {};
module(window.jiodate, moment);
}(['exports', 'moment'], function (to_export, moment) {
"use strict"; "use strict";
/** // /**
* Add a secured (write permission denied) property to an object. // * Add a secured (write permission denied) property to an object.
* // *
* @param {Object} object The object to fill // * @param {Object} object The object to fill
* @param {String} key The object key where to store the property // * @param {String} key The object key where to store the property
* @param {Any} value The value to store // * @param {Any} value The value to store
*/ // */
function _export(key, value) { // function _export(key, value) {
Object.defineProperty(to_export, key, { // Object.defineProperty(to_export, key, {
"configurable": false, // "configurable": false,
"enumerable": true, // "enumerable": true,
"writable": false, // "writable": false,
"value": value // "value": value
}); // });
} // }
var YEAR = 'year', var YEAR = 'year',
MONTH = 'month', MONTH = 'month',
...@@ -50,7 +38,7 @@ ...@@ -50,7 +38,7 @@
lesserPrecision = function (p1, p2) { lesserPrecision = function (p1, p2) {
return (precision_grade[p1] < precision_grade[p2]) ? p1 : p2; return (precision_grade[p1] < precision_grade[p2]) ? p1 : p2;
}, },
JIODate = null; JIODate;
JIODate = function (str) { JIODate = function (str) {
...@@ -161,17 +149,24 @@ ...@@ -161,17 +149,24 @@
}; };
_export('JIODate', JIODate); // _export('JIODate', JIODate);
//
_export('YEAR', YEAR); // _export('YEAR', YEAR);
_export('MONTH', MONTH); // _export('MONTH', MONTH);
_export('DAY', DAY); // _export('DAY', DAY);
_export('HOUR', HOUR); // _export('HOUR', HOUR);
_export('MIN', MIN); // _export('MIN', MIN);
_export('SEC', SEC); // _export('SEC', SEC);
_export('MSEC', MSEC); // _export('MSEC', MSEC);
return to_export; window.jiodate = {
JIODate: JIODate,
})); YEAR: YEAR,
MONTH: MONTH,
DAY: DAY,
HOUR: HOUR,
MIN: MIN,
SEC: SEC,
MSEC: MSEC
};
}(window, moment));
/*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query */
/*jslint maxlen: 200*/
(function (window, RSVP, Blob, QueryFactory, Query) {
"use strict";
var util = {},
jIO;
function jIOError(message, status_code) {
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
this.status_code = status_code || 500;
}
jIOError.prototype = new Error();
jIOError.prototype.constructor = jIOError;
util.jIOError = jIOError;
/**
* Send request with XHR and return a promise. xhr.onload: The promise is
* resolved 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 the 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.xhrFields === 'object' && param.xhrFields !== null) {
for (k in param.xhrFields) {
if (param.xhrFields.hasOwnProperty(k)) {
xhr[k] = param.xhrFields[k];
}
}
}
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
}
xhr.send(param.data);
}, function () {
xhr.abort();
});
}
util.ajax = ajax;
/**
* 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 returned 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 an invalid date. In phantomjs, it
// returns `"Invalid Date"` but in browsers it returns `null`. In
// browsers, giving `null` as parameter to `new Date()` doesn't return an
// invalid date.
// Cloning a date with `return new Date(object)` has 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;
}
util.deepClone = deepClone;
/**
* 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();
}
util.generateUuid = generateUuid;
//
// // // XXX What is "jio"?
// // var rest_method_names = [
// // "remove",
// // "allDocs",
// // "removeAttachment",
// // "check",
// // "repair"
// // ],
// // i,
// // len = rest_method_names.length;
// //
// // for (i = 0; i < len; i += 1) {
// // declareMethod(rest_method_names[i]);
// // }
// // ["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;
// // }
// // }
// // });
// // });
// tools
function checkId(param) {
if (typeof param._id !== 'string' || param._id === '') {
throw new jIO.util.jIOError("Document id must be a non empty string.",
400);
}
}
function checkAttachmentId(param) {
if (typeof param._attachment !== 'string' || param._attachment === '') {
throw new jIO.util.jIOError(
"Attachment id must be a non empty string.",
400
);
}
}
function declareMethod(klass, name, precondition_function) {
klass.prototype[name] = function () {
var argument_list = arguments,
context = this;
return new RSVP.Queue()
.push(function () {
if (precondition_function !== undefined) {
return precondition_function.apply(
context.__storage,
argument_list
);
}
})
.push(function () {
var storage_method = context.__storage[name];
if (storage_method === undefined) {
throw new jIO.util.jIOError(
"Capacity '" + name + "' is not implemented",
500
);
}
return storage_method.apply(
context.__storage,
argument_list
);
});
};
// Allow chain
return this;
}
/////////////////////////////////////////////////////////////////
// jIO Storage Proxy
/////////////////////////////////////////////////////////////////
function JioProxyStorage(storage) {
if (!(this instanceof JioProxyStorage)) {
return new JioProxyStorage();
}
this.__storage = storage;
}
declareMethod(JioProxyStorage, "put", checkId);
declareMethod(JioProxyStorage, "get", checkId);
declareMethod(JioProxyStorage, "remove", checkId);
// listeners
declareMethod(JioProxyStorage, "post", function (param) {
if (param._id !== undefined) {
return checkId(param);
}
});
declareMethod(JioProxyStorage, 'putAttachment', function (param) {
checkId(param);
checkAttachmentId(param);
if (!(param._blob instanceof Blob) &&
typeof param._data === 'string') {
param._blob = new Blob([param._data], {
"type": param._content_type || param._mimetype || ""
});
delete param._data;
delete param._mimetype;
delete param._content_type;
} else if (param._blob instanceof Blob) {
delete param._data;
delete param._mimetype;
delete param._content_type;
} else if (param._data instanceof Blob) {
param._blob = param._data;
delete param._data;
delete param._mimetype;
delete param._content_type;
} else {
throw new jIO.util.jIOError(
'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}',
400
);
}
});
declareMethod(JioProxyStorage, 'getAttachment', function (param) {
// if (param.storage_spec.type !== "indexeddb" &&
// param.storage_spec.type !== "dav" &&
// (param.kwargs._start !== undefined
// || param.kwargs._end !== undefined)) {
// restCommandRejecter(param, [
// 'bad_request',
// 'unsupport',
// '_start, _end not support'
// ]);
// return false;
// }
checkId(param);
checkAttachmentId(param);
});
declareMethod(JioProxyStorage, "allDocs");
/////////////////////////////////////////////////////////////////
// Storage builder
/////////////////////////////////////////////////////////////////
function JioBuilder() {
if (!(this instanceof JioBuilder)) {
return new JioBuilder();
}
this.__storage_types = {};
}
JioBuilder.prototype.createJIO = function (storage_spec, util) {
if (typeof storage_spec.type !== 'string') {
throw new TypeError("Invalid storage description");
}
if (!this.__storage_types[storage_spec.type]) {
throw new TypeError("Unknown storage '" + storage_spec.type + "'");
}
return new JioProxyStorage(
new this.__storage_types[storage_spec.type](storage_spec, util)
);
};
JioBuilder.prototype.addStorage = function (type, Constructor) {
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 (this.__storage_types[type] !== undefined) {
throw new TypeError("jIO.addStorage(): Storage type already exists");
}
this.__storage_types[type] = Constructor;
};
JioBuilder.prototype.util = util;
JioBuilder.prototype.QueryFactory = QueryFactory;
JioBuilder.prototype.Query = Query;
/////////////////////////////////////////////////////////////////
// global
/////////////////////////////////////////////////////////////////
jIO = new JioBuilder();
window.jIO = jIO;
}(window, RSVP, Blob, QueryFactory, Query));
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO: true, sjcl: true, $: true, setTimeout: true */ /*global jIO: true, sjcl: true, $: true, setTimeout: true */
jIO.addStorageType('crypt', function (spec, my) { jIO.addStorage('crypt', function (spec, my) {
/*jslint todo: true*/ /*jslint todo: true*/
spec = spec || {}; spec = spec || {};
var that = my.basicStorage(spec, my), var that = my.basicStorage(spec, my),
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
* http://www.gnu.org/licenses/lgpl.html * http://www.gnu.org/licenses/lgpl.html
*/ */
/*jslint indent: 2, maxlen: 80, nomen: true, regexp: true, unparam: true */ /*jslint indent: 2, maxlen: 200, nomen: true, regexp: true, unparam: true */
/*global define, window, jIO, RSVP, btoa, DOMParser, Blob */ /*global define, window, jIO, RSVP, btoa, DOMParser, Blob, console */
// JIO Dav Storage Description : // JIO Dav Storage Description :
// { // {
...@@ -172,14 +172,6 @@ ...@@ -172,14 +172,6 @@
return [restoreName(file_name[1].replace(/_\./g, '.'))]; return [restoreName(file_name[1].replace(/_\./g, '.'))];
} }
function promiseSucceed(promise) {
return new RSVP.Promise(function (resolve, reject, notify) {
promise.then(resolve, reject, notify);
}, function () {
promise.cancel();
});
}
/** /**
* An ajax object to do the good request according to the auth type * An ajax object to do the good request according to the auth type
*/ */
...@@ -204,7 +196,7 @@ ...@@ -204,7 +196,7 @@
"headers": headers "headers": headers
}); });
}, },
"basic": function (method, type, url, data, start, end, login) { "basic": function (method, type, url, data, login, start, end) {
var headers = {"Authorization": "Basic " + login}; var headers = {"Authorization": "Basic " + login};
if (start !== undefined) { if (start !== undefined) {
if (end !== undefined) { if (end !== undefined) {
...@@ -250,22 +242,12 @@ ...@@ -250,22 +242,12 @@
} }
} }
DavStorage.prototype._put = function (metadata) {
return ajax[this._auth_type](
"PUT",
"text",
this._url + '/' + idsToFileName(metadata._id) + "?_=" + Date.now(),
JSON.stringify(metadata),
this._login
);
};
DavStorage.prototype._putAttachment = function (param) { DavStorage.prototype._putAttachment = function (param) {
return ajax[this._auth_type]( return ajax[this._auth_type](
"PUT", "PUT",
null, null,
this._url + '/' + idsToFileName(param._id, param._attachment) + this._url + '/' + idsToFileName(param._id, param._attachment),
"?_=" + Date.now(),
param._blob, param._blob,
undefined, undefined,
undefined, undefined,
...@@ -273,29 +255,36 @@ ...@@ -273,29 +255,36 @@
); );
}; };
DavStorage.prototype._get = function (param) { /**
return ajax[this._auth_type]( * Retrieve metadata
"GET", *
"text", * @method get
this._url + '/' + idsToFileName(param._id), * @param {Object} param The command parameters
null, * @param {Object} options The command options
undefined, */
undefined, DavStorage.prototype.get = function (param) {
this._login var context = this;
).then(function (e) { return new RSVP.Queue()
try { .push(function () {
return {"target": { return ajax[context._auth_type](
"status": e.target.status, "GET",
"statusText": e.target.statusText, "text",
"response": JSON.parse(e.target.responseText) context._url + '/' + idsToFileName(param._id),
}}; null,
} catch (err) { context._login
throw {"target": { );
"status": 0, })
"statusText": "Parse error" .push(undefined, function (error) {
}}; if (error.target !== undefined) {
} if (error.target.status === 404) {
}); throw new jIO.util.jIOError("Cannot find document", 404);
}
}
throw error;
})
.push(function (xhr) {
return JSON.parse(xhr.target.responseText);
});
}; };
DavStorage.prototype._getAttachment = function (param) { DavStorage.prototype._getAttachment = function (param) {
...@@ -314,7 +303,7 @@ ...@@ -314,7 +303,7 @@
return ajax[this._auth_type]( return ajax[this._auth_type](
"DELETE", "DELETE",
null, null,
this._url + '/' + idsToFileName(param._id) + "?_=" + Date.now(), this._url + '/' + idsToFileName(param._id),
null, null,
undefined, undefined,
undefined, undefined,
...@@ -326,8 +315,7 @@ ...@@ -326,8 +315,7 @@
return ajax[this._auth_type]( return ajax[this._auth_type](
"DELETE", "DELETE",
null, null,
this._url + '/' + idsToFileName(param._id, param._attachment) + this._url + '/' + idsToFileName(param._id, param._attachment),
"?_=" + Date.now(),
null, null,
undefined, undefined,
undefined, undefined,
...@@ -411,79 +399,34 @@ ...@@ -411,79 +399,34 @@
// adding custom headers triggers preflight OPTIONS request! // adding custom headers triggers preflight OPTIONS request!
// http://remysharp.com/2011/04/21/getting-cors-working/ // http://remysharp.com/2011/04/21/getting-cors-working/
DavStorage.prototype.postOrPut = function (method, command, metadata) {
metadata._id = metadata._id || jIO.util.generateUuid();
var that = this, o = {
error_message: "DavStorage, unable to get metadata.",
notify_message: "Getting metadata",
percentage: [0, 30],
notifyProgress: function (e) {
command.notify({
"method": method,
"message": o.notify_message,
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) +
o.percentage[0]
});
},
putMetadata: function (e) {
metadata._attachments = e.target.response._attachments;
o.notify_message = "Updating metadata";
o.error_message = "DavStorage, unable to update document.";
o.percentage = [30, 100];
that._put(metadata).then(o.success, o.reject, o.notifyProgress);
},
errorDocumentExists: function (e) {
command.error(
"conflict",
"Document exists",
"DavStorage, cannot overwrite document metadata."
);
},
putMetadataIfPossible: function (e) {
if (e.target.status !== 404) {
return command.reject(
e.target.status,
e.target.statusText,
o.error_message
);
}
o.percentage = [30, 100];
o.notify_message = "Updating metadata";
o.error_message = "DavStorage, unable to create document.";
that._put(metadata).then(o.success, o.reject, o.notifyProgress);
},
success: function (e) {
command.success(e.target.status, {"id": metadata._id});
},
reject: function (e) {
command.reject(
e.target.status,
e.target.statusText,
o.error_message
);
}
};
this._get(metadata).then(
method === 'post' ? o.errorDocumentExists : o.putMetadata,
o.putMetadataIfPossible,
o.notifyProgress
);
};
/** /**
* Creates a new document if not already exists * Creates a new document if not already exists
* *
* @method post * @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to put * @param {Object} metadata The metadata to put
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.post = function (command, metadata) { DavStorage.prototype.post = function (metadata) {
this.postOrPut('post', command, metadata); var doc, doc_id = metadata._id, context = this;
if (doc_id === undefined) {
doc_id = jIO.util.generateUuid();
}
doc = jIO.util.deepClone(metadata);
doc._id = doc_id;
return this.get(doc)
.push(function () {
// the document already exists
throw new jIO.util.jIOError("Cannot create a new document", 409);
}, function (error) {
if (error.status_code === 404) {
// the document does not exist
// XXX
delete doc._attachments;
return context.put(doc);
}
throw error;
});
}; };
...@@ -491,711 +434,682 @@ ...@@ -491,711 +434,682 @@
* Creates or updates a document * Creates or updates a document
* *
* @method put * @method put
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to post * @param {Object} metadata The metadata to post
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.put = function (command, metadata) { DavStorage.prototype.put = function (metadata) {
this.postOrPut('put', command, metadata); var context = this;
return new RSVP.Queue()
.push(function () {
return ajax[context._auth_type](
"PUT",
"text",
context._url + '/' + idsToFileName(metadata._id),
JSON.stringify(metadata),
context._login
);
})
.push(function () {
return metadata._id;
});
}; };
/** /**
* Add an attachment to a document * Add an attachment to a document
* *
* @method putAttachment * @method putAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.putAttachment = function (command, param) {
var that = this, o = {
error_message: "DavStorage unable to put attachment",
percentage: [0, 30],
notify_message: "Getting metadata",
notifyProgress: function (e) {
command.notify({
"method": "putAttachment",
"message": o.notify_message,
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) +
o.percentage[0]
});
},
putAttachmentAndReadBlob: function (e) {
o.percentage = [30, 70];
o.notify_message = "Putting attachment";
o.remote_metadata = e.target.response;
return RSVP.all([
that._putAttachment(param),
jIO.util.readBlobAsBinaryString(param._blob)
]).then(null, null, function (e) {
// propagate only putAttachment progress
if (e.index === 0) {
return e.value;
}
throw null;
});
},
putMetadata: function (answers) {
o.percentage = [70, 100];
o.notify_message = "Updating metadata";
o.remote_metadata._id = param._id;
o.remote_metadata._attachments = o.remote_metadata._attachments || {};
o.remote_metadata._attachments[param._attachment] = {
"length": param._blob.size,
"digest": jIO.util.makeBinaryStringDigest(answers[1].target.result),
"content_type": param._blob.type
};
return that._put(o.remote_metadata);
},
success: function (e) {
command.success(e.target.status, {
"digest": o.remote_metadata._attachments[param._attachment].digest
});
},
reject: function (e) {
command.reject(
e.target.status,
e.target.statusText,
o.error_message
);
}
};
this._get(param).
then(o.putAttachmentAndReadBlob).
then(o.putMetadata).
then(o.success, o.reject, o.notifyProgress);
};
/**
* Retrieve metadata
*
* @method get
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.get = function (command, param) { // DavStorage.prototype.putAttachment = function (param) {
var o = { // var that = this, o = {
notifyGetProgress: function (e) { // error_message: "DavStorage unable to put attachment",
command.notify({ // percentage: [0, 30],
"method": "get", // notify_message: "Getting metadata",
"message": "Getting metadata", // notifyProgress: function (e) {
"loaded": e.loaded, // command.notify({
"total": e.total, // "method": "putAttachment",
"percentage": (e.loaded / e.total) * 100 // 0% to 100% // "message": o.notify_message,
}); // "loaded": e.loaded,
}, // "total": e.total,
success: function (e) { // "percentage": (e.loaded / e.total) *
command.success(e.target.status, {"data": e.target.response}); // (o.percentage[1] - o.percentage[0]) +
}, // o.percentage[0]
reject: function (e) { // });
command.reject( // },
e.target.status, // putAttachmentAndReadBlob: function (e) {
e.target.statusText, // o.percentage = [30, 70];
"DavStorage, unable to get document." // o.notify_message = "Putting attachment";
); // o.remote_metadata = e.target.response;
} // return RSVP.all([
}; // that._putAttachment(param),
// jIO.util.readBlobAsBinaryString(param._blob)
// ]).then(null, null, function (e) {
// // propagate only putAttachment progress
// if (e.index === 0) {
// return e.value;
// }
// throw null;
// });
// },
// putMetadata: function (answers) {
// o.percentage = [70, 100];
// o.notify_message = "Updating metadata";
// o.remote_metadata._id = param._id;
// o.remote_metadata._attachments = o.remote_metadata._attachments || {};
// o.remote_metadata._attachments[param._attachment] = {
// "length": param._blob.size,
// "digest": jIO.util.makeBinaryStringDigest(answers[1].target.result),
// "content_type": param._blob.type
// };
// return that._put(o.remote_metadata);
// },
// success: function (e) {
// command.success(e.target.status, {
// "digest": o.remote_metadata._attachments[param._attachment].digest
// });
// },
// reject: function (e) {
// command.reject(
// e.target.status,
// e.target.statusText,
// o.error_message
// );
// }
// };
//
// this._get(param).
// then(o.putAttachmentAndReadBlob).
// then(o.putMetadata).
// then(o.success, o.reject, o.notifyProgress);
// };
this._get(param).then(o.success, o.reject, o.notifyGetProgress);
};
/** /**
* Retriev a document attachment * Retriev a document attachment
* *
* @method getAttachment * @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.getAttachment = function (command, param) { // DavStorage.prototype.getAttachment = function (param) {
var that = this, o = { // var that = this, o = {
error_message: "DavStorage, unable to get attachment.", // error_message: "DavStorage, unable to get attachment.",
percentage: [0, 30], // percentage: [0, 30],
notify_message: "Getting metedata", // notify_message: "Getting metedata",
"404": "missing document", // Not Found // "404": "missing document", // Not Found
notifyProgress: function (e) { // notifyProgress: function (e) {
command.notify({ // command.notify({
"method": "getAttachment", // "method": "getAttachment",
"message": o.notify_message, // "message": o.notify_message,
"loaded": e.loaded, // "loaded": e.loaded,
"total": e.total, // "total": e.total,
"percentage": (e.loaded / e.total) * // "percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) + // (o.percentage[1] - o.percentage[0]) +
o.percentage[0] // o.percentage[0]
}); // });
}, // },
getAttachment: function (e) { // getAttachment: function (e) {
var attachment = e.target.response._attachments && // var attachment = e.target.response._attachments &&
e.target.response._attachments[param._attachment]; // e.target.response._attachments[param._attachment];
delete o["404"]; // delete o["404"];
if (typeof attachment !== 'object' || attachment === null) { // if (typeof attachment !== 'object' || attachment === null) {
throw {"target": { // throw {"target": {
"status": 404, // "status": 404,
"statusText": "missing attachment" // "statusText": "missing attachment"
}}; // }};
} // }
o.type = attachment.content_type || "application/octet-stream"; // o.type = attachment.content_type || "application/octet-stream";
o.notify_message = "Retrieving attachment"; // o.notify_message = "Retrieving attachment";
o.percentage = [30, 100]; // o.percentage = [30, 100];
o.digest = attachment.digest; // o.digest = attachment.digest;
return that._getAttachment(param); // return that._getAttachment(param);
}, // },
success: function (e) { // success: function (e) {
command.success(e.target.status, { // command.success(e.target.status, {
"data": new Blob([e.target.response], {"type": o.type}), // "data": new Blob([e.target.response], {"type": o.type}),
"digest": o.digest // "digest": o.digest
}); // });
}, // },
reject: function (e) { // reject: function (e) {
command.reject( // command.reject(
e.target.status, // e.target.status,
o[e.target.status] || e.target.statusText, // o[e.target.status] || e.target.statusText,
o.error_message // o.error_message
); // );
} // }
}; // };
if (param._start < 0 || param._end < 0) { // if (param._start < 0 || param._end < 0) {
command.reject(405, // command.reject(405,
"invalide _start,_end", // "invalide _start,_end",
"_start and _end must be positive"); // "_start and _end must be positive");
return; // return;
} // }
if (param._start > param._end) { // if (param._start > param._end) {
command.reject(405, // command.reject(405,
"invalide _start,_end", // "invalide _start,_end",
"start is great then end"); // "start is great then end");
return; // return;
} // }
this._get(param). // this._get(param).
then(o.getAttachment). // then(o.getAttachment).
then(o.success, o.reject, o.notifyProgress); // then(o.success, o.reject, o.notifyProgress);
}; // };
/** /**
* Remove a document * Remove a document
* *
* @method remove * @method remove
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.remove = function (command, param) { // DavStorage.prototype.remove = function (param) {
var that = this, o = { // var that = this, o = {
error_message: "DavStorage, unable to get metadata.", // error_message: "DavStorage, unable to get metadata.",
notify_message: "Getting metadata", // notify_message: "Getting metadata",
percentage: [0, 70], // percentage: [0, 70],
notifyProgress: function (e) { // notifyProgress: function (e) {
if (e === null) { // if (e === null) {
return; // return;
} // }
command.notify({ // command.notify({
"method": "remove", // "method": "remove",
"message": o.notify_message, // "message": o.notify_message,
"loaded": e.loaded, // "loaded": e.loaded,
"total": e.total, // "total": e.total,
"percentage": (e.loaded / e.total) * // "percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) + o.percentage[0] // (o.percentage[1] - o.percentage[0]) + o.percentage[0]
}); // });
}, // },
removeDocument: function (e) { // removeDocument: function (e) {
o.get_result = e; // o.get_result = e;
o.percentage = [70, 80]; // o.percentage = [70, 80];
o.notify_message = "Removing document"; // o.notify_message = "Removing document";
o.error_message = "DavStorage, unable to remove document"; // o.error_message = "DavStorage, unable to remove document";
return that._remove(param); // return that._remove(param);
}, // },
removeAllAttachments: function (e) { // removeAllAttachments: function (e) {
var k, requests = [], attachments; // var k, requests = [], attachments;
attachments = o.get_result.target.response._attachments; // attachments = o.get_result.target.response._attachments;
o.remove_result = e; // o.remove_result = e;
if (typeof attachments === 'object' && attachments !== null) { // if (typeof attachments === 'object' && attachments !== null) {
for (k in attachments) { // for (k in attachments) {
if (attachments.hasOwnProperty(k)) { // if (attachments.hasOwnProperty(k)) {
requests[requests.length] = promiseSucceed( // requests[requests.length] = promiseSucceed(
that._removeAttachment({ // that._removeAttachment({
"_id": param._id, // "_id": param._id,
"_attachment": k // "_attachment": k
}) // })
); // );
} // }
} // }
} // }
if (requests.length === 0) { // if (requests.length === 0) {
return; // return;
} // }
o.count = 0; // o.count = 0;
o.nb_requests = requests.length; // o.nb_requests = requests.length;
return RSVP.all(requests).then(null, null, function (e) { // return RSVP.all(requests).then(null, null, function (e) {
if (e.value.loaded === e.value.total) { // if (e.value.loaded === e.value.total) {
o.count += 1; // o.count += 1;
command.notify({ // command.notify({
"method": "remove", // "method": "remove",
"message": "Removing all associated attachments", // "message": "Removing all associated attachments",
"loaded": o.count, // "loaded": o.count,
"total": o.nb_requests, // "total": o.nb_requests,
"percentage": Math.min( // "percentage": Math.min(
o.count / o.nb_requests * 20 + 80, // o.count / o.nb_requests * 20 + 80,
100 // 100
) // )
}); // });
} // }
return null; // return null;
}); // });
}, // },
success: function () { // success: function () {
command.success(o.remove_result.target.status); // command.success(o.remove_result.target.status);
}, // },
reject: function (e) { // reject: function (e) {
return command.reject( // return command.reject(
e.target.status, // e.target.status,
e.target.statusText, // e.target.statusText,
o.error_message // o.error_message
); // );
} // }
}; // };
//
this._get(param). // this._get(param).
then(o.removeDocument). // then(o.removeDocument).
then(o.removeAllAttachments). // then(o.removeAllAttachments).
then(o.success, o.reject, o.notifyProgress); // then(o.success, o.reject, o.notifyProgress);
}; // };
/** /**
* Remove an attachment * Remove an attachment
* *
* @method removeAttachment * @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.removeAttachment = function (command, param) { // DavStorage.prototype.removeAttachment = function (param) {
var that = this, o = { // var that = this, o = {
error_message: "DavStorage, an error occured while getting metadata.", // error_message: "DavStorage, an error occured while getting metadata.",
percentage: [0, 40], // percentage: [0, 40],
notify_message: "Getting metadata", // notify_message: "Getting metadata",
notifyProgress: function (e) { // notifyProgress: function (e) {
command.notify({ // command.notify({
"method": "remove", // "method": "remove",
"message": o.notify_message, // "message": o.notify_message,
"loaded": e.loaded, // "loaded": e.loaded,
"total": e.total, // "total": e.total,
"percentage": (e.loaded / e.total) * // "percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) + // (o.percentage[1] - o.percentage[0]) +
o.percentage[0] // o.percentage[0]
}); // });
}, // },
updateMetadata: function (e) { // updateMetadata: function (e) {
var k, doc = e.target.response, attachment; // var k, doc = e.target.response, attachment;
attachment = doc._attachments && doc._attachments[param._attachment]; // attachment = doc._attachments && doc._attachments[param._attachment];
o.error_message = "DavStorage, document attachment not found."; // o.error_message = "DavStorage, document attachment not found.";
if (typeof attachment !== 'object' || attachment === null) { // if (typeof attachment !== 'object' || attachment === null) {
throw {"target": { // throw {"target": {
"status": 404, // "status": 404,
"statusText": "missing attachment" // "statusText": "missing attachment"
}}; // }};
} // }
delete doc._attachments[param._attachment]; // delete doc._attachments[param._attachment];
for (k in doc._attachments) { // for (k in doc._attachments) {
if (doc._attachments.hasOwnProperty(k)) { // if (doc._attachments.hasOwnProperty(k)) {
break; // break;
} // }
} // }
if (k === undefined) { // if (k === undefined) {
delete doc._attachments; // delete doc._attachments;
} // }
o.percentage = [40, 80]; // o.percentage = [40, 80];
o.notify_message = "Updating metadata"; // o.notify_message = "Updating metadata";
o.error_message = "DavStorage, an error occured " + // o.error_message = "DavStorage, an error occured " +
"while updating metadata."; // "while updating metadata.";
return that._put(doc); // return that._put(doc);
}, // },
removeAttachment: function () { // removeAttachment: function () {
o.percentage = [80, 100]; // o.percentage = [80, 100];
o.notify_message = "Removing attachment"; // o.notify_message = "Removing attachment";
o.error_message = "DavStorage, an error occured " + // o.error_message = "DavStorage, an error occured " +
"while removing attachment."; // "while removing attachment.";
return that._removeAttachment(param); // return that._removeAttachment(param);
}, // },
success: function (e) { // success: function (e) {
command.success(e.status); // command.success(e.status);
}, // },
reject: function (e) { // reject: function (e) {
return command.reject( // return command.reject(
e.target.status, // e.target.status,
e.target.statusText, // e.target.statusText,
o.error_message // o.error_message
); // );
} // }
}; // };
//
this._get(param). // this._get(param).
then(o.updateMetadata). // then(o.updateMetadata).
then(o.removeAttachment). // then(o.removeAttachment).
then(o.success, o.reject, o.notifyProgress); // then(o.success, o.reject, o.notifyProgress);
}; // };
/** /**
* Retrieve a list of present document * Retrieve a list of present document
* *
* @method allDocs * @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
* @param {Boolean} [options.include_docs=false] * @param {Boolean} [options.include_docs=false]
* Also retrieve the actual document content. * Also retrieve the actual document content.
*/ */
DavStorage.prototype.allDocs = function (command, param, options) { // DavStorage.prototype.allDocs = function (param, options) {
var that = this, o = { // var that = this, o = {
error_message: "DavStorage, an error occured while " + // error_message: "DavStorage, an error occured while " +
"retrieving document list", // "retrieving document list",
max_percentage: options.include_docs === true ? 20 : 100, // max_percentage: options.include_docs === true ? 20 : 100,
notifyAllDocsProgress: function (e) { // notifyAllDocsProgress: function (e) {
command.notify({ // command.notify({
"method": "remove", // "method": "remove",
"message": "Retrieving document list", // "message": "Retrieving document list",
"loaded": e.loaded, // "loaded": e.loaded,
"total": e.total, // "total": e.total,
"percentage": (e.loaded / e.total) * o.max_percentage // "percentage": (e.loaded / e.total) * o.max_percentage
}); // });
}, // },
getAllMetadataIfNecessary: function (e) { // getAllMetadataIfNecessary: function (e) {
var requests = []; // var requests = [];
o.alldocs_result = e; // o.alldocs_result = e;
if (options.include_docs !== true || // if (options.include_docs !== true ||
e.target.response.rows.length === 0) { // e.target.response.rows.length === 0) {
return; // return;
} // }
//
e.target.response.rows.forEach(function (row) { // e.target.response.rows.forEach(function (row) {
if (row.id !== "") { // if (row.id !== "") {
requests[requests.length] = that._get({"_id": row.id}). // requests[requests.length] = that._get({"_id": row.id}).
then(function (e) { // then(function (e) {
row.doc = e.target.response; // row.doc = e.target.response;
}); // });
} // }
}); // });
//
o.count = 0; // o.count = 0;
o.nb_requests = requests.length; // o.nb_requests = requests.length;
o.error_message = "DavStorage, an error occured while " + // o.error_message = "DavStorage, an error occured while " +
"getting document metadata"; // "getting document metadata";
return RSVP.all(requests).then(null, null, function (e) { // return RSVP.all(requests).then(null, null, function (e) {
if (e.value.loaded === e.value.total) { // if (e.value.loaded === e.value.total) {
o.count += 1; // o.count += 1;
command.notify({ // command.notify({
"method": "allDocs", // "method": "allDocs",
"message": "Getting all documents metadata", // "message": "Getting all documents metadata",
"loaded": o.count, // "loaded": o.count,
"total": o.nb_requests, // "total": o.nb_requests,
"percentage": Math.min( // "percentage": Math.min(
o.count / o.nb_requests * 80 + 20, // o.count / o.nb_requests * 80 + 20,
100 // 100
) // )
}); // });
} // }
throw null; // throw null;
}); // });
}, // },
success: function () { // success: function () {
command.success(o.alldocs_result.target.status, { // command.success(o.alldocs_result.target.status, {
"data": o.alldocs_result.target.response // "data": o.alldocs_result.target.response
}); // });
}, // },
reject: function (e) { // reject: function (e) {
return command.reject( // return command.reject(
e.target.status, // e.target.status,
e.target.statusText, // e.target.statusText,
o.error_message // o.error_message
); // );
} // }
}; // };
//
this._allDocs(param, options). // this._allDocs(param, options).
then(o.getAllMetadataIfNecessary). // then(o.getAllMetadataIfNecessary).
then(o.success, o.reject, o.notifyProgress); // then(o.success, o.reject, o.notifyProgress);
}; // };
/** /**
* Check the storage or a specific document * Check the storage or a specific document
* *
* @method check * @method check
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.check = function (command, param) { // DavStorage.prototype.check = function (param) {
this.genericRepair(command, param, false); // this.genericRepair(param, false);
}; // };
/** /**
* Repair the storage or a specific document * Repair the storage or a specific document
* *
* @method repair * @method repair
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
DavStorage.prototype.repair = function (command, param) { // DavStorage.prototype.repair = function (param) {
this.genericRepair(command, param, true); // this.genericRepair(param, true);
}; // };
/** /**
* A generic method that manage check or repair command * A generic method that manage check or repair command
* *
* @method genericRepair * @method genericRepair
* @param {Object} command The JIO command
* @param {Object} param The command parameters * @param {Object} param The command parameters
* @param {Boolean} repair If true then repair else just check * @param {Boolean} repair If true then repair else just check
*/ */
DavStorage.prototype.genericRepair = function (command, param, repair) { // DavStorage.prototype.genericRepair = function (param, repair) {
//
var that = this, repair_promise; // var that = this, repair_promise;
//
// returns a jio object // // returns a jio object
function getAllFile() { // function getAllFile() {
return ajax[that._auth_type]( // return ajax[that._auth_type](
"PROPFIND", // "PROPFIND",
"text", // "text",
that._url + '/', // that._url + '/',
null, // null,
that._login // that._login
).then(function (e) { // on success // ).then(function (e) { // on success
var i, length, rows = new DOMParser().parseFromString( // var i, length, rows = new DOMParser().parseFromString(
e.target.responseText, // e.target.responseText,
"text/xml" // "text/xml"
).querySelectorAll( // ).querySelectorAll(
"D\\:response, response" // "D\\:response, response"
); // );
if (rows.length === 1) { // if (rows.length === 1) {
return {"status": 200, "data": []}; // return {"status": 200, "data": []};
} // }
// exclude parent folder and browse // // exclude parent folder and browse
rows = [].slice.call(rows); // rows = [].slice.call(rows);
rows.shift(); // rows.shift();
length = rows.length; // length = rows.length;
for (i = 0; i < length; i += 1) { // for (i = 0; i < length; i += 1) {
rows[i] = rows[i].querySelector("D\\:href, href"). // rows[i] = rows[i].querySelector("D\\:href, href").
textContent.split('/').slice(-1)[0]; // textContent.split('/').slice(-1)[0];
} // }
return {"data": rows, "status": 200}; // return {"data": rows, "status": 200};
// rows -> [ // // rows -> [
// 'file_path_1', // // 'file_path_1',
// ... // // ...
// ] // // ]
}, function (e) { // on error // }, function (e) { // on error
// convert into jio error object // // convert into jio error object
// then propagate // // then propagate
throw {"status": e.target.status, // throw {"status": e.target.status,
"reason": e.target.statusText}; // "reason": e.target.statusText};
}); // });
} // }
//
// returns jio object // // returns jio object
function repairOne(shared, repair) { // function repairOne(shared, repair) {
var modified = false, document_id = shared._id; // var modified = false, document_id = shared._id;
return that._get({"_id": document_id}).then(function (event) { // return that._get({"_id": document_id}).then(function (event) {
var attachment_id, metadata = event.target.response; // var attachment_id, metadata = event.target.response;
//
// metadata should be an object // // metadata should be an object
if (typeof metadata !== 'object' || metadata === null || // if (typeof metadata !== 'object' || metadata === null ||
Array.isArray(metadata)) { // Array.isArray(metadata)) {
if (!repair) { // if (!repair) {
throw { // throw {
"status": "conflict", // "status": "conflict",
"reason": "corrupted", // "reason": "corrupted",
"message": "Bad metadata found in document \"" + // "message": "Bad metadata found in document \"" +
document_id + "\"" // document_id + "\""
}; // };
} // }
return {}; // return {};
} // }
//
// check metadata content // // check metadata content
if (!repair) { // if (!repair) {
if (!(new jIO.Metadata(metadata).check())) { // if (!(new jIO.Metadata(metadata).check())) {
return { // return {
"status": "conflict", // "status": "conflict",
"reason": "corrupted", // "reason": "corrupted",
"message": "Some metadata might be lost" // "message": "Some metadata might be lost"
}; // };
} // }
} else { // } else {
modified = ( // modified = (
jIO.util.uniqueJSONStringify(metadata) !== // jIO.util.uniqueJSONStringify(metadata) !==
jIO.util.uniqueJSONStringify( // jIO.util.uniqueJSONStringify(
new jIO.Metadata(metadata).format()._dict // new jIO.Metadata(metadata).format()._dict
) // )
); // );
} // }
//
// check metadata id // // check metadata id
if (metadata._id !== document_id) { // if (metadata._id !== document_id) {
// metadata id is different than file // // metadata id is different than file
// this is not a critical thing // // this is not a critical thing
modified = true; // modified = true;
metadata._id = document_id; // metadata._id = document_id;
} // }
//
// check attachment metadata container // // check attachment metadata container
if (metadata._attachments && // if (metadata._attachments &&
(typeof metadata._attachments !== 'object' || // (typeof metadata._attachments !== 'object' ||
Array.isArray(metadata._attachments))) { // Array.isArray(metadata._attachments))) {
// is not undefined nor object // // is not undefined nor object
if (!repair) { // if (!repair) {
throw { // throw {
"status": "conflict", // "status": "conflict",
"reason": "corrupted", // "reason": "corrupted",
"message": "Bad attachment metadata found in document \"" + // "message": "Bad attachment metadata found in document \"" +
document_id + "\"" // document_id + "\""
}; // };
} // }
delete metadata._attachments; // delete metadata._attachments;
modified = true; // modified = true;
} // }
//
// check every attachment metadata // // check every attachment metadata
if (metadata._attachments) { // if (metadata._attachments) {
for (attachment_id in metadata._attachments) { // for (attachment_id in metadata._attachments) {
if (metadata._attachments.hasOwnProperty(attachment_id)) { // if (metadata._attachments.hasOwnProperty(attachment_id)) {
// check attachment metadata type // // check attachment metadata type
if (typeof metadata._attachments[attachment_id] !== 'object' || // if (typeof metadata._attachments[attachment_id] !== 'object' ||
metadata._attachments[attachment_id] === null || // metadata._attachments[attachment_id] === null ||
Array.isArray(metadata._attachments[attachment_id])) { // Array.isArray(metadata._attachments[attachment_id])) {
// is not object // // is not object
if (!repair) { // if (!repair) {
throw { // throw {
"status": "conflict", // "status": "conflict",
"reason": "corrupted", // "reason": "corrupted",
"message": "Bad attachment metadata found in document \"" + // "message": "Bad attachment metadata found in document \"" +
document_id + "\", attachment \"" + // document_id + "\", attachment \"" +
attachment_id + "\"" // attachment_id + "\""
}; // };
} // }
metadata._attachments[attachment_id] = {}; // metadata._attachments[attachment_id] = {};
modified = true; // modified = true;
} // }
// check attachment existency if all attachment are listed // // check attachment existency if all attachment are listed
if (shared.referenced_dict) { // if (shared.referenced_dict) {
if (shared.unreferenced_dict[metadata._id] && // if (shared.unreferenced_dict[metadata._id] &&
shared.unreferenced_dict[metadata._id][attachment_id]) { // shared.unreferenced_dict[metadata._id][attachment_id]) {
// attachment seams to exist but is not referenced // // attachment seams to exist but is not referenced
shared.referenced_dict[metadata._id] = // shared.referenced_dict[metadata._id] =
shared.referenced_dict[metadata._id] || {}; // shared.referenced_dict[metadata._id] || {};
shared.referenced_dict[metadata._id][attachment_id] = true; // shared.referenced_dict[metadata._id][attachment_id] = true;
delete shared.unreferenced_dict[metadata._id][attachment_id]; // delete shared.unreferenced_dict[metadata._id][attachment_id];
} else if ( // } else if (
!(shared.referenced_dict[metadata._id] && // !(shared.referenced_dict[metadata._id] &&
shared.referenced_dict[metadata._id][attachment_id]) // shared.referenced_dict[metadata._id][attachment_id])
) { // ) {
// attachment doesn't exist, remove attachment id // // attachment doesn't exist, remove attachment id
if (!repair) { // if (!repair) {
throw { // throw {
"status": "conflict", // "status": "conflict",
"reason": "attachment missing", // "reason": "attachment missing",
"message": "Attachment \"" + // "message": "Attachment \"" +
attachment_id + "\" from document \"" + // attachment_id + "\" from document \"" +
document_id + "\" is missing" // document_id + "\" is missing"
}; // };
} // }
delete metadata._attachments[attachment_id]; // delete metadata._attachments[attachment_id];
modified = true; // modified = true;
} // }
} // }
} // }
} // }
} // }
return { // return {
"modified": modified, // "modified": modified,
"metadata": metadata // "metadata": metadata
}; // };
}, function (event) { // on error // }, function (event) { // on error
// convert into jio error object // // convert into jio error object
// then propagate // // then propagate
throw {"status": event.target.status, // throw {"status": event.target.status,
"reason": event.target.statustext}; // "reason": event.target.statustext};
}).then(function (dict) { // }).then(function (dict) {
if (dict.modified) { // if (dict.modified) {
return this._put(dict.metadata); // return this._put(dict.metadata);
} // }
return null; // return null;
}).then(function () { // }).then(function () {
return "no_content"; // return "no_content";
}); // });
} // }
//
// returns jio object // // returns jio object
function repairAll(shared, repair) { // function repairAll(shared, repair) {
return getAllFile().then(function (answer) { // return getAllFile().then(function (answer) {
var index, data = answer.data, length = data.length, id_list, // var index, data = answer.data, length = data.length, id_list,
document_list = []; // document_list = [];
for (index = 0; index < length; index += 1) { // for (index = 0; index < length; index += 1) {
// parsing all files // // parsing all files
id_list = fileNameToIds(data[index]); // id_list = fileNameToIds(data[index]);
if (id_list.length === 1) { // if (id_list.length === 1) {
// this is a document // // this is a document
document_list[document_list.length] = id_list[0]; // document_list[document_list.length] = id_list[0];
} else if (id_list.length === 2) { // } else if (id_list.length === 2) {
// this is an attachment // // this is an attachment
// reference it // // reference it
shared.unreferenced_dict[id_list[0]] = // shared.unreferenced_dict[id_list[0]] =
shared.unreferenced_dict[id_list[0]] || {}; // shared.unreferenced_dict[id_list[0]] || {};
shared.unreferenced_dict[id_list[0]][id_list[1]] = true; // shared.unreferenced_dict[id_list[0]][id_list[1]] = true;
} else { // } else {
shared.unknown_file_list.push(data[index]); // shared.unknown_file_list.push(data[index]);
} // }
} // }
length = document_list.length; // length = document_list.length;
for (index = 0; index < length; index += 1) { // for (index = 0; index < length; index += 1) {
shared._id = document_list[index]; // shared._id = document_list[index];
document_list[index] = repairOne(shared, repair); // document_list[index] = repairOne(shared, repair);
} // }
//
function removeFile(name) { // function removeFile(name) {
return ajax[that._auth_type]( // return ajax[that._auth_type](
"DELETE", // "DELETE",
null, // null,
that._url + '/' + name + "?_=" + Date.now(), // that._url + '/' + name,
null, // null,
that._login // that._login
); // );
} // }
//
function errorEventConverter(event) { // function errorEventConverter(event) {
throw {"status": event.target.status, // throw {"status": event.target.status,
"reason": event.target.statusText}; // "reason": event.target.statusText};
} // }
//
length = shared.unknown_file_list.length; // length = shared.unknown_file_list.length;
for (index = 0; index < length; index += 1) { // for (index = 0; index < length; index += 1) {
document_list.push( // document_list.push(
removeFile(shared.unknown_file_list[index]). // removeFile(shared.unknown_file_list[index]).
then(null, errorEventConverter) // then(null, errorEventConverter)
); // );
} // }
//
return RSVP.all(document_list); // return RSVP.all(document_list);
}).then(function () { // }).then(function () {
return "no_content"; // return "no_content";
}); // });
} // }
//
if (typeof param._id === 'string') { // if (typeof param._id === 'string') {
repair_promise = repairOne(param, repair); // repair_promise = repairOne(param, repair);
} else { // } else {
param.referenced_attachment_path_dict = {}; // param.referenced_attachment_path_dict = {};
param.unreferenced_attachment_path_dict = {}; // param.unreferenced_attachment_path_dict = {};
param.unknown_file_list = []; // param.unknown_file_list = [];
repair_promise = repairAll(param, repair); // repair_promise = repairAll(param, repair);
} // }
//
repair_promise.then(command.success, command.error, command.notify); // repair_promise.then(command.success, command.error, command.notify);
//
}; // };
jIO.addStorage('dav', DavStorage); jIO.addStorage('dav', DavStorage);
......
...@@ -28,25 +28,28 @@ ...@@ -28,25 +28,28 @@
* - https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB * - https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB
*/ */
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint indent: 2, maxlen: 120, nomen: true */
/*global define, module, require, indexedDB, jIO, RSVP, Blob, Math*/ /*global define, module, require, indexedDB, jIO, RSVP, Blob, Math, alert*/
(function (dependencies, factory) { // (function (dependencies, factory) {
"use strict"; // "use strict";
if (typeof define === "function" && define.amd) { // if (typeof define === "function" && define.amd) {
return define(dependencies, factory); // return define(dependencies, factory);
} // }
if (typeof module === "object" && module !== null && // if (typeof module === "object" && module !== null &&
typeof module.exports === "object" && module.exports !== null && // typeof module.exports === "object" && module.exports !== null &&
typeof require === "function") { // typeof require === "function") {
module.exports = factory.apply(null, dependencies.map(require)); // module.exports = factory.apply(null, dependencies.map(require));
return; // return;
} // }
factory(jIO, RSVP); // factory(jIO, RSVP);
}(["jio", "rsvp"], function (jIO, RSVP) { // }(["jio", "rsvp"], function (jIO, RSVP) {
// "use strict";
(function (jIO) {
"use strict"; "use strict";
var Promise = RSVP.Promise, generateUuid = jIO.util.generateUuid; var generateUuid = jIO.util.generateUuid;
function metadataObjectToString(value) { function metadataObjectToString(value) {
var i, l; var i, l;
...@@ -319,7 +322,7 @@ ...@@ -319,7 +322,7 @@
*@param {Object} command The JIO command *@param {Object} command The JIO command
*@param {Object} param The command parameters *@param {Object} param The command parameters
*/ */
IndexedDBStorage.prototype.get = function (command, param) { IndexedDBStorage.prototype.get = function (param) {
var jio_storage = this, var jio_storage = this,
transaction, transaction,
global_db, global_db,
...@@ -341,8 +344,7 @@ ...@@ -341,8 +344,7 @@
var store = transaction.objectStore("attachment"); var store = transaction.objectStore("attachment");
return getIndexedDB(store, param._id); return getIndexedDB(store, param._id);
} }
throw ({"status": 404, "reason": "Not Found", throw new jIO.util.jIOError("Cannot find document", 404);
"message": "IndexeddbStorage, unable to get document."});
}) })
.push(function (result) { .push(function (result) {
//get the reste data from attachment //get the reste data from attachment
...@@ -359,8 +361,7 @@ ...@@ -359,8 +361,7 @@
global_db.close(); global_db.close();
} }
throw error; throw error;
}) });
.push(command.success, command.error, command.notify);
}; };
...@@ -505,390 +506,390 @@ ...@@ -505,390 +506,390 @@
/** // /**
* Retrieve a list of present document // * Retrieve a list of present document
* // *
* @method allDocs // * @method allDocs
* @param {Object} command The JIO command // * @param {Object} command The JIO command
* @param {Object} param The command parameters // * @param {Object} param The command parameters
* @param {Object} options The command options // * @param {Object} options The command options
* @param {Boolean} [options.include_docs=false] // * @param {Boolean} [options.include_docs=false]
* Also retrieve the actual document content. // * Also retrieve the actual document content.
*/ // */
IndexedDBStorage.prototype.getListMetadata = function (option) { // IndexedDBStorage.prototype.getListMetadata = function (option) {
var rows = [], onCancel, open_req = indexedDB.open(this._database_name); // var rows = [], onCancel, open_req = indexedDB.open(this._database_name);
return new Promise(function (resolve, reject, notify) { // return new Promise(function (resolve, reject, notify) {
open_req.onerror = function () { // open_req.onerror = function () {
if (open_req.result) { open_req.result.close(); } // if (open_req.result) { open_req.result.close(); }
reject(open_req.error); // reject(open_req.error);
}; // };
open_req.onsuccess = function () { // open_req.onsuccess = function () {
var tx, date, j = 0, index_req, db = open_req.result; // var tx, date, j = 0, index_req, db = open_req.result;
try { // try {
tx = db.transaction(["metadata", "attachment"], "readonly"); // tx = db.transaction(["metadata", "attachment"], "readonly");
onCancel = function () { // onCancel = function () {
tx.abort(); // tx.abort();
db.close(); // db.close();
}; // };
index_req = tx.objectStore("metadata").index("_id").openCursor(); // index_req = tx.objectStore("metadata").index("_id").openCursor();
date = Date.now(); // date = Date.now();
index_req.onsuccess = function (event) { // index_req.onsuccess = function (event) {
var cursor = event.target.result, now, value, i, key; // var cursor = event.target.result, now, value, i, key;
if (cursor) { // if (cursor) {
// Called for each matching record // // Called for each matching record
// notification management // // notification management
now = Date.now(); // now = Date.now();
if (date <= now - 1000) { // if (date <= now - 1000) {
notify({"loaded": rows.length}); // notify({"loaded": rows.length});
date = now; // date = now;
} // }
// option.limit management // // option.limit management
if (Array.isArray(option.limit)) { // if (Array.isArray(option.limit)) {
if (option.limit.length > 1) { // if (option.limit.length > 1) {
if (option.limit[0] > 0) { // if (option.limit[0] > 0) {
option.limit[0] -= 1; // option.limit[0] -= 1;
cursor["continue"](); // cursor["continue"]();
return; // return;
} // }
if (option.limit[1] <= 0) { // if (option.limit[1] <= 0) {
// end // // end
index_req.onsuccess({"target": {}}); // index_req.onsuccess({"target": {}});
return; // return;
} // }
option.limit[1] -= 1; // option.limit[1] -= 1;
} else { // } else {
if (option.limit[0] <= 0) { // if (option.limit[0] <= 0) {
// end // // end
index_req.onsuccess({"target": {}}); // index_req.onsuccess({"target": {}});
return; // return;
} // }
option.limit[0] -= 1; // option.limit[0] -= 1;
} // }
} // }
value = {}; // value = {};
// option.select_list management // // option.select_list management
if (option.select_list) { // if (option.select_list) {
for (i = 0; i < option.select_list.length; i += 1) { // for (i = 0; i < option.select_list.length; i += 1) {
key = option.select_list[i]; // key = option.select_list[i];
value[key] = cursor.value[key]; // value[key] = cursor.value[key];
} // }
} // }
// option.include_docs management // // option.include_docs management
if (option.include_docs) { // if (option.include_docs) {
rows.push({ // rows.push({
"id": cursor.value._id, // "id": cursor.value._id,
"doc": cursor.value, // "doc": cursor.value,
"value": value // "value": value
}); // });
} else { // } else {
rows.push({ // rows.push({
"id": cursor.value._id, // "id": cursor.value._id,
"value": value // "value": value
}); // });
} // }
// continue to next iteration // // continue to next iteration
cursor["continue"](); // cursor["continue"]();
} else { // } else {
index_req = tx.objectStore("attachment"). // index_req = tx.objectStore("attachment").
index("_id").openCursor(); // index("_id").openCursor();
index_req.onsuccess = function (event) { // index_req.onsuccess = function (event) {
//second table // //second table
cursor = event.target.result; // cursor = event.target.result;
if (cursor) { // if (cursor) {
value = {}; // value = {};
if (cursor.value._attachment) { // if (cursor.value._attachment) {
if (option.select_list) { // if (option.select_list) {
for (i = 0; i < option.select_list.length; i += 1) { // for (i = 0; i < option.select_list.length; i += 1) {
key = option.select_list[i]; // key = option.select_list[i];
value[key] = cursor.value._attachment[key]; // value[key] = cursor.value._attachment[key];
} // }
} // }
//add info of attachment into metadata // //add info of attachment into metadata
rows[j].value._attachment = value; // rows[j].value._attachment = value;
if (option.include_docs) { // if (option.include_docs) {
rows[j].doc._attachment = cursor.value._attachment; // rows[j].doc._attachment = cursor.value._attachment;
} // }
} // }
j += 1; // j += 1;
cursor["continue"](); // cursor["continue"]();
} else { // } else {
notify({"loaded": rows.length}); // notify({"loaded": rows.length});
resolve({"data": {"rows": rows, "total_rows": rows.length}}); // resolve({"data": {"rows": rows, "total_rows": rows.length}});
db.close(); // db.close();
} // }
}; // };
} // }
}; // };
} catch (e) { // } catch (e) {
reject(e); // reject(e);
db.close(); // db.close();
} // }
}; // };
}, function () { // }, function () {
if (typeof onCancel === "function") { // if (typeof onCancel === "function") {
onCancel(); // onCancel();
} // }
}); // });
}; // };
/** // /**
* Add an attachment to a document // * Add an attachment to a document
* // *
* @param {Object} command The JIO command // * @param {Object} command The JIO command
* @param {Object} metadata The data // * @param {Object} metadata The data
* // *
*/ // */
IndexedDBStorage.prototype.putAttachment = function (command, metadata) { // IndexedDBStorage.prototype.putAttachment = function (command, metadata) {
var jio_storage = this, // var jio_storage = this,
transaction, // transaction,
global_db, // global_db,
BlobInfo, // BlobInfo,
readResult; // readResult;
function putAllPart(store, metadata, readResult, count, part) { // function putAllPart(store, metadata, readResult, count, part) {
var blob, // var blob,
readPart, // readPart,
end; // end;
if (count >= metadata._blob.size) { // if (count >= metadata._blob.size) {
return; // return;
} // }
end = count + jio_storage._unite; // end = count + jio_storage._unite;
blob = metadata._blob.slice(count, end); // blob = metadata._blob.slice(count, end);
readPart = readResult.slice(count, end); // readPart = readResult.slice(count, end);
return putIndexedDB(store, {"_id": metadata._id, // return putIndexedDB(store, {"_id": metadata._id,
"_attachment" : metadata._attachment, // "_attachment" : metadata._attachment,
"_part" : part, // "_part" : part,
"blob": blob}, readPart) // "blob": blob}, readPart)
.then(function () { // .then(function () {
return putAllPart(store, metadata, readResult, end, part + 1); // return putAllPart(store, metadata, readResult, end, part + 1);
}); // });
} // }
return jIO.util.readBlobAsArrayBuffer(metadata._blob) // return jIO.util.readBlobAsArrayBuffer(metadata._blob)
.then(function (event) { // .then(function (event) {
readResult = event.target.result; // readResult = event.target.result;
BlobInfo = { // BlobInfo = {
"content_type": metadata._blob.type, // "content_type": metadata._blob.type,
"length": metadata._blob.size // "length": metadata._blob.size
}; // };
return new RSVP.Queue() // return new RSVP.Queue()
.push(function () { // .push(function () {
return openIndexedDB(jio_storage._database_name); // return openIndexedDB(jio_storage._database_name);
}) // })
.push(function (db) { // .push(function (db) {
global_db = db; // global_db = db;
transaction = db.transaction(["attachment", // transaction = db.transaction(["attachment",
"blob"], "readwrite"); // "blob"], "readwrite");
return promiseResearch(transaction, // return promiseResearch(transaction,
metadata._id, "attachment", "_id"); // metadata._id, "attachment", "_id");
}) // })
.push(function (researchResult) { // .push(function (researchResult) {
if (researchResult.result === undefined) { // if (researchResult.result === undefined) {
throw ({"status": 404, "reason": "Not Found", // throw ({"status": 404, "reason": "Not Found",
"message": "indexeddbStorage unable to put attachment"}); // "message": "indexeddbStorage unable to put attachment"});
} // }
//update attachment // //update attachment
researchResult.result._attachment = researchResult. // researchResult.result._attachment = researchResult.
result._attachment || {}; // result._attachment || {};
researchResult.result._attachment[metadata._attachment] = // researchResult.result._attachment[metadata._attachment] =
(BlobInfo === undefined) ? "BlobInfo" : BlobInfo; // (BlobInfo === undefined) ? "BlobInfo" : BlobInfo;
return putIndexedDB(researchResult.store, researchResult.result); // return putIndexedDB(researchResult.store, researchResult.result);
}) // })
.push(function () { // .push(function () {
//put in blob // //put in blob
var store = transaction.objectStore("blob"); // var store = transaction.objectStore("blob");
return putAllPart(store, metadata, readResult, 0, 0); // return putAllPart(store, metadata, readResult, 0, 0);
}) // })
.push(function () { // .push(function () {
return transactionEnd(transaction); // return transactionEnd(transaction);
}) // })
.push(function () { // .push(function () {
return {"status": 204}; // return {"status": 204};
}) // })
.push(undefined, function (error) { // .push(undefined, function (error) {
if (global_db !== undefined) { // if (global_db !== undefined) {
global_db.close(); // global_db.close();
} // }
throw error; // throw error;
}) // })
.push(command.success, command.error, command.notify); // .push(command.success, command.error, command.notify);
}); // });
}; // };
/** // /**
* Retriev a document attachment // * Retriev a document attachment
* // *
* @param {Object} command The JIO command // * @param {Object} command The JIO command
* @param {Object} param The command parameter // * @param {Object} param The command parameter
*/ // */
IndexedDBStorage.prototype.getAttachment = function (command, param) { // IndexedDBStorage.prototype.getAttachment = function (command, param) {
var jio_storage = this, // var jio_storage = this,
transaction, // transaction,
global_db, // global_db,
blob, // blob,
totalLength; // totalLength;
function getDesirePart(store, start, end) { // function getDesirePart(store, start, end) {
if (start > end) { // if (start > end) {
return; // return;
} // }
return getIndexedDB(store, [param._id, param._attachment, start]) // return getIndexedDB(store, [param._id, param._attachment, start])
.then(function (result) { // .then(function (result) {
var blobPart = result.blob; // var blobPart = result.blob;
if (result.blob.byteLength !== undefined) { // if (result.blob.byteLength !== undefined) {
blobPart = new Blob([result.blob]); // blobPart = new Blob([result.blob]);
} // }
if (blob) { // if (blob) {
blob = new Blob([blob, blobPart]); // blob = new Blob([blob, blobPart]);
} else { // } else {
blob = blobPart; // blob = blobPart;
} // }
return getDesirePart(store, start + 1, end); // return getDesirePart(store, start + 1, end);
}); // });
} // }
return new RSVP.Queue() // return new RSVP.Queue()
.push(function () { // .push(function () {
return openIndexedDB(jio_storage._database_name); // return openIndexedDB(jio_storage._database_name);
}) // })
.push(function (db) { // .push(function (db) {
global_db = db; // global_db = db;
transaction = db.transaction(["attachment", "blob"], "readwrite"); // transaction = db.transaction(["attachment", "blob"], "readwrite");
//check if the attachment exists // //check if the attachment exists
return promiseResearch(transaction, // return promiseResearch(transaction,
param._id, "attachment", "_id"); // param._id, "attachment", "_id");
}) // })
.push(function (researchResult) { // .push(function (researchResult) {
var result = researchResult.result, // var result = researchResult.result,
start, // start,
end; // end;
if (result === undefined || // if (result === undefined ||
result._attachment[param._attachment] === undefined) { // result._attachment[param._attachment] === undefined) {
throw ({"status": 404, "reason": "missing attachment", // throw ({"status": 404, "reason": "missing attachment",
"message": "IndexeddbStorage, unable to get attachment."}); // "message": "IndexeddbStorage, unable to get attachment."});
} // }
totalLength = result._attachment[param._attachment].length; // totalLength = result._attachment[param._attachment].length;
param._start = param._start === undefined ? 0 : param._start; // param._start = param._start === undefined ? 0 : param._start;
param._end = param._end === undefined ? totalLength // param._end = param._end === undefined ? totalLength
: param._end; // : param._end;
if (param._end > totalLength) { // if (param._end > totalLength) {
param._end = totalLength; // param._end = totalLength;
} // }
if (param._start < 0 || param._end < 0) { // if (param._start < 0 || param._end < 0) {
throw ({"status": 404, "reason": "invalide _start, _end", // throw ({"status": 404, "reason": "invalide _start, _end",
"message": "_start and _end must be positive"}); // "message": "_start and _end must be positive"});
} // }
if (param._start > param._end) { // if (param._start > param._end) {
throw ({"status": 404, "reason": "invalide offset", // throw ({"status": 404, "reason": "invalide offset",
"message": "start is great then end"}); // "message": "start is great then end"});
} // }
start = Math.floor(param._start / jio_storage._unite); // start = Math.floor(param._start / jio_storage._unite);
end = Math.floor(param._end / jio_storage._unite); // end = Math.floor(param._end / jio_storage._unite);
if (param._end % jio_storage._unite === 0) { // if (param._end % jio_storage._unite === 0) {
end -= 1; // end -= 1;
} // }
return getDesirePart(transaction.objectStore("blob"), // return getDesirePart(transaction.objectStore("blob"),
start, // start,
end); // end);
}) // })
.push(function () { // .push(function () {
var start = param._start % jio_storage._unite, // var start = param._start % jio_storage._unite,
end = start + param._end - param._start; // end = start + param._end - param._start;
blob = blob.slice(start, end); // blob = blob.slice(start, end);
return ({ "data": new Blob([blob], {type: "text/plain"})}); // return ({ "data": new Blob([blob], {type: "text/plain"})});
}) // })
.push(undefined, function (error) { // .push(undefined, function (error) {
// Check if transaction is ongoing, if so, abort it // // Check if transaction is ongoing, if so, abort it
if (transaction !== undefined) { // if (transaction !== undefined) {
transaction.abort(); // transaction.abort();
} // }
if (global_db !== undefined) { // if (global_db !== undefined) {
global_db.close(); // global_db.close();
} // }
throw error; // throw error;
}) // })
.push(command.success, command.error, command.notify); // .push(command.success, command.error, command.notify);
}; // };
/** // /**
* Remove an attachment // * Remove an attachment
* // *
* @method removeAttachment // * @method removeAttachment
* @param {Object} command The JIO command // * @param {Object} command The JIO command
* @param {Object} param The command parameters // * @param {Object} param The command parameters
*/ // */
IndexedDBStorage.prototype.removeAttachment = function (command, param) { // IndexedDBStorage.prototype.removeAttachment = function (command, param) {
var jio_storage = this, // var jio_storage = this,
transaction, // transaction,
global_db, // global_db,
totalLength; // totalLength;
function removePart(store, part) { // function removePart(store, part) {
if (part * jio_storage._unite >= totalLength) { // if (part * jio_storage._unite >= totalLength) {
return; // return;
} // }
return removeIndexedDB(store, [param._id, param._attachment, part]) // return removeIndexedDB(store, [param._id, param._attachment, part])
.then(function () { // .then(function () {
return removePart(store, part + 1); // return removePart(store, part + 1);
}); // });
} // }
return new RSVP.Queue() // return new RSVP.Queue()
.push(function () { // .push(function () {
return openIndexedDB(jio_storage._database_name); // return openIndexedDB(jio_storage._database_name);
}) // })
.push(function (db) { // .push(function (db) {
global_db = db; // global_db = db;
transaction = db.transaction(["attachment", "blob"], "readwrite"); // transaction = db.transaction(["attachment", "blob"], "readwrite");
//check if the attachment exists // //check if the attachment exists
return promiseResearch(transaction, param._id, // return promiseResearch(transaction, param._id,
"attachment", "_id"); // "attachment", "_id");
}) // })
.push(function (researchResult) { // .push(function (researchResult) {
var result = researchResult.result; // var result = researchResult.result;
if (result === undefined || // if (result === undefined ||
result._attachment[param._attachment] === undefined) { // result._attachment[param._attachment] === undefined) {
throw ({"status": 404, "reason": "missing attachment", // throw ({"status": 404, "reason": "missing attachment",
"message": // "message":
"IndexeddbStorage, document attachment not found."}); // "IndexeddbStorage, document attachment not found."});
} // }
totalLength = result._attachment[param._attachment].length; // totalLength = result._attachment[param._attachment].length;
//updata attachment // //updata attachment
delete result._attachment[param._attachment]; // delete result._attachment[param._attachment];
return putIndexedDB(researchResult.store, result); // return putIndexedDB(researchResult.store, result);
}) // })
.push(function () { // .push(function () {
var store = transaction.objectStore("blob"); // var store = transaction.objectStore("blob");
return removePart(store, 0); // return removePart(store, 0);
}) // })
.push(function () { // .push(function () {
return transactionEnd(transaction); // return transactionEnd(transaction);
}) // })
.push(function () { // .push(function () {
return ({ "status": 204 }); // return ({ "status": 204 });
}) // })
.push(undefined, function (error) { // .push(undefined, function (error) {
if (global_db !== undefined) { // if (global_db !== undefined) {
global_db.close(); // global_db.close();
} // }
throw error; // throw error;
}) // })
.push(command.success, command.error, command.notify); // .push(command.success, command.error, command.notify);
}; // };
IndexedDBStorage.prototype.allDocs = function (command, param, option) { // IndexedDBStorage.prototype.allDocs = function (command, param, option) {
/*jslint unparam: true */ // /*jslint unparam: true */
this.createDBIfNecessary(). // this.createDBIfNecessary().
then(this.getListMetadata.bind(this, option)). // then(this.getListMetadata.bind(this, option)).
then(command.success, command.error, command.notify); // then(command.success, command.error, command.notify);
}; // };
//
IndexedDBStorage.prototype.check = function (command) { // IndexedDBStorage.prototype.check = function (command) {
command.success(); // command.success();
}; // };
//
IndexedDBStorage.prototype.repair = function (command) { // IndexedDBStorage.prototype.repair = function (command) {
command.success(); // command.success();
}; // };
jIO.addStorage("indexeddb", IndexedDBStorage); jIO.addStorage("indexeddb", IndexedDBStorage);
})); }(jIO));
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global jIO, localStorage, setTimeout, window, define, Blob, Uint8Array, /*global jIO, localStorage, setTimeout, window, define, Blob, Uint8Array,
exports, require */ exports, require, console, RSVP */
/** /**
* JIO Local Storage. Type = 'local'. * JIO Local Storage. Type = 'local'.
...@@ -47,21 +47,7 @@ ...@@ -47,21 +47,7 @@
* @class LocalStorage * @class LocalStorage
*/ */
// define([module_name], [dependencies], module); (function (exports, jIO) {
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports, require('jio'));
}
window.local_storage = {};
module(window.local_storage, jIO);
}([
'exports',
'jio'
], function (exports, jIO) {
"use strict"; "use strict";
/** /**
...@@ -123,13 +109,6 @@ ...@@ -123,13 +109,6 @@
* @constructor * @constructor
*/ */
function LocalStorage(spec) { function LocalStorage(spec) {
if (typeof spec.username !== 'string' || spec.username === '') {
throw new TypeError("LocalStorage 'username' must be a non-empty string");
}
this._localpath = 'jio/localstorage/' + spec.username + '/' + (
spec.application_name === null || spec.application_name ===
undefined ? 'untitled' : spec.application_name.toString()
);
switch (spec.mode) { switch (spec.mode) {
case "memory": case "memory":
this._database = ram; this._database = ram;
...@@ -150,31 +129,25 @@ ...@@ -150,31 +129,25 @@
* Create a document in local storage. * Create a document in local storage.
* *
* @method post * @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store * @param {Object} metadata The metadata to store
* @param {Object} options The command options * @param {Object} options The command options
*/ */
LocalStorage.prototype.post = function (command, metadata) { LocalStorage.prototype.post = function (metadata) {
var doc, doc_id = metadata._id; var doc, doc_id = metadata._id;
if (!doc_id) { if (doc_id === undefined) {
doc_id = jIO.util.generateUuid(); doc_id = jIO.util.generateUuid();
} }
doc = this._storage.getItem(this._localpath + "/" + doc_id); if (this._storage.getItem(doc_id) === null) {
if (doc === null) {
// the document does not exist // the document does not exist
doc = jIO.util.deepClone(metadata); doc = jIO.util.deepClone(metadata);
doc._id = doc_id; doc._id = doc_id;
// XXX
delete doc._attachments; delete doc._attachments;
this._storage.setItem(this._localpath + "/" + doc_id, doc); this._storage.setItem(doc_id, doc);
command.success({"id": doc_id}); return doc_id;
} else {
// the document already exists
command.error(
"conflict",
"document exists",
"Cannot create a new document"
);
} }
// the document already exists
throw new jIO.util.jIOError("Cannot create a new document", 409);
}; };
/** /**
...@@ -185,130 +158,94 @@ ...@@ -185,130 +158,94 @@
* @param {Object} metadata The metadata to store * @param {Object} metadata The metadata to store
* @param {Object} options The command options * @param {Object} options The command options
*/ */
LocalStorage.prototype.put = function (command, metadata) { LocalStorage.prototype.put = function (metadata) {
var doc, tmp, status; var doc, tmp;
doc = this._storage.getItem(this._localpath + "/" + metadata._id); doc = this._storage.getItem(metadata._id);
if (doc === null) { if (doc === null) {
// the document does not exist // the document does not exist
doc = jIO.util.deepClone(metadata); doc = jIO.util.deepClone(metadata);
delete doc._attachments; delete doc._attachments;
status = "created";
} else { } else {
// the document already exists // the document already exists
tmp = jIO.util.deepClone(metadata); tmp = jIO.util.deepClone(metadata);
tmp._attachments = doc._attachments; tmp._attachments = doc._attachments;
doc = tmp; doc = tmp;
status = "no_content";
} }
// write // write
this._storage.setItem(this._localpath + "/" + metadata._id, doc); this._storage.setItem(metadata._id, doc);
command.success(status); return metadata._id;
}; };
/** /**
* Add an attachment to a document * Add an attachment to a document
* *
* @method putAttachment * @method putAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters * @param {Object} param The given parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
LocalStorage.prototype.putAttachment = function (command, param) { LocalStorage.prototype.putAttachment = function (param) {
var that = this, doc, status = "created"; var that = this, doc;
doc = this._storage.getItem(this._localpath + "/" + param._id); doc = this.get({"_id": param._id});
if (doc === null) {
// the document does not exist
return command.error(
"not_found",
"missing",
"Impossible to add attachment"
);
}
// the document already exists // the document already exists
// download data // download data
jIO.util.readBlobAsBinaryString(param._blob).then(function (e) { return new RSVP.Queue()
doc._attachments = doc._attachments || {}; .push(function () {
if (doc._attachments[param._attachment]) { return jIO.util.readBlobAsBinaryString(param._blob);
status = "no_content"; })
} .push(function (e) {
doc._attachments[param._attachment] = { doc._attachments = doc._attachments || {};
"content_type": param._blob.type, // if (doc._attachments[param._attachment]) {
"digest": jIO.util.makeBinaryStringDigest(e.target.result), // status = "no_content";
"length": param._blob.size // }
}; doc._attachments[param._attachment] = {
"content_type": param._blob.type,
that._storage.setItem(that._localpath + "/" + param._id + "/" + "digest": jIO.util.makeBinaryStringDigest(e.target.result),
param._attachment, e.target.result); "length": param._blob.size
that._storage.setItem(that._localpath + "/" + param._id, doc); };
command.success(status,
{"digest": doc._attachments[param._attachment].digest}); that._storage.setItem(param._id + "/" +
}, function (e) { param._attachment, e.target.result);
command.error( that._storage.setItem(param._id, doc);
"request_timeout", return {"digest": doc._attachments[param._attachment].digest};
"blob error", });
"Error " + e.status + ", unable to get blob content"
);
}, function (e) {
command.notify((e.loaded / e.total) * 100);
});
}; };
/** /**
* Get a document * Get a document
* *
* @method get * @method get
* @param {Object} command The JIO command
* @param {Object} param The given parameters * @param {Object} param The given parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
LocalStorage.prototype.get = function (command, param) { LocalStorage.prototype.get = function (param) {
var doc = this._storage.getItem( var doc = this._storage.getItem(param._id);
this._localpath + "/" + param._id if (doc === null) {
); throw new jIO.util.jIOError("Cannot find document", 404);
if (doc !== null) {
command.success({"data": doc});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
} }
return doc;
}; };
/** /**
* Get an attachment * Get an attachment
* *
* @method getAttachment * @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters * @param {Object} param The given parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
LocalStorage.prototype.getAttachment = function (command, param) { LocalStorage.prototype.getAttachment = function (param) {
var doc, i, uint8array, binarystring; var doc, i, uint8array, binarystring;
doc = this._storage.getItem(this._localpath + "/" + param._id); doc = this.get({"_id": param._id});
if (doc === null) {
return command.error(
"not_found",
"missing document",
"Cannot find document"
);
}
if (typeof doc._attachments !== 'object' || if (typeof doc._attachments !== 'object' ||
typeof doc._attachments[param._attachment] !== 'object') { typeof doc._attachments[param._attachment] !== 'object') {
return command.error( throw new jIO.util.jIOError("Cannot find attachment", 404);
"not_found",
"missing attachment",
"Cannot find attachment"
);
} }
// Storing data twice in binarystring and in uint8array (in memory) // Storing data twice in binarystring and in uint8array (in memory)
// is not a problem here because localStorage <= 5MB // is not a problem here because localStorage <= 5MB
binarystring = this._storage.getItem( binarystring = this._storage.getItem(
this._localpath + "/" + param._id + "/" + param._attachment param._id + "/" + param._attachment
) || ""; ) || "";
uint8array = new Uint8Array(binarystring.length); uint8array = new Uint8Array(binarystring.length);
for (i = 0; i < binarystring.length; i += 1) { for (i = 0; i < binarystring.length; i += 1) {
...@@ -318,10 +255,10 @@ ...@@ -318,10 +255,10 @@
"type": doc._attachments[param._attachment].content_type || "" "type": doc._attachments[param._attachment].content_type || ""
}); });
command.success({ return {
"data": uint8array, "data": uint8array,
"digest": doc._attachments[param._attachment].digest "digest": doc._attachments[param._attachment].digest
}); };
}; };
/** /**
...@@ -334,7 +271,7 @@ ...@@ -334,7 +271,7 @@
*/ */
LocalStorage.prototype.remove = function (command, param) { LocalStorage.prototype.remove = function (command, param) {
var doc, i, attachment_list; var doc, i, attachment_list;
doc = this._storage.getItem(this._localpath + "/" + param._id); doc = this._storage.getItem(param._id);
attachment_list = []; attachment_list = [];
if (doc !== null && typeof doc === "object") { if (doc !== null && typeof doc === "object") {
if (typeof doc._attachments === "object") { if (typeof doc._attachments === "object") {
...@@ -397,87 +334,88 @@ ...@@ -397,87 +334,88 @@
command.success(); command.success();
}; };
/** // /**
* Get all filenames belonging to a user from the document index // * Get all filenames belonging to a user from the document index
* // *
* @method allDocs // * @method allDocs
* @param {Object} command The JIO command // * @param {Object} command The JIO command
* @param {Object} param The given parameters // * @param {Object} param The given parameters
* @param {Object} options The command options // * @param {Object} options The command options
*/ // */
LocalStorage.prototype.allDocs = function (command, param, options) { // LocalStorage.prototype.allDocs = function (command, param, options) {
var i, row, path_re, rows, document_list, document_object, delete_id; // console.log("allDocs begin");
param.unused = true; // var i, row, path_re, rows, document_list, document_object, delete_id;
rows = []; // param.unused = true;
document_list = []; // rows = [];
path_re = new RegExp( // document_list = [];
"^" + jIO.Query.stringEscapeRegexpCharacters(this._localpath) + // path_re = new RegExp(
"/[^/]+$" // "^" + jIO.Query.stringEscapeRegexpCharacters(this._localpath) +
); // "/[^/]+$"
if (options.query === undefined && options.sort_on === undefined && // );
options.select_list === undefined && // if (options.query === undefined && options.sort_on === undefined &&
options.include_docs === undefined) { // options.select_list === undefined &&
rows = []; // options.include_docs === undefined) {
for (i in this._database) { // rows = [];
if (this._database.hasOwnProperty(i)) { // for (i in this._database) {
// filter non-documents // if (this._database.hasOwnProperty(i)) {
if (path_re.test(i)) { // // filter non-documents
row = { value: {} }; // if (path_re.test(i)) {
row.id = i.split('/').slice(-1)[0]; // row = { value: {} };
row.key = row.id; // row.id = i.split('/').slice(-1)[0];
if (options.include_docs) { // row.key = row.id;
row.doc = JSON.parse(this._storage.getItem(i)); // if (options.include_docs) {
} // row.doc = JSON.parse(this._storage.getItem(i));
rows.push(row); // }
} // rows.push(row);
} // }
} // }
command.success({"data": {"rows": rows, "total_rows": rows.length}}); // }
} else { // command.success({"data": {"rows": rows, "total_rows": rows.length}});
// create jio query object from returned results // } else {
for (i in this._database) { // // create jio query object from returned results
if (this._database.hasOwnProperty(i)) { // for (i in this._database) {
if (path_re.test(i)) { // if (this._database.hasOwnProperty(i)) {
document_list.push(this._storage.getItem(i)); // if (path_re.test(i)) {
} // document_list.push(this._storage.getItem(i));
} // }
} // }
options.select_list = options.select_list || []; // }
if (options.select_list.indexOf("_id") === -1) { // options.select_list = options.select_list || [];
options.select_list.push("_id"); // if (options.select_list.indexOf("_id") === -1) {
delete_id = true; // options.select_list.push("_id");
} // delete_id = true;
if (options.include_docs === true) { // }
document_object = {}; // if (options.include_docs === true) {
document_list.forEach(function (meta) { // document_object = {};
document_object[meta._id] = meta; // document_list.forEach(function (meta) {
}); // document_object[meta._id] = meta;
} // });
jIO.QueryFactory.create(options.query || "", // }
this._key_schema). // jIO.QueryFactory.create(options.query || "",
exec(document_list, options).then(function () { // this._key_schema).
document_list = document_list.map(function (value) { // exec(document_list, options).then(function () {
var o = { // document_list = document_list.map(function (value) {
"id": value._id, // var o = {
"key": value._id // "id": value._id,
}; // "key": value._id
if (options.include_docs === true) { // };
o.doc = document_object[value._id]; // if (options.include_docs === true) {
delete document_object[value._id]; // o.doc = document_object[value._id];
} // delete document_object[value._id];
if (delete_id) { // }
delete value._id; // if (delete_id) {
} // delete value._id;
o.value = value; // }
return o; // o.value = value;
}); // return o;
command.success({"data": { // });
"total_rows": document_list.length, // command.success({"data": {
"rows": document_list // "total_rows": document_list.length,
}}); // "rows": document_list
}); // }});
} // });
}; // }
// };
/** /**
* Check the storage or a specific document * Check the storage or a specific document
...@@ -679,51 +617,6 @@ ...@@ -679,51 +617,6 @@
jIO.addStorage('local', LocalStorage); jIO.addStorage('local', LocalStorage);
//////////////////////////////////////////////////////////////////////
// Tools
function createLocalDescription(username, application_name) {
if (typeof username !== 'string') {
throw new TypeError("LocalStorage username must be a string");
}
var description = {
"type": "local",
"username": username
};
if (typeof application_name === 'string') {
description.application_name = application_name;
}
return description;
}
function createMemoryDescription(username, application_name) {
var description = createLocalDescription(username, application_name);
description.mode = "memory";
return description;
}
/**
* Tool to help users to create local storage description for JIO
*
* @param {String} username The username
* @param {String} [application_name] The application_name
* @param {String} [mode="localStorage"] Use localStorage or memory
* @return {Object} The storage description
*/
function createDescription(username, application_name, mode) {
if (mode === undefined || mode.toString() === 'localStorage') {
return createLocalDescription(username, application_name);
}
if (mode.toString() === 'memory') {
return createMemoryDescription(username, application_name);
}
throw new TypeError("Unknown LocalStorage '" + mode.toString() + "' mode");
}
exports.createDescription = createDescription;
exports.createLocalDescription = createLocalDescription;
exports.createMemoryDescription = createMemoryDescription;
function clearLocalStorage() { function clearLocalStorage() {
var k; var k;
for (k in localStorage) { for (k in localStorage) {
...@@ -743,4 +636,4 @@ ...@@ -743,4 +636,4 @@
exports.clearLocalStorage = clearLocalStorage; exports.clearLocalStorage = clearLocalStorage;
exports.clearMemoryStorage = clearMemoryStorage; exports.clearMemoryStorage = clearMemoryStorage;
})); }(window, jIO));
/*jslint indent: 2, maxlen: 80, nomen: true, regexp: true, unparam: true */ /*jslint nomen: true*/
/*global define, RSVP, jIO */ /*global console*/
(function (jIO) {
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(RSVP, jIO);
}(['rsvp', 'jio'], function (RSVP, jIO) {
"use strict"; "use strict";
/** /**
* The jIO QueryStorage extension * The jIO QueryStorage extension
* *
...@@ -18,116 +10,23 @@ ...@@ -18,116 +10,23 @@
* @constructor * @constructor
*/ */
function QueryStorage(spec) { function QueryStorage(spec) {
this._sub_storage = spec.sub_storage; this._sub_storage = jIO.createJIO(spec.sub_storage);
this._key_schema = spec.key_schema; this._key_schema = spec.key_schema;
} }
QueryStorage.prototype.get = function () {
/** return this._sub_storage.get.apply(this._sub_storage, arguments);
* Get a document
* Parameters are passed through to the sub storage.
*
* @method get
* @param {Object} command The JIO command
*/
QueryStorage.prototype.get = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.get.apply(substorage, args).
then(command.success, command.error, command.notify);
};
/**
* Create a document in the sub storage.
* Parameters are passed through to the sub storage.
*
* @method post
* @param {Object} command The JIO command
*/
QueryStorage.prototype.post = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.post.apply(substorage, args).
then(command.success, command.error, command.notify);
}; };
QueryStorage.prototype.post = function () {
return this._sub_storage.post.apply(this._sub_storage, arguments);
/**
* Create or update a document in the sub storage.
* Parameters are passed through to the sub storage.
*
* @method put
* @param {Object} command The JIO command
*/
QueryStorage.prototype.put = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.put.apply(substorage, args).
then(command.success, command.error, command.notify);
};
/**
* Remove a document.
* Parameters are passed through to the sub storage.
*
* @method remove
* @param {Object} command The JIO command
*/
QueryStorage.prototype.remove = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.remove.apply(substorage, args).
then(command.success, command.error, command.notify);
};
/**
* Get an attachment.
* Parameters are passed through to the sub storage.
*
* @method getAttachment
* @param {Object} command The JIO command
*/
QueryStorage.prototype.getAttachment = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.getAttachment.apply(substorage, args).
then(command.success, command.error, command.notify);
}; };
QueryStorage.prototype.put = function () {
return this._sub_storage.put.apply(this._sub_storage, arguments);
/**
* Add an attachment to a document.
* Parameters are passed through to the sub storage.
*
* @method putAttachment
* @param {Object} command The JIO command
*/
QueryStorage.prototype.putAttachment = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.putAttachment.apply(substorage, args).
then(command.success, command.error, command.notify);
}; };
QueryStorage.prototype.remove = function () {
return this._sub_storage.remove.apply(this._sub_storage, arguments);
/**
* Remove an attachment.
* Parameters are passed through to the sub storage.
*
* @method removeAttachment
* @param {Object} command The JIO command
*/
QueryStorage.prototype.removeAttachment = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.removeAttachment.apply(substorage, args).
then(command.success, command.error, command.notify);
}; };
/** /**
* Retrieve documents. * Retrieve documents.
* This method performs an .allDocs() call on the substorage, * This method performs an .allDocs() call on the substorage,
...@@ -137,103 +36,74 @@ ...@@ -137,103 +36,74 @@
* @param {Object} command The given parameters * @param {Object} command The given parameters
* @param {Object} options The command options * @param {Object} options The command options
*/ */
QueryStorage.prototype.allDocs = function (command, param, options) { QueryStorage.prototype.allDocs = function (options) {
var that = this, console.log(options);
substorage = command.storage(this._sub_storage), // var context = this,
// we need the full documents in order to perform the query, will var substorage = this._sub_storage;
// remove them later if they were not required. // // we need the full documents in order to perform the query, will
include_docs = (options.include_docs || options.query) ? true : false; // // remove them later if they were not required.
// include_docs = (options.include_docs || options.query) ? true : false;
substorage.allDocs({
"include_docs": include_docs return substorage.allDocs.apply(substorage, arguments);
}).then(function (response) { // substorage.allDocs({
// "include_docs": include_docs
var data_rows = response.data.rows, docs = {}, row, i, l; // }).then(function (response) {
//
if (!include_docs) { // var data_rows = response.data.rows, docs = {}, row, i, l;
return response; //
} // if (!include_docs) {
// return response;
if (options.include_docs) { // }
for (i = 0, l = data_rows.length; i < l; i += 1) { //
row = data_rows[i]; // if (options.include_docs) {
docs[row.id] = JSON.parse(JSON.stringify(row.doc)); // for (i = 0, l = data_rows.length; i < l; i += 1) {
row.doc._id = row.id; // row = data_rows[i];
data_rows[i] = row.doc; // docs[row.id] = JSON.parse(JSON.stringify(row.doc));
} // row.doc._id = row.id;
} else { // data_rows[i] = row.doc;
for (i = 0, l = data_rows.length; i < l; i += 1) { // }
row = data_rows[i]; // } else {
row.doc._id = row.id; // for (i = 0, l = data_rows.length; i < l; i += 1) {
data_rows[i] = row.doc; // row = data_rows[i];
} // row.doc._id = row.id;
} // data_rows[i] = row.doc;
// }
if (options.select_list) { // }
options.select_list.push("_id"); //
} // if (options.select_list) {
// options.select_list.push("_id");
return jIO.QueryFactory.create(options.query || "", that._key_schema). // }
exec(data_rows, options). //
then(function (filtered_docs) { // return jIO.QueryFactory.create(options.query || "", that._key_schema).
// reconstruct filtered rows, preserving the order from docs // exec(data_rows, options).
if (options.include_docs) { // then(function (filtered_docs) {
for (i = 0, l = filtered_docs.length; i < l; i += 1) { // // reconstruct filtered rows, preserving the order from docs
filtered_docs[i] = { // if (options.include_docs) {
"id": filtered_docs[i]._id, // for (i = 0, l = filtered_docs.length; i < l; i += 1) {
"doc": docs[filtered_docs[i]._id], // filtered_docs[i] = {
"value": options.select_list ? filtered_docs[i] : {} // "id": filtered_docs[i]._id,
}; // "doc": docs[filtered_docs[i]._id],
delete filtered_docs[i].value._id; // "value": options.select_list ? filtered_docs[i] : {}
} // };
} else { // delete filtered_docs[i].value._id;
for (i = 0, l = filtered_docs.length; i < l; i += 1) { // }
filtered_docs[i] = { // } else {
"id": filtered_docs[i]._id, // for (i = 0, l = filtered_docs.length; i < l; i += 1) {
"value": options.select_list ? filtered_docs[i] : {} // filtered_docs[i] = {
}; // "id": filtered_docs[i]._id,
delete filtered_docs[i].value._id; // "value": options.select_list ? filtered_docs[i] : {}
} // };
} // delete filtered_docs[i].value._id;
response.data.rows = filtered_docs; // }
response.data.total_rows = filtered_docs.length; // }
return response; // response.data.rows = filtered_docs;
}); // response.data.total_rows = filtered_docs.length;
// return response;
}).then(command.success, command.error, command.notify); // });
}; //
// }).then(command.success, command.error, command.notify);
/**
* Check a document
* Parameters are passed through to the sub storage.
*
* @method check
* @param {Object} command The JIO command
*/
QueryStorage.prototype.check = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.check.apply(substorage, args).
then(command.success, command.error, command.notify);
}; };
/**
* Repair a document
* Parameters are passed through to the sub storage.
*
* @method repair
* @param {Object} command The JIO command
*/
QueryStorage.prototype.repair = function (command) {
var args = [].slice.call(arguments, 1), substorage;
substorage = command.storage(this._sub_storage);
substorage.repair.apply(substorage, args).
then(command.success, command.error, command.notify);
};
jIO.addStorage('query', QueryStorage); jIO.addStorage('query', QueryStorage);
})); }(jIO));
/*jslint nomen: true */
/**
* JIO Union Storage. Type = 'union'.
* This provide a unified access other multiple storage.
* New document are created in the first sub storage.
* Document are searched in each sub storage until it is found.
*
*
* Storage Description:
*
* {
* "type": "union",
* "storage_list": [
* sub_storage_description_1,
* sub_storage_description_2,
*
* sub_storage_description_X,
* ]
* }
*
* @class UnionStorage
*/
(function (jIO) {
"use strict";
/**
* The JIO UnionStorage extension
*
* @class UnionStorage
* @constructor
*/
function UnionStorage(spec) {
if (!Array.isArray(spec.storage_list)) {
throw new jIO.util.jIOError("storage_list is not an Array", 400);
}
var i;
this._storage_list = [];
for (i = 0; i < spec.storage_list.length; i += 1) {
this._storage_list.push(jIO.createJIO(spec.storage_list[i]));
}
}
UnionStorage.prototype._getWithStorageIndex = function () {
var i,
index = 0,
context = this,
arg = arguments,
result = this._storage_list[0].get.apply(this._storage_list[0], arg);
function handle404(j) {
result
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return context._storage_list[j].get.apply(context._storage_list[j],
arg)
.push(function (doc) {
index = j;
return doc;
});
}
throw error;
});
}
for (i = 1; i < this._storage_list.length; i += 1) {
handle404(i);
}
return result
.push(function (doc) {
return [index, doc];
});
};
/*
* Get a document
* Try on each substorage on after the other
*/
UnionStorage.prototype.get = function () {
return this._getWithStorageIndex.apply(this, arguments)
.push(function (result) {
return result[1];
});
};
/*
* Post a document
* Simply store on the first substorage
*/
UnionStorage.prototype.post = function () {
return this._storage_list[0].post.apply(this._storage_list[0], arguments);
};
/*
* Put a document
* Search the document location, and modify it in its storage.
*/
UnionStorage.prototype.put = function () {
var arg = arguments,
context = this;
return this._getWithStorageIndex({"_id": arg[0]._id})
.push(function (result) {
// Storage found, modify in it directly
var sub_storage = context._storage_list[result[0]];
return sub_storage.put.apply(sub_storage, arg);
});
};
/*
* Remove a document
* Search the document location, and remove it from its storage.
*/
UnionStorage.prototype.remove = function () {
var arg = arguments,
context = this;
return this._getWithStorageIndex({"_id": arg[0]._id})
.push(function (result) {
// Storage found, remove from it directly
var sub_storage = context._storage_list[result[0]];
return sub_storage.remove.apply(sub_storage, arg);
});
};
jIO.addStorage('union', UnionStorage);
}(jIO));
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global secureMethods, exports, console */
/**
* Inspired by nodejs EventEmitter class
* http://nodejs.org/api/events.html
*
* When an EventEmitter instance experiences an error, the typical action is
* to emit an 'error' event. Error events are treated as a special case in
* node. If there is no listener for it, then the default action throws the
* exception again.
*
* All EventEmitters emit the event 'newListener' when new listeners are added
* and 'removeListener' when a listener is removed.
*
* @class EventEmitter
* @constructor
*/
function EventEmitter() {
this._events = {};
this._maxListeners = 10;
}
/**
* Adds a listener to the end of the listeners array for the specified
* event.
*
* @method addListener
* @param {String} event The event name
* @param {Function} listener The listener callback
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.addListener = function (event, listener) {
var listener_list;
if (typeof listener !== "function") {
return this;
}
this.emit("newListener", event, listener);
listener_list = this._events[event];
if (listener_list === undefined) {
this._events[event] = listener;
listener_list = listener;
} else if (typeof listener_list === "function") {
this._events[event] = [listener_list, listener];
listener_list = this._events[event];
} else {
listener_list[listener_list.length] = listener;
}
if (this._maxListeners > 0 &&
typeof listener_list !== "function" &&
listener_list.length > this._maxListeners &&
listener_list.warned !== true) {
console.warn("warning: possible EventEmitter memory leak detected. " +
listener_list.length + " listeners added. " +
"Use emitter.setMaxListeners() to increase limit.");
listener_list.warned = true;
}
return this;
};
/**
* #crossLink "EventEmitter/addListener:method"
*
* @method on
*/
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
/**
* Adds a one time listener for the event. This listener is invoked only the
* next time the event is fired, after which it is removed.
*
* @method once
* @param {String} event The event name
* @param {Function} listener The listener callback
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.once = function (event, listener) {
var that = this, wrapper = function () {
that.removeListener(event, wrapper);
listener.apply(that, arguments);
};
wrapper.original = listener;
return that.on(event, wrapper);
};
/**
* Remove a listener from the listener array for the specified event.
* Caution: changes array indices in the listener array behind the listener
*
* @method removeListener
* @param {String} event The event name
* @param {Function} listener The listener callback
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.removeListener = function (event, listener) {
var listener_list = this._events[event], i;
if (listener_list) {
if (typeof listener_list === "function") {
if (listener_list === listener || listener_list.original === listener) {
delete this._events[event];
}
return this;
}
for (i = 0; i < listener_list.length; i += 1) {
if (listener_list[i] === listener ||
listener_list[i].original === listener) {
listener_list.splice(i, 1);
this.emit("removeListener", event, listener);
break;
}
}
if (listener_list.length === 1) {
this._events[event] = listener_list[0];
}
if (listener_list.length === 0) {
this._events[event] = undefined;
}
}
return this;
};
/**
* Removes all listeners, or those of the specified event.
*
* @method removeAllListeners
* @param {String} event The event name (optional)
* @return {EventEmitter} This emitter
*/
EventEmitter.prototype.removeAllListeners = function (event) {
var key;
if (event === undefined) {
for (key in this._events) {
if (this._events.hasOwnProperty(key)) {
delete this._events[key];
}
}
return this;
}
delete this._events[event];
return this;
};
/**
* By default EventEmitters will print a warning if more than 10 listeners
* are added for a particular event. This is a useful default which helps
* finding memory leaks. Obviously not all Emitters should be limited to 10.
* This function allows that to be increased. Set to zero for unlimited.
*
* @method setMaxListeners
* @param {Number} max_listeners The maximum of listeners
*/
EventEmitter.prototype.setMaxListeners = function (max_listeners) {
this._maxListeners = max_listeners;
};
/**
* Execute each of the listeners in order with the supplied arguments.
*
* @method emit
* @param {String} event The event name
* @param {Any} [args]* The listener argument to give
* @return {Boolean} true if event had listeners, false otherwise.
*/
EventEmitter.prototype.emit = function (event) {
var i, argument_list, listener_list;
listener_list = this._events[event];
if (typeof listener_list === 'function') {
listener_list = [listener_list];
} else if (Array.isArray(listener_list)) {
listener_list = listener_list.slice();
} else {
return false;
}
argument_list = Array.prototype.slice.call(arguments, 1);
for (i = 0; i < listener_list.length; i += 1) {
try {
listener_list[i].apply(this, argument_list);
} catch (e) {
if (this.listeners("error").length > 0) {
this.emit("error", e);
break;
}
throw e;
}
}
return true;
};
/**
* Returns an array of listeners for the specified event.
*
* @method listeners
* @param {String} event The event name
* @return {Array} The array of listeners
*/
EventEmitter.prototype.listeners = function (event) {
return (typeof this._events[event] === 'function' ?
[this._events[event]] : (this._events[event] || []).slice());
};
/**
* Static method; Return the number of listeners for a given event.
*
* @method listenerCount
* @static
* @param {EventEmitter} emitter The event emitter
* @param {String} event The event name
* @return {Number} The number of listener
*/
EventEmitter.listenerCount = function (emitter, event) {
return emitter.listeners(event).length;
};
exports.EventEmitter = EventEmitter;
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global EventEmitter, deepClone, inherits, exports */
/*global enableRestAPI, enableRestParamChecker, enableJobMaker, enableJobRetry,
enableJobReference, enableJobChecker, enableJobQueue, enableJobRecovery,
enableJobTimeout, enableJobExecuter */
function JIO(storage_spec, options) {
JIO.super_.call(this);
var shared = new EventEmitter();
shared.storage_spec = deepClone(storage_spec);
if (options === undefined) {
options = {};
} else if (typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError("JIO(): Optional argument 2 is not of type 'object'");
}
enableRestAPI(this, shared, options);
enableRestParamChecker(this, shared, options);
enableJobMaker(this, shared, options);
enableJobReference(this, shared, options);
enableJobRetry(this, shared, options);
enableJobTimeout(this, shared, options);
enableJobChecker(this, shared, options);
enableJobQueue(this, shared, options);
enableJobRecovery(this, shared, options);
enableJobExecuter(this, shared, options);
shared.emit('load');
}
inherits(JIO, EventEmitter);
JIO.createInstance = function (storage_spec, options) {
return new JIO(storage_spec, options);
};
exports.JIO = JIO;
exports.createJIO = JIO.createInstance;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global deepClone, dictFilter, uniqueJSONStringify */
/**
* Tool to manipulate a list of object containing at least one property: 'id'.
* Id must be a number > 0.
*
* @class JobQueue
* @constructor
* @param {Workspace} workspace The workspace where to store
* @param {String} namespace The namespace to use in the workspace
* @param {Array} job_keys An array of job keys to store
* @param {Array} [array] An array of object
*/
function JobQueue(workspace, namespace, job_keys, array) {
this._workspace = workspace;
this._namespace = namespace;
this._job_keys = job_keys;
if (Array.isArray(array)) {
this._array = array;
} else {
this._array = [];
}
}
/**
* Store the job queue into the workspace.
*
* @method save
*/
JobQueue.prototype.save = function () {
var i, job_queue = deepClone(this._array);
for (i = 0; i < job_queue.length; i += 1) {
dictFilter(job_queue[i], this._job_keys);
}
if (this._array.length === 0) {
this._workspace.removeItem(this._namespace);
} else {
this._workspace.setItem(
this._namespace,
uniqueJSONStringify(job_queue)
);
}
return this;
};
/**
* Loads the job queue from the workspace.
*
* @method load
*/
JobQueue.prototype.load = function () {
var job_list;
try {
job_list = JSON.parse(this._workspace.getItem(this._namespace));
} catch (ignore) {}
if (!Array.isArray(job_list)) {
job_list = [];
}
this.clear();
new JobQueue(job_list).repair();
this.update(job_list);
return this;
};
/**
* Returns the array version of the job queue
*
* @method asArray
* @return {Array} The job queue as array
*/
JobQueue.prototype.asArray = function () {
return this._array;
};
/**
* Removes elements which are not objects containing at least 'id' property.
*
* @method repair
*/
JobQueue.prototype.repair = function () {
var i, job;
for (i = 0; i < this._array.length; i += 1) {
job = this._array[i];
if (typeof job !== 'object' || Array.isArray(job) ||
typeof job.id !== 'number' || job.id <= 0) {
this._array.splice(i, 1);
i -= 1;
}
}
};
/**
* Post an object and generate an id
*
* @method post
* @param {Object} job The job object
* @return {Number} The generated id
*/
JobQueue.prototype.post = function (job) {
var i, next = 1;
// get next id
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id >= next) {
next = this._array[i].id + 1;
}
}
job.id = next;
this._array[this._array.length] = deepClone(job);
return this;
};
/**
* Put an object to the list. If an object contains the same id, it is replaced
* by the new one.
*
* @method put
* @param {Object} job The job object with an id
*/
JobQueue.prototype.put = function (job) {
var i;
if (typeof job.id !== 'number' || job.id <= 0) {
throw new TypeError("JobQueue().put(): Job id should be a positive number");
}
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id === job.id) {
break;
}
}
this._array[i] = deepClone(job);
return this;
};
/**
* Puts some object into the list. Update object with the same id, and add
* unreferenced one.
*
* @method update
* @param {Array} job_list A list of new jobs
*/
JobQueue.prototype.update = function (job_list) {
var i, j = 0, j_max, index = {}, next = 1, job, post_list = [];
j_max = this._array.length;
for (i = 0; i < job_list.length; i += 1) {
if (typeof job_list[i].id !== 'number' || job_list[i].id <= 0) {
// this job has no id, it has to be post
post_list[post_list.length] = job_list[i];
} else {
job = deepClone(job_list[i]);
if (index[job.id] !== undefined) {
// this job is on the list, update
this._array[index[job.id]] = job;
} else if (j === j_max) {
// this job is not on the list, update
this._array[this._array.length] = job;
} else {
// don't if the job is there or not
// searching same job in the original list
while (j < j_max) {
// references visited job
index[this._array[j].id] = j;
if (this._array[j].id >= next) {
next = this._array[j].id + 1;
}
if (this._array[j].id === job.id) {
// found on the list, just update
this._array[j] = job;
break;
}
j += 1;
}
if (j === j_max) {
// not found on the list, add to the end
this._array[this._array.length] = job;
} else {
// found on the list, already updated
j += 1;
}
}
if (job.id >= next) {
next = job.id + 1;
}
}
}
for (i = 0; i < post_list.length; i += 1) {
// adding job without id
post_list[i].id = next;
next += 1;
this._array[this._array.length] = deepClone(post_list[i]);
}
return this;
};
/**
* Get an object from an id. Returns undefined if not found
*
* @method get
* @param {Number} id The job id
* @return {Object} The job or undefined
*/
JobQueue.prototype.get = function (id) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id === id) {
return deepClone(this._array[i]);
}
}
};
/**
* Removes an object from an id
*
* @method remove
* @param {Number} id The job id
*/
JobQueue.prototype.remove = function (id) {
var i;
for (i = 0; i < this._array.length; i += 1) {
if (this._array[i].id === id) {
this._array.splice(i, 1);
return true;
}
}
return false;
};
/**
* Clears the list.
*
* @method clear
*/
JobQueue.prototype.clear = function () {
this._array.length = 0;
return this;
};
/*jslint indent: 2, maxlen: 80, sloppy: true */
/*global localStorage */
// keywords: js, javascript, store on local storage as array
function LocalStorageArray(namespace) {
var index, next;
function nextId() {
var i = next;
next += 1;
return i;
}
this.length = function () {
return index.length;
};
this.truncate = function (length) {
var i;
if (length === index.length) {
return this;
}
if (length > index.length) {
index.length = length;
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
}
while (length < index.length) {
i = index.pop();
if (i !== undefined && i !== null) {
delete localStorage[namespace + '.' + i];
}
}
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
};
this.get = function (i) {
return JSON.parse(localStorage[namespace + '.' + index[i]] || 'null');
};
this.set = function (i, value) {
if (index[i] === undefined || index[i] === null) {
index[i] = nextId();
localStorage[namespace + '.' + index[i]] = JSON.stringify(value);
localStorage[namespace + '.index'] = JSON.stringify(index);
} else {
localStorage[namespace + '.' + index[i]] = JSON.stringify(value);
}
return this;
};
this.append = function (value) {
index[index.length] = nextId();
localStorage[namespace + '.' + index[index.length - 1]] =
JSON.stringify(value);
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
};
this.pop = function (i) {
var value, key;
if (i === undefined || i === null) {
key = namespace + '.' + index[index.length - 1];
index.pop();
} else {
if (i < 0 || i >= index.length) {
return null;
}
key = namespace + '.' + i;
index.splice(i, 1);
}
value = localStorage[key];
if (index.length === 0) {
delete localStorage[namespace + '.index'];
} else {
localStorage[namespace + '.index'] = JSON.stringify(index);
}
delete localStorage[key];
return JSON.parse(value || 'null');
};
this.clear = function () {
var i;
for (i = 0; i < index.length; i += 1) {
delete localStorage[namespace + '.' + index[i]];
}
index = [];
delete localStorage[namespace + '.index'];
return this;
};
this.reload = function () {
var i;
index = JSON.parse(localStorage[namespace + '.index'] || '[]');
next = 0;
for (i = 0; i < index.length; i += 1) {
if (next < index[i]) {
next = index[i];
}
}
return this;
};
this.toArray = function () {
var i, list = [];
for (i = 0; i < index.length; i += 1) {
list[list.length] = this.get(i);
}
return list;
};
this.update = function (list) {
if (!Array.isArray(list)) {
throw new TypeError("LocalStorageArray().saveArray(): " +
"Argument 1 is not of type 'array'");
}
var i, location;
// update previous values
for (i = 0; i < list.length; i += 1) {
location = index[i];
if (location === undefined || location === null) {
location = nextId();
index[i] = location;
}
localStorage[namespace + '.' + location] =
JSON.stringify(list[i]);
}
// remove last ones
while (list.length < index.length) {
location = index.pop();
if (location !== undefined && location !== null) {
delete localStorage[namespace + '.' + location];
}
}
// store index
localStorage[namespace + '.index'] = JSON.stringify(index);
return this;
};
this.reload();
}
LocalStorageArray.saveArray = function (namespace, list) {
if (!Array.isArray(list)) {
throw new TypeError("LocalStorageArray.saveArray(): " +
"Argument 2 is not of type 'array'");
}
var local_storage_array = new LocalStorageArray(namespace).clear(), i;
for (i = 0; i < list.length; i += 1) {
local_storage_array.append(list[i]);
}
};
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global */
/**
* 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, DOMException */
function restCommandRejecter(param, args) {
// reject(status, reason, message, {"custom": "value"});
// reject(status, reason, {..});
// reject(status, {..});
var arg, current_priority, priority = [
// 0 - custom parameter values
{},
// 1 - default values
{
"status": constants.http_status.unknown,
"statusText": constants.http_status_text.unknown,
"message": "Command failed",
"reason": "unknown"
},
// 2 - status, reason, message properties
{},
// 3 - status, reason, message parameters
{},
// 4 - never change
{"result": "error", "method": param.method}
];
args = Array.prototype.slice.call(args);
arg = args.shift();
// priority 4 - never change
current_priority = priority[4];
if (param.kwargs._id) {
current_priority.id = param.kwargs._id;
}
if (/Attachment$/.test(param.method)) {
current_priority.attachment = param.kwargs._attachment;
}
// priority 3 - status, reason, message parameters
current_priority = priority[3];
// parsing first parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
// first parameter is mandatory
current_priority.status = arg;
arg = args.shift();
}
// parsing second parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
if (arg !== undefined) {
current_priority.reason = arg;
}
arg = args.shift();
}
// parsing third parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
if (arg !== undefined) {
current_priority.message = arg;
}
arg = args.shift();
}
// parsing fourth parameter if is an object
if (typeof arg === 'object' && arg !== null && !Array.isArray(arg)) {
// priority 0 - custom values
dictUpdate(priority[0], arg);
// priority 2 - status, reason, message properties
current_priority = priority[2];
if (arg.hasOwnProperty('reason')) {
current_priority.reason = arg.reason;
}
if (arg.hasOwnProperty('message')) {
current_priority.message = arg.message;
}
if ((arg.statusText || arg.status >= 0)) {
current_priority.status = arg.statusText || arg.status;
}
if (arg instanceof Error || arg instanceof DOMException) {
if (arg.code !== undefined && arg.code !== null) {
current_priority.code = arg.code;
}
if (arg.lineNumber !== undefined && arg.lineNumber !== null) {
current_priority.lineNumber = arg.lineNumber;
}
if (arg.columnNumber !== undefined && arg.columnNumber !== null) {
current_priority.columnNumber = arg.columnNumber;
}
if (arg.fileName !== undefined && arg.fileName !== null) {
current_priority.fileName = arg.fileName;
}
if (arg.filename !== undefined && arg.filename !== null) {
current_priority.filename = arg.filename;
}
if (arg.message !== undefined && arg.message !== null) {
current_priority.reason = arg.message;
}
current_priority.error = arg.name;
}
}
// merge priority dicts
for (current_priority = priority.length - 1;
current_priority > 0;
current_priority -= 1) {
dictUpdate(priority[current_priority - 1], priority[current_priority]);
}
priority = priority[0];
// check status
priority.statusText = constants.http_status_text[priority.status];
if (priority.statusText === undefined) {
return restCommandRejecter(param, [
// can create infernal loop if 'internal_storage_error' is not defined in
// the constants
'internal_storage_error',
'invalid response',
'Unknown status "' + priority.status + '"'
]);
}
priority.status = constants.http_status[priority.statusText];
// set default priority error if not already set
if (priority.error === undefined) {
priority.error = priority.statusText.toLowerCase().replace(/ /g, '_').
replace(/[^_a-z]/g, '');
}
param.storage_response = priority;
return param.solver.reject(deepClone(priority));
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global constants, methodType, dictUpdate, Blob, deepClone,
restCommandRejecter */
function restCommandResolver(param, args) {
// resolve('ok', {"custom": "value"});
// resolve(200, {...});
// resolve({...});
var arg, current_priority, priority = [
// 0 - custom parameter values
{},
// 1 - default values
{},
// 2 - status property
{},
// 3 - status parameter
{},
// 4 - never change
{"result": "success", "method": param.method}
];
args = Array.prototype.slice.call(args);
arg = args.shift();
// priority 4 - never change
current_priority = priority[4];
if (param.kwargs._id) {
current_priority.id = param.kwargs._id;
}
if (/Attachment$/.test(param.method)) {
current_priority.attachment = param.kwargs._attachment;
}
// priority 1 - default values
current_priority = priority[1];
if (param.method === 'post') {
current_priority.status = constants.http_status.created;
current_priority.statusText = constants.http_status_text.created;
} else if (methodType(param.method) === "writer" ||
param.method === "check") {
current_priority.status = constants.http_status.no_content;
current_priority.statusText = constants.http_status_text.no_content;
} else {
current_priority.status = constants.http_status.ok;
current_priority.statusText = constants.http_status_text.ok;
}
// priority 3 - status parameter
current_priority = priority[3];
// parsing first parameter if is not an object
if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {
if (arg !== undefined) {
current_priority.status = arg;
}
arg = args.shift();
}
// parsing second parameter if is an object
if (typeof arg === 'object' && arg !== null && !Array.isArray(arg)) {
// priority 0 - custom values
dictUpdate(current_priority, arg);
// priority 2 - status property
if (arg.hasOwnProperty("status") || arg.hasOwnProperty("statusText")) {
priority[2].status = arg.statusText || arg.status;
}
}
// merge priority dicts
for (current_priority = priority.length - 1;
current_priority > 0;
current_priority -= 1) {
dictUpdate(priority[current_priority - 1], priority[current_priority]);
}
priority = priority[0];
// check document id if post method
if (param.method === 'post' &&
(typeof priority.id !== 'string' || !priority.id)) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
'New document id have to be specified'
]);
}
// check status
priority.statusText = constants.http_status_text[priority.status];
if (priority.statusText === undefined) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
'Unknown status "' + priority.status + '"'
]);
}
priority.status = constants.http_status[priority.statusText];
// check data for get Attachment
if (param.method === 'getAttachment') {
if (typeof priority.data === 'string') {
priority.data = new Blob([priority.data], {
"type": priority.content_type || priority.mimetype || ""
});
delete priority.content_type;
delete priority.mimetype;
}
if (!(priority.data instanceof Blob)) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
'getAttachment method needs a Blob as returned "data".'
]);
}
// check data for readers (except check method)
} else if (methodType(param.method) === 'reader' &&
param.method !== 'check' &&
(typeof priority.data !== 'object' ||
priority.data === null ||
Object.getPrototypeOf(priority.data) !== Object.prototype)) {
return restCommandRejecter(param, [
'internal_storage_error',
'invalid response',
param.method + ' method needs a dict as returned "data".'
]);
}
param.storage_response = priority;
return param.solver.resolve(deepClone(priority));
}
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */ /*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global exports, Blob, FileReader, RSVP, hex_sha256, XMLHttpRequest, /*global exports, Blob, FileReader, RSVP, hex_sha256, XMLHttpRequest,
constants */ constants, console */
/** /**
* Do not exports these tools unless they are not writable, not configurable. * Do not exports these tools unless they are not writable, not configurable.
...@@ -44,66 +44,6 @@ function jsonDeepClone(object) { ...@@ -44,66 +44,6 @@ function jsonDeepClone(object) {
} }
exports.util.jsonDeepClone = jsonDeepClone; 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 returned 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 an invalid date. In phantomjs, it
// returns `"Invalid Date"` but in browsers it returns `null`. In
// browsers, giving `null` as parameter to `new Date()` doesn't return an
// invalid date.
// Cloning a date with `return new Date(object)` has 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 dictionary by adding/replacing key values from another dict. * Update a dictionary by adding/replacing key values from another dict.
...@@ -178,24 +118,6 @@ function arrayValuesToTypeDict(array) { ...@@ -178,24 +118,6 @@ function arrayValuesToTypeDict(array) {
return type_object; return type_object;
} }
/**
* 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;
/** /**
* Concatenate a `string` `n` times. * Concatenate a `string` `n` times.
...@@ -349,58 +271,6 @@ function readBlobAsText(blob) { ...@@ -349,58 +271,6 @@ function readBlobAsText(blob) {
} }
exports.util.readBlobAsText = readBlobAsText; exports.util.readBlobAsText = readBlobAsText;
/**
* Send request with XHR and return a promise. xhr.onload: The promise is
* resolved 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 the 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.xhrFields === 'object' && param.xhrFields !== null) {
for (k in param.xhrFields) {
if (param.xhrFields.hasOwnProperty(k)) {
xhr[k] = param.xhrFields[k];
}
}
}
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 * Acts like `Array.prototype.concat` but does not create a copy of the original
......
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayInsert, deepClone, defaults */
// creates
// - some defaults job rule actions
function enableJobChecker(jio, shared, options) {
// dependencies
// - shared.jobs Object Array
// - param.promise Object
// creates
// - shared.job_rules Array
// uses 'job:new' event
// emits 'job:modified', 'job:start', 'job:resolved',
// 'job:end', 'job:reject' events
shared.job_rule_action_names = [undefined, "ok", "wait", "update", "deny"];
shared.job_rule_actions = {
wait: function (original_job, new_job) {
original_job.promise.always(function () {
new_job.state = 'ready';
new_job.modified = new Date();
shared.emit('job:modified', new_job);
shared.emit('job:start', new_job);
});
new_job.state = 'waiting';
new_job.modified = new Date();
shared.emit('job:modified', new_job);
},
update: function (original_job, new_job) {
if (!new_job.solver) {
// promise associated to the job
new_job.state = 'done';
shared.emit('job:resolved', new_job, []); // XXX why resolve?
shared.emit('job:end', new_job);
} else {
if (!original_job.solver) {
original_job.solver = new_job.solver;
} else {
original_job.promise.then(function () {
new_job.command.resolve(deepClone(original_job.storage_response));
}, function () {
new_job.command.reject(deepClone(original_job.storage_response));
}, new_job.command.notify);
}
}
new_job.state = 'running';
new_job.modified = new Date();
shared.emit('job:modified', new_job);
},
deny: function (original_job, new_job) {
new_job.state = "running";
shared.emit('job:reject', new_job, [
'precondition_failed',
'command denied',
'Command rejected by the job checker.'
]);
}
};
function addJobRule(job_rule) {
var i, old_position, before_position, after_position;
// job_rule = {
// code_name: string
// conditions: [string, ...]
// action: 'wait',
// after: code_name
// before: code_name
// }
if (typeof job_rule !== 'object' || job_rule === null) {
// wrong job rule
return;
}
if (typeof job_rule.code_name !== 'string') {
// wrong code name
return;
}
if (!Array.isArray(job_rule.conditions)) {
// wrong conditions
return;
}
if (job_rule.single !== undefined && typeof job_rule.single !== 'boolean') {
// wrong single property
return;
}
if (shared.job_rule_action_names.indexOf(job_rule.action) === -1) {
// wrong action
return;
}
if (job_rule.action !== 'deny' && job_rule.single === true) {
// only 'deny' action doesn't require original_job parameter
return;
}
if (typeof job_rule.after !== 'string') {
job_rule.after = '';
}
if (typeof job_rule.before !== 'string') {
job_rule.before = '';
}
for (i = 0; i < shared.job_rules.length; i += 1) {
if (shared.job_rules[i].code_name === job_rule.after) {
after_position = i + 1;
}
if (shared.job_rules[i].code_name === job_rule.before) {
before_position = i;
}
if (shared.job_rules[i].code_name === job_rule.code_name) {
old_position = i;
}
}
job_rule = {
"code_name": job_rule.code_name,
"conditions": job_rule.conditions,
"single": job_rule.single || false,
"action": job_rule.action || "ok"
};
if (before_position === undefined) {
before_position = shared.job_rules.length;
}
if (after_position > before_position) {
before_position = undefined;
}
if (job_rule.action !== "ok" && before_position !== undefined) {
arrayInsert(shared.job_rules, before_position, job_rule);
}
if (old_position !== undefined) {
if (old_position >= before_position) {
old_position += 1;
}
shared.job_rules.splice(old_position, 1);
}
}
function jobsRespectConditions(original_job, new_job, conditions) {
var j;
// browsing conditions
for (j = 0; j < conditions.length; j += 1) {
if (defaults.job_rule_conditions[conditions[j]]) {
if (
!defaults.job_rule_conditions[conditions[j]](original_job, new_job)
) {
return false;
}
}
}
return true;
}
function checkJob(job) {
var i, j;
if (job.state === 'ready') {
// browsing rules
for (i = 0; i < shared.job_rules.length; i += 1) {
if (shared.job_rules[i].single) {
// no browse
if (
jobsRespectConditions(
job,
undefined,
shared.job_rules[i].conditions
)
) {
shared.job_rule_actions[shared.job_rules[i].action](
undefined,
job
);
return;
}
} else {
// browsing jobs
for (j = shared.jobs.length - 1; j >= 0; j -= 1) {
if (shared.jobs[j] !== job) {
if (
jobsRespectConditions(
shared.jobs[j],
job,
shared.job_rules[i].conditions
)
) {
shared.job_rule_actions[shared.job_rules[i].action](
shared.jobs[j],
job
);
return;
}
}
}
}
}
}
}
var index;
if (options.job_management !== false) {
shared.job_rules = [{
"code_name": "readers update",
"conditions": [
"sameStorageDescription",
"areReaders",
"sameMethod",
"sameParameters",
"sameOptions"
],
"action": "update"
}, {
"code_name": "metadata writers update",
"conditions": [
"sameStorageDescription",
"areWriters",
"useMetadataOnly",
"sameMethod",
"haveDocumentIds",
"sameParameters"
],
"action": "update"
}, {
"code_name": "writers wait",
"conditions": [
"sameStorageDescription",
"areWriters",
"haveDocumentIds",
"sameDocumentId"
],
"action": "wait"
}];
if (options.clear_job_rules === true) {
shared.job_rules.length = 0;
}
if (Array.isArray(options.job_rules)) {
for (index = 0; index < options.job_rules.length; index += 1) {
addJobRule(deepClone(options.job_rules[index]));
}
}
shared.on('job:new', checkJob);
}
jio.jobRules = function () {
return deepClone(shared.job_rules);
};
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global setTimeout, Job, createStorage, deepClone, restCommandResolver,
restCommandRejecter */
function enableJobExecuter(jio, shared) { // , options) {
// uses 'job:new' events
// uses actions 'job:resolve', 'job:reject' and 'job:notify'
// emits 'job:modified', 'job:started', 'job:resolved',
// 'job:rejected', 'job:notified' and 'job:end' events
// emits action 'job:start'
function startJobIfReady(job) {
if (job.state === 'ready') {
shared.emit('job:start', job);
}
}
function executeJobIfReady(param) {
var storage;
if (param.state === 'ready') {
param.tried += 1;
param.started = new Date();
param.state = 'running';
param.modified = new Date();
shared.emit('job:modified', param);
shared.emit('job:started', param);
try {
storage = createStorage(deepClone(param.storage_spec));
} catch (e) {
return param.command.reject(
'internal_storage_error',
'invalid description',
'Check if the storage description respects the ' +
'constraints provided by the storage designer. (' +
e.name + ": " + e.message + ')'
);
}
if (typeof storage[param.method] !== 'function') {
return param.command.reject(
'not_implemented',
'method missing',
'Storage "' + param.storage_spec.type + '", "' +
param.method + '" method is missing.'
);
}
setTimeout(function () {
storage[param.method](
deepClone(param.command),
deepClone(param.kwargs),
deepClone(param.options)
);
});
}
}
function endAndResolveIfRunning(job, args) {
if (job.state === 'running') {
job.state = 'done';
job.modified = new Date();
shared.emit('job:modified', job);
if (job.solver) {
restCommandResolver(job, args);
}
shared.emit('job:resolved', job, args);
shared.emit('job:end', job);
}
}
function endAndRejectIfRunning(job, args) {
if (job.state === 'running') {
job.state = 'fail';
job.modified = new Date();
shared.emit('job:modified', job);
if (job.solver) {
restCommandRejecter(job, args);
}
shared.emit('job:rejected', job, args);
shared.emit('job:end', job);
}
}
function notifyJobIfRunning(job, args) {
if (job.state === 'running' && job.solver) {
job.solver.notify(args[0]);
shared.emit('job:notified', job, args);
}
}
// listeners
shared.on('job:new', startJobIfReady);
shared.on('job:start', executeJobIfReady);
shared.on('job:resolve', endAndResolveIfRunning);
shared.on('job:reject', endAndRejectIfRunning);
shared.on('job:notify', notifyJobIfRunning);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend */
function enableJobMaker(jio, shared, options) {
// dependencies
// - param.method
// - param.storage_spec
// - param.kwargs
// - param.options
// uses (Job)
// - param.created date
// - param.modified date
// - param.tried number >= 0
// - param.state string 'ready'
// - param.method string
// - param.storage_spec object
// - param.kwargs object
// - param.options object
// - param.command object
// list of job events:
// - Job existence -> new, end
// - Job execution -> started, stopped
// - Job resolution -> resolved, rejected, notified, cancelled
// - Job modification -> modified
// emits actions 'job:resolve', 'job:reject' and 'job:notify'
// uses `rest method` events
// emits 'job:new' event
shared.job_keys = arrayExtend(shared.job_keys || [], [
"created",
"modified",
"tried",
"state",
"method",
"storage_spec",
"kwargs",
"options"
]);
function addCommandToJob(job) {
job.command = {};
job.command.resolve = function () {
shared.emit('job:resolve', job, arguments);
};
job.command.success = job.command.resolve;
job.command.reject = function () {
shared.emit('job:reject', job, arguments);
};
job.command.error = job.command.reject;
job.command.notify = function () {
shared.emit('job:notify', job, arguments);
};
job.command.storage = function () {
return shared.createRestApi.apply(null, arguments);
};
job.command.setCanceller = function (canceller) {
job.cancellers["command:canceller"] = canceller;
};
job.cancellers = job.cancellers || {};
job.cancellers["job:canceller"] = function () {
shared.emit("job:reject", job, ["cancelled"]);
};
}
function createJobFromRest(param) {
if (param.solver) {
// rest parameters are good
shared.emit('job:new', param);
}
}
function initJob(job) {
job.state = 'ready';
if (typeof job.tried !== 'number' || !isFinite(job.tried)) {
job.tried = 0;
}
if (!job.created) {
job.created = new Date();
}
addCommandToJob(job);
job.modified = new Date();
}
// listeners
shared.rest_method_names.forEach(function (method) {
shared.on(method, createJobFromRest);
});
shared.on('job:new', initJob);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, localStorage, Workspace, uniqueJSONStringify, JobQueue,
constants, setTimeout, clearTimeout */
function enableJobQueue(jio, shared, options) {
// dependencies
// - shared.storage_spec Object
// uses
// - options.workspace Workspace
// - shared.job_keys String Array
// creates
// - shared.storage_spec_str String
// - shared.workspace Workspace
// - shared.job_queue JobQueue
// uses 'job:new', 'job:started', 'job:stopped', 'job:modified',
// 'job:notified', 'job:end' events
// emits 'job:end' event
function postJobIfReady(param) {
if (!param.stored && param.state === 'ready') {
clearTimeout(param.queue_ident);
delete param.queue_ident;
shared.job_queue.load();
shared.job_queue.post(param);
shared.job_queue.save();
param.stored = true;
}
}
function deferredPutJob(param) {
if (param.queue_ident === undefined) {
param.queue_ident = setTimeout(function () {
delete param.queue_ident;
if (param.stored) {
shared.job_queue.load();
shared.job_queue.put(param);
shared.job_queue.save();
}
});
}
}
function removeJob(param) {
clearTimeout(param.queue_ident);
delete param.queue_ident;
if (param.stored) {
shared.job_queue.load();
shared.job_queue.remove(param.id);
shared.job_queue.save();
delete param.stored;
delete param.id;
}
}
function initJob(param) {
if (!param.command.end) {
param.command.end = function () {
shared.emit('job:end', param);
};
}
}
shared.on('job:new', initJob);
if (options.job_management !== false) {
shared.job_keys = arrayExtend(shared.job_keys || [], ["id"]);
if (typeof options.workspace !== 'object') {
shared.workspace = localStorage;
} else {
shared.workspace = new Workspace(options.workspace);
}
if (!shared.storage_spec_str) {
shared.storage_spec_str = uniqueJSONStringify(shared.storage_spec);
}
shared.job_queue = new JobQueue(
shared.workspace,
'jio/jobs/' + shared.storage_spec_str,
shared.job_keys
);
// Listeners
shared.on('job:new', postJobIfReady);
shared.on('job:started', deferredPutJob);
shared.on('job:stopped', deferredPutJob);
shared.on('job:modified', deferredPutJob);
shared.on('job:notified', deferredPutJob);
shared.on('job:end', removeJob);
}
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global setTimeout, methodType */
function enableJobRecovery(jio, shared, options) {
// dependencies
// - JobQueue enabled and before this
// uses
// - shared.job_queue JobQueue
// emits 'job:new' event
function numberOrDefault(number, default_value) {
return (typeof number === 'number' &&
isFinite(number) ? number : default_value);
}
function recoverJob(param) {
shared.job_queue.load();
shared.job_queue.remove(param.id);
delete param.id;
if (methodType(param.method) === 'writer' &&
(param.state === 'ready' ||
param.state === 'running' ||
param.state === 'waiting')) {
shared.job_queue.save();
shared.emit('job:new', param);
}
}
function jobWaiter(id, modified) {
return function () {
var job;
shared.job_queue.load();
job = shared.job_queue.get(id);
if (job && job.modified === modified) {
// job not modified, no one takes care of it
recoverJob(job);
}
};
}
var i, job_array, delay, deadline, recovery_delay;
// 1 m 30 s === default firefox request timeout
recovery_delay = numberOrDefault(options.recovery_delay, 90000);
if (recovery_delay < 0) {
recovery_delay = 90000;
}
if (options.job_management !== false && options.job_recovery !== false) {
shared.job_queue.load();
job_array = shared.job_queue.asArray();
for (i = 0; i < job_array.length; i += 1) {
delay = numberOrDefault(job_array[i].timeout + recovery_delay,
recovery_delay);
deadline = new Date(job_array[i].modified).getTime() + delay;
if (!isFinite(delay)) {
// 'modified' date is broken
recoverJob(job_array[i]);
} else if (deadline <= Date.now()) {
// deadline reached
recoverJob(job_array[i]);
} else {
// deadline not reached yet
// wait until deadline is reached then check job again
setTimeout(jobWaiter(job_array[i].id, job_array[i].modified),
deadline - Date.now());
}
}
}
}
/*jslint indent: 2, maxlen: 80, sloppy: true, unparam: true */
/*global ReferenceArray */
function enableJobReference(jio, shared, options) {
// creates
// - shared.jobs Object Array
// uses 'job:new' and 'job:end' events
shared.jobs = [];
var job_references = new ReferenceArray(shared.jobs);
shared.on('job:new', function (param) {
job_references.put(param);
});
shared.on('job:end', function (param) {
job_references.remove(param);
});
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, setTimeout, methodType, constants */
function enableJobRetry(jio, shared, options) {
// dependencies
// - param.method
// - param.storage_spec
// - param.kwargs
// - param.options
// - param.command
// uses
// - options.default_writers_max_retry number >= 0 or null
// - options.default_readers_max_retry number >= 0 or null
// - options.default_max_retry number >= 0 or null
// - options.writers_max_retry number >= 0 or null
// - options.readers_max_retry number >= 0 or null
// - options.max_retry number >= 0 or null
// - param.modified date
// - param.tried number >= 0
// - param.max_retry >= 0 or undefined
// - param.state string 'ready' 'waiting'
// - param.method string
// - param.storage_spec object
// - param.kwargs object
// - param.options object
// - param.command object
// uses 'job:new' and 'job:retry' events
// emits action 'job:start' event
// emits 'job:retry', 'job:reject', 'job:modified' and 'job:stopped' events
shared.job_keys = arrayExtend(shared.job_keys || [], ["max_retry"]);
var writers_max_retry, readers_max_retry, max_retry;
function defaultMaxRetry(param) {
if (methodType(param.method) === 'writers') {
if (max_retry === undefined) {
return writers_max_retry;
}
return max_retry;
}
if (max_retry === undefined) {
return readers_max_retry;
}
return max_retry;
}
function positiveNumberOrDefault(number, default_value) {
return (typeof number === 'number' &&
number >= 0 ?
number : default_value);
}
function positiveNumberNullOrDefault(number, default_value) {
return ((typeof number === 'number' &&
number >= 0) || number === null ?
number : default_value);
}
max_retry = positiveNumberNullOrDefault(
options.max_retry || options.default_max_retry,
undefined
);
writers_max_retry = positiveNumberNullOrDefault(
options.writers_max_retry || options.default_writers_max_retry,
null
);
readers_max_retry = positiveNumberNullOrDefault(
options.readers_max_retry || options.default_readers_max_retry,
2
);
function initJob(param) {
if (typeof param.max_retry !== 'number' || param.max_retry < 0) {
param.max_retry = positiveNumberOrDefault(
param.options.max_retry,
defaultMaxRetry(param)
);
}
param.command.reject = function (status) {
if (constants.http_action[status || 0] === "retry") {
shared.emit('job:retry', param, arguments);
} else {
shared.emit('job:reject', param, arguments);
}
};
param.command.retry = function () {
shared.emit('job:retry', param, arguments);
};
}
function retryIfRunning(param, args) {
if (param.state === 'running') {
if (param.max_retry === undefined ||
param.max_retry === null ||
param.max_retry >= param.tried) {
param.state = 'waiting';
param.modified = new Date();
shared.emit('job:modified', param);
shared.emit('job:stopped', param);
setTimeout(function () {
param.state = 'ready';
param.modified = new Date();
shared.emit('job:modified', param);
shared.emit('job:start', param);
}, Math.min(10000, param.tried * 2000));
} else {
shared.emit('job:reject', param, args);
}
}
}
// listeners
shared.on('job:new', initJob);
shared.on('job:retry', retryIfRunning);
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayExtend, setTimeout, clearTimeout */
function enableJobTimeout(jio, shared, options) {
// dependencies
// - param.tried number > 0
// - param.state string 'running'
// uses
// - param.tried number > 0
// - param.timeout number >= 0
// - param.timeout_ident Timeout
// - param.state string 'running'
// uses 'job:new', 'job:stopped', 'job:started',
// 'job:notified' and 'job:end' events
// emits 'job:modified' event
shared.job_keys = arrayExtend(shared.job_keys || [], ["timeout"]);
function positiveNumberOrDefault(number, default_value) {
return (typeof number === 'number' &&
number >= 0 ?
number : default_value);
}
// Infinity by default
var default_timeout = positiveNumberOrDefault(options.default_timeout, 0);
function timeoutReject(param) {
return function () {
param.command.reject(
'request_timeout',
'timeout',
'Operation canceled after around ' + (
Date.now() - param.modified.getTime()
) + ' milliseconds of inactivity.'
);
};
}
function initJob(job) {
if (typeof job.timeout !== 'number' || !isFinite(job.timeout) ||
job.timeout < 0) {
job.timeout = positiveNumberOrDefault(
job.options.timeout,
default_timeout
);
}
job.modified = new Date();
shared.emit('job:modified', job);
}
function clearJobTimeout(job) {
clearTimeout(job.timeout_ident);
delete job.timeout_ident;
}
function restartJobTimeoutIfRunning(job) {
clearTimeout(job.timeout_ident);
if (job.state === 'running' && job.timeout > 0) {
job.timeout_ident = setTimeout(timeoutReject(job), job.timeout);
job.modified = new Date();
} else {
delete job.timeout_ident;
}
}
// listeners
shared.on('job:new', initJob);
shared.on("job:stopped", clearJobTimeout);
shared.on("job:end", clearJobTimeout);
shared.on("job:started", restartJobTimeoutIfRunning);
shared.on("job:notified", restartJobTimeoutIfRunning);
}
/*jslint indent: 2, maxlen: 80, sloppy: true */
/*global arrayValuesToTypeDict, dictClear, RSVP, deepClone */
// adds methods to JIO
// - post
// - put
// - get
// - remove
// - allDocs
// - putAttachment
// - getAttachment
// - removeAttachment
// - check
// - repair
// event shared objet
// - storage_spec object
// - method string
// - kwargs object
// - options object
// - solver object
// - solver.resolve function
// - solver.reject function
// - solver.notify function
// - cancellers object
// - promise object
function enableRestAPI(jio, shared) { // (jio, shared, options)
shared.rest_method_names = [
"post",
"put",
"get",
"remove",
"allDocs",
"putAttachment",
"getAttachment",
"removeAttachment",
"check",
"repair"
];
function prepareParamAndEmit(method, storage_spec, args) {
var callback, type_dict, param = {};
type_dict = arrayValuesToTypeDict(Array.prototype.slice.call(args));
type_dict.object = type_dict.object || [];
if (method !== 'allDocs') {
param.kwargs = type_dict.object.shift();
if (param.kwargs === undefined) {
throw new TypeError("JIO()." + method +
"(): Argument 1 is not of type 'object'");
}
param.kwargs = deepClone(param.kwargs);
} else {
param.kwargs = {};
}
param.solver = {};
param.options = deepClone(type_dict.object.shift()) || {};
param.promise = new RSVP.Promise(function (resolve, reject, notify) {
param.solver.resolve = resolve;
param.solver.reject = reject;
param.solver.notify = notify;
}, function () {
if (!param.cancellers) { return; }
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;
}
});
["removeAttachment"].forEach(function (method) {
shared.on(method, function (param) {
if (!checkId(param)) {
checkAttachmentId(param);
}
});
});
["getAttachment"].forEach(function (method) {
shared.on(method, function (param) {
if (param.storage_spec.type !== "indexeddb" &&
param.storage_spec.type !== "dav" &&
(param.kwargs._start !== undefined
|| param.kwargs._end !== undefined)) {
restCommandRejecter(param, [
'bad_request',
'unsupport',
'_start, _end not support'
]);
return false;
}
if (!checkId(param)) {
checkAttachmentId(param);
}
});
});
["check", "repair"].forEach(function (method) {
shared.on(method, function (param) {
if (param.kwargs._id !== undefined) {
if (!checkId(param)) {
return;
}
}
});
});
}
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
window.jIO = {};
module(window.jIO, RSVP, {hex_sha256: hex_sha256});
}(['exports', 'rsvp', 'sha256'], function (exports, RSVP, sha256) {
"use strict";
var hex_sha256 = sha256.hex_sha256;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query: true, query_class_dict: true, inherits: true, /*global Query: true, query_class_dict: true, inherits: true,
exports, QueryFactory, RSVP, sequence */ window, QueryFactory, RSVP, sequence */
/** /**
* The ComplexQuery inherits from Query, and compares one or several metadata * The ComplexQuery inherits from Query, and compares one or several metadata
...@@ -208,4 +208,4 @@ ComplexQuery.prototype.NOT = function (item) { ...@@ -208,4 +208,4 @@ ComplexQuery.prototype.NOT = function (item) {
query_class_dict.complex = ComplexQuery; query_class_dict.complex = ComplexQuery;
exports.ComplexQuery = ComplexQuery; window.ComplexQuery = ComplexQuery;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit: /*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
true, select: true, exports, stringEscapeRegexpCharacters: true, true, select: true, window, stringEscapeRegexpCharacters: true,
deepClone, RSVP, sequence */ deepClone, RSVP, sequence */
/** /**
...@@ -194,4 +194,4 @@ Query.prototype.serialized = function () { ...@@ -194,4 +194,4 @@ Query.prototype.serialized = function () {
return undefined; return undefined;
}; };
exports.Query = Query; window.Query = Query;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global exports, ComplexQuery, SimpleQuery, Query, parseStringToObject, /*global window, ComplexQuery, SimpleQuery, Query, parseStringToObject,
query_class_dict */ query_class_dict */
/** /**
...@@ -36,4 +36,4 @@ QueryFactory.create = function (object, key_schema) { ...@@ -36,4 +36,4 @@ QueryFactory.create = function (object, key_schema) {
"Argument 1 is not a search text or a parsable object"); "Argument 1 is not a search text or a parsable object");
}; };
exports.QueryFactory = QueryFactory; window.QueryFactory = QueryFactory;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Query, inherits, query_class_dict, exports, /*global Query, inherits, query_class_dict, window,
searchTextToRegExp, RSVP */ searchTextToRegExp, RSVP */
var checkKeySchema = function (key_schema) { var checkKeySchema = function (key_schema) {
...@@ -394,4 +394,4 @@ SimpleQuery.prototype[">="] = function (object_value, comparison_value) { ...@@ -394,4 +394,4 @@ SimpleQuery.prototype[">="] = function (object_value, comparison_value) {
query_class_dict.simple = SimpleQuery; query_class_dict.simple = SimpleQuery;
exports.SimpleQuery = SimpleQuery; window.SimpleQuery = SimpleQuery;
This source diff could not be displayed because it is too large. You can view the blob instead.
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint maxlen: 120, nomen: true */
/*global module, test, stop, start, expect, ok, deepEqual, location, sinon, /*global indexedDB, test_util, console, Blob, sinon*/
davstorage_spec, RSVP, jIO, test_util, dav_storage, btoa, define, (function (jIO, QUnit) {
setTimeout, clearTimeout, indexedDB */
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(test_util, RSVP, jIO);
}([
'test_util',
'rsvp',
'jio',
'indexeddbstorage',
'qunit'
], function (util, RSVP, jIO) {
"use strict"; "use strict";
module("indexeddbStorage"); var test = QUnit.test,
function success(promise) { stop = QUnit.stop,
return new RSVP.Promise(function (resolve, notify) { start = QUnit.start,
promise.then(resolve, resolve, notify); ok = QUnit.ok,
}, function () { expect = QUnit.expect,
promise.cancel(); deepEqual = QUnit.deepEqual,
}); equal = QUnit.equal,
} module = QUnit.module;
test("Scenario", 46, function () { module("indexeddbStorage", {
indexedDB.deleteDatabase("jio:test"); setup: function () {
var server, shared = {}, jio = jIO.createJIO( // localStorage.clear();
{"type" : "indexeddb", this.jio = jIO.createJIO({
"database" : "test" "type": "indexeddb",
}, "database": "qunit"
{"workspace": {}}
);
stop();
server = {restore: function () {
return;
}};
function postNewDocument() {
return jio.post({"title": "Unique ID"});
}
function postNewDocumentTest(answer) {
var uuid = answer.id;
answer.id = "<uuid>";
deepEqual(answer, {
"id": "<uuid>",
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Post a new document");
ok(util.isUuid(uuid), "New document id should look like " +
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
shared.created_document_id = uuid;
}
function getCreatedDocument() {
return jio.get({"_id": shared.created_document_id});
}
function getCreatedDocumentTest(answer) {
deepEqual(answer, {
"data": {
"_id": shared.created_document_id,
"title": "Unique ID"
},
"id": shared.created_document_id,
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get new document");
}
function postSpecificDocument() {
return jio.post({"_id": "b", "title": "Bee"});
}
function postSpecificDocumentTest(answer) {
deepEqual(answer, {
"id": "b",
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Post specific document");
}
function listDocument() {
return jio.allDocs();
}
function list2DocumentsTest(answer) {
if (answer && answer.data && Array.isArray(answer.data.rows)) {
answer.data.rows.sort(function (a) {
return a.id === "b" ? 1 : 0;
});
}
deepEqual(answer, {
"data": {
"total_rows": 2,
"rows": [{
"id": shared.created_document_id,
"value": {}
}, {
"id": "b",
"value": {}
}]
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "List 2 documents");
}
function listDocumentsWithMetadata() {
return jio.allDocs({"include_docs": true});
}
function list2DocumentsWithMetadataTest(answer) {
if (answer && answer.data && Array.isArray(answer.data.rows)) {
answer.data.rows.sort(function (a) {
return a.id === "b" ? 1 : 0;
});
}
deepEqual(answer, {
"data": {
"total_rows": 2,
"rows": [{
"id": shared.created_document_id,
"value": {},
"doc": {
"_id": shared.created_document_id,
"title": "Unique ID"
}
}, {
"id": "b",
"value": {},
"doc": {
"_id": "b",
"title": "Bee"
}
}]
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "List 2 documents with their metadata");
}
function removeCreatedDocument() {
return jio.remove({"_id": shared.created_document_id});
}
function removeCreatedDocumentTest(answer) {
deepEqual(answer, {
"id": shared.created_document_id,
"method": "remove",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Remove first document.");
}
function removeSpecificDocument() {
return jio.remove({"_id": "b"});
}
function removeSpecificDocumentTest(answer) {
deepEqual(answer, {
"id": "b",
"method": "remove",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Remove second document.");
}
function listEmptyStorage() {
return jio.allDocs();
}
function listEmptyStorageTest(answer) {
deepEqual(answer, {
"data": {
"total_rows": 0,
"rows": []
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "List empty storage");
}
function putNewDocument() {
return jio.put({"_id": "a", "title": "Hey"});
}
function putNewDocumentTest(answer) {
deepEqual(answer, {
"id": "a",
"method": "put",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Put new document");
}
function getCreatedDocument2() {
return jio.get({"_id": "a"});
}
function getCreatedDocument2Test(answer) {
deepEqual(answer, {
"data": {
"_id": "a",
"title": "Hey"
},
"id": "a",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get new document");
}
function postSameDocument() {
return success(jio.post({"_id": "a", "title": "Hoo"}));
}
function postSameDocumentTest(answer) {
deepEqual(answer, {
"error": "conflict",
"id": "a",
"message": "Command failed",
"method": "post",
"reason": "Document exists",
"result": "error",
"status": 409,
"statusText": "Conflict"
}, "Unable to post the same document (conflict)");
}
function putAttachmentToNonExistentDocument() {
return success(jio.putAttachment({
"_id": "ahaha",
"_attachment": "aa",
"_data": "aaa",
"_content_type": "text/plain"
}));
}
function putAttachmentToNonExistentDocumentTest(answer) {
deepEqual(answer, {
"attachment": "aa",
"error": "not_found",
"id": "ahaha",
"message": "indexeddbStorage unable to put attachment",
"method": "putAttachment",
"reason": "Not Found",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Put attachment to a non existent document -> 404 Not Found");
}
function createAttachment() {
return jio.putAttachment({
"_id": "a",
"_attachment": "aa",
"_data": "aaa",
"_content_type": "text/plain"
});
}
function createAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "aa",
"id": "a",
"method": "putAttachment",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Create new attachment");
}
function updateAttachment() {
return jio.putAttachment({
"_id": "a",
"_attachment": "aa",
"_data": "aab",
"_content_type": "text/plain"
});
}
function updateAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "aa",
"id": "a",
"method": "putAttachment",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Update last attachment");
}
function createAnotherAttachment() {
return jio.putAttachment({
"_id": "a",
"_attachment": "ab",
"_data": "aba",
"_content_type": "text/plain"
});
}
function createAnotherAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "ab",
"id": "a",
"method": "putAttachment",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Create another attachment");
}
function updateLastDocument() {
return jio.put({"_id": "a", "title": "Hoo"});
}
function updateLastDocumentTest(answer) {
deepEqual(answer, {
"id": "a",
"method": "put",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Update document metadata");
}
function getFirstAttachment() {
return jio.getAttachment({"_id": "a", "_attachment": "aa"});
}
function getFirstAttachmentTest(answer) {
var blob = answer.data;
answer.data = "<blob>";
return jIO.util.readBlobAsText(blob).then(function (e) {
deepEqual(blob.type, "text/plain", "Check blob type");
deepEqual(e.target.result, "aab", "Check blob text content");
deepEqual(answer, {
"attachment": "aa",
"data": "<blob>",
"id": "a",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get first attachment");
}, function (err) {
deepEqual(err, "no error", "Check blob text content");
}); });
} }
});
function getFirstAttachmentRange1() { /////////////////////////////////////////////////////////////////
return jio.getAttachment({"_id": "a", // indexeddbStorage.get
"_attachment": "aa", /////////////////////////////////////////////////////////////////
"_start": 0}); test("get inexistent document", function () {
} stop();
expect(3);
function getFirstAttachmentRangeTest1(answer) {
var blob = answer.data; this.jio.get({"_id": "inexistent"})
answer.data = "<blob>"; .fail(function (error) {
return jIO.util.readBlobAsText(blob).then(function (e) { ok(error instanceof jIO.util.jIOError);
deepEqual(blob.type, "text/plain", "Check blob type"); equal(error.message, "Cannot find document");
deepEqual(e.target.result, "aab", "Check blob text content"); equal(error.status_code, 404);
deepEqual(answer, { })
"attachment": "aa", .fail(function (error) {
"data": "<blob>", ok(false, error);
"id": "a", })
"method": "getAttachment", .always(function () {
"result": "success", start();
"status": 200,
"statusText": "Ok"
}, "Get first attachment with range :_start:0, _end:undefined");
}, function (err) {
deepEqual(err, "no error", "Check blob text content");
});
}
function getFirstAttachmentRange2() {
return jio.getAttachment({"_id": "a",
"_attachment": "aa",
"_start": 0,
"_end": 1});
}
function getFirstAttachmentRangeTest2(answer) {
var blob = answer.data;
answer.data = "<blob>";
return jIO.util.readBlobAsText(blob).then(function (e) {
deepEqual(blob.type, "text/plain", "Check blob type");
deepEqual(e.target.result, "a", "Check blob text content");
deepEqual(answer, {
"attachment": "aa",
"data": "<blob>",
"id": "a",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get first attachment with range :_start:0, _end:1");
}, function (err) {
deepEqual(err, "no error", "Check blob text content");
});
}
function getFirstAttachmentRange3() {
return jio.getAttachment({"_id": "a",
"_attachment": "aa",
"_start": 1,
"_end": 3});
}
function getFirstAttachmentRangeTest3(answer) {
var blob = answer.data;
answer.data = "<blob>";
return jIO.util.readBlobAsText(blob).then(function (e) {
deepEqual(blob.type, "text/plain", "Check blob type");
deepEqual(e.target.result, "ab", "Check blob text content");
deepEqual(answer, {
"attachment": "aa",
"data": "<blob>",
"id": "a",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get first attachment with range :_start:1, _end:3");
}, function (err) {
deepEqual(err, "no error", "Check blob text content");
});
}
function getSecondAttachment() {
return jio.getAttachment({"_id": "a", "_attachment": "ab"});
}
function getSecondAttachmentTest(answer) {
var blob = answer.data;
answer.data = "<blob>";
return jIO.util.readBlobAsText(blob).then(function (e) {
deepEqual(blob.type, "text/plain", "Check blob type");
deepEqual(e.target.result, "aba", "Check blob text content");
deepEqual(answer, {
"attachment": "ab",
"data": "<blob>",
"id": "a",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get second attachment");
}, function (err) {
deepEqual(err, "no error", "Check blob text content");
});
}
function getSecondAttachmentRange1() {
return success(jio.getAttachment({"_id": "a",
"_attachment": "ab",
"_start": -1}));
}
function getSecondAttachmentRangeTest1(answer) {
deepEqual(answer, {
"attachment": "ab",
"error": "not_found",
"id": "a",
"message": "_start and _end must be positive",
"method": "getAttachment",
"reason": "invalide _start, _end",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "get attachment with _start or _end negative -> 404 Not Found");
}
function getSecondAttachmentRange2() {
return success(jio.getAttachment({"_id": "a",
"_attachment": "ab",
"_start": 1,
"_end": 0}));
}
function getSecondAttachmentRangeTest2(answer) {
deepEqual(answer, {
"attachment": "ab",
"error": "not_found",
"id": "a",
"message": "start is great then end",
"method": "getAttachment",
"reason": "invalide offset",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "get attachment with _start > _end -> 404 Not Found");
}
function getSecondAttachmentRange3() {
return jio.getAttachment({"_id": "a",
"_attachment": "ab",
"_start": 1,
"_end": 2});
}
function getSecondAttachmentRangeTest3(answer) {
var blob = answer.data;
answer.data = "<blob>";
return jIO.util.readBlobAsText(blob).then(function (e) {
deepEqual(blob.type, "text/plain", "Check blob type");
deepEqual(e.target.result, "b", "Check blob text content");
deepEqual(answer, {
"attachment": "ab",
"data": "<blob>",
"id": "a",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get second attachment with range :_start:1, _end:3");
}, function (err) {
deepEqual(err, "no error", "Check blob text content");
}); });
} });
function getLastDocument() {
return jio.get({"_id": "a"});
}
function getLastDocumentTest(answer) {
deepEqual(answer, {
"data": {
"_id": "a",
"title": "Hoo",
"_attachment": {
"aa": {
"content_type": "text/plain",
"length": 3
},
"ab": {
"content_type": "text/plain",
"length": 3
}
}
},
"id": "a",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get last document metadata");
}
function removeSecondAttachment() {
return jio.removeAttachment({"_id": "a", "_attachment": "ab"});
}
function removeSecondAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "ab",
"id": "a",
"method": "removeAttachment",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Remove second document");
}
function getInexistentSecondAttachment() {
return success(jio.getAttachment({"_id": "a", "_attachment": "ab"}));
}
function getInexistentSecondAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "ab",
"error": "not_found",
"id": "a",
"message": "IndexeddbStorage, unable to get attachment.",
"method": "getAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent second attachment");
}
function getOneAttachmentDocument() {
return jio.get({"_id": "a"});
}
function getOneAttachmentDocumentTest(answer) {
deepEqual(answer, {
"data": {
"_attachment": {
"aa": {
"content_type": "text/plain",
"length": 3
}
},
"_id": "a",
"title": "Hoo"
},
"id": "a",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get document metadata");
}
function removeSecondAttachmentAgain() {
return success(jio.removeAttachment({"_id": "a", "_attachment": "ab"}));
}
function removeSecondAttachmentAgainTest(answer) {
deepEqual(answer, {
"attachment": "ab",
"error": "not_found",
"id": "a",
"message": "IndexeddbStorage, document attachment not found.",
"method": "removeAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Remove inexistent attachment");
}
function removeDocument() {
return jio.remove({"_id": "a"});
}
function removeDocumentTest(answer) {
deepEqual(answer, {
"id": "a",
"method": "remove",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Remove document and its attachments");
}
function getInexistentFirstAttachment() {
return success(jio.getAttachment({"_id": "a", "_attachment": "aa"}));
}
function getInexistentFirstAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "aa",
"error": "not_found",
"id": "a",
"message": "IndexeddbStorage, unable to get attachment.",
"method": "getAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent first attachment");
}
function getInexistentDocument() {
return success(jio.get({"_id": "a"}));
}
function getInexistentDocumentTest(answer) {
deepEqual(answer, {
"error": "not_found",
"id": "a",
"message": "IndexeddbStorage, unable to get document.",
"method": "get",
"reason": "Not Found",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent document");
}
function removeInexistentDocument() {
return success(jio.remove({"_id": "a"}));
}
function removeInexistentDocumentTest(answer) {
deepEqual(answer, {
"error": "not_found",
"id": "a",
"message": "IndexeddbStorage, unable to get metadata.",
"method": "remove",
"reason": "Not Found",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Remove already removed document");
}
function unexpectedError(error) { test("get document", function () {
if (error instanceof Error) { var id = "post1",
deepEqual([ // myAPI,
error.name + ": " + error.message, indexedDB,
error mock;
], "UNEXPECTED ERROR", "Unexpected error"); // localStorage[id] = JSON.stringify({
} else { // title: "myPost1"
deepEqual(error, "UNEXPECTED ERROR", "Unexpected error"); // });
indexedDB = {
open: function () {
var result = {
result: "taboulet"
};
RSVP.delay().then(function () {
result.onsuccess();
});
return result;
} }
} // return RSVP.success("taboulet");
};
mock = sinon.mock(indexedDB);
// mock = sinon.mock(indexedDB.open, "open", function () {
// var result = {
// result: "taboulet"
// };
// RSVP.delay().then(function () {
// result.onsuccess();
// });
// return result;
// // return RSVP.success("taboulet");
// });
mock.expects("open").once().withArgs("couscous");
// # Post new documents, list them and remove them stop();
// post a 201 expect(1);
postNewDocument().then(postNewDocumentTest).
// get 200 this.jio.get({"_id": id})
then(getCreatedDocument).then(getCreatedDocumentTest). .then(function (result) {
// post b 201 deepEqual(result, {
then(postSpecificDocument).then(postSpecificDocumentTest). "title": "myPost1"
// allD 200 2 documents }, "Check document");
then(listDocument).then(list2DocumentsTest). mock.verify();
// allD+include_docs 200 2 documents })
then(listDocumentsWithMetadata).then(list2DocumentsWithMetadataTest). .fail(function (error) {
// remove a 204 ok(false, error);
then(removeCreatedDocument).then(removeCreatedDocumentTest). })
// remove b 204 .always(function () {
then(removeSpecificDocument).then(removeSpecificDocumentTest). start();
// allD 200 empty storage mock.restore();
then(listEmptyStorage).then(listEmptyStorageTest).
// # Create and update documents, and some attachment and remove them
// put 201
then(putNewDocument).then(putNewDocumentTest).
// get 200
then(getCreatedDocument2).then(getCreatedDocument2Test).
// post 409
then(postSameDocument).then(postSameDocumentTest).
// putA 404
then(putAttachmentToNonExistentDocument).
then(putAttachmentToNonExistentDocumentTest).
// putA a 204
then(createAttachment).then(createAttachmentTest).
// putA a 204
then(updateAttachment).then(updateAttachmentTest).
// putA b 204
then(createAnotherAttachment).then(createAnotherAttachmentTest).
// put 204
then(updateLastDocument).then(updateLastDocumentTest).
// getA a 200
then(getFirstAttachment).then(getFirstAttachmentTest).
then(getFirstAttachmentRange1).then(getFirstAttachmentRangeTest1).
then(getFirstAttachmentRange2).then(getFirstAttachmentRangeTest2).
then(getFirstAttachmentRange3).then(getFirstAttachmentRangeTest3).
// getA b 200
then(getSecondAttachment).then(getSecondAttachmentTest).
then(getSecondAttachmentRange1).then(getSecondAttachmentRangeTest1).
then(getSecondAttachmentRange2).then(getSecondAttachmentRangeTest2).
then(getSecondAttachmentRange3).then(getSecondAttachmentRangeTest3).
// get 200
then(getLastDocument).then(getLastDocumentTest).
// removeA b 204
then(removeSecondAttachment).then(removeSecondAttachmentTest).
// getA b 404
then(getInexistentSecondAttachment).
then(getInexistentSecondAttachmentTest).
// get 200
then(getOneAttachmentDocument).then(getOneAttachmentDocumentTest).
// removeA b 404
then(removeSecondAttachmentAgain).then(removeSecondAttachmentAgainTest).
// remove 204
then(removeDocument).then(removeDocumentTest).
// getA a 404
then(getInexistentFirstAttachment).then(getInexistentFirstAttachmentTest).
// get 404
then(getInexistentDocument).then(getInexistentDocumentTest).
// remove 404
then(removeInexistentDocument).then(removeInexistentDocumentTest).
// end
fail(unexpectedError).
always(start).
always(function () {
server.restore();
}); });
}); });
}));
}(jIO, QUnit));
// /*jslint indent: 2, maxlen: 80, nomen: true */
// /*global module, test, stop, start, expect, ok, deepEqual, location, sinon,
// davstorage_spec, RSVP, jIO, test_util, dav_storage, btoa, define,
// setTimeout, clearTimeout, indexedDB */
//
// // define([module_name], [dependencies], module);
// (function (dependencies, module) {
// "use strict";
// if (typeof define === 'function' && define.amd) {
// return define(dependencies, module);
// }
// module(test_util, RSVP, jIO);
// }([
// 'test_util',
// 'rsvp',
// 'jio',
// 'indexeddbstorage',
// 'qunit'
// ], function (util, RSVP, jIO) {
// "use strict";
// module("indexeddbStorage");
// function success(promise) {
// return new RSVP.Promise(function (resolve, notify) {
// promise.then(resolve, resolve, notify);
// }, function () {
// promise.cancel();
// });
// }
//
// test("Scenario", 46, function () {
// indexedDB.deleteDatabase("jio:test");
// var server, shared = {}, jio = jIO.createJIO(
// {"type" : "indexeddb",
// "database" : "test"
// },
// {"workspace": {}}
// );
// stop();
// server = {restore: function () {
// return;
// }};
//
// function postNewDocument() {
// return jio.post({"title": "Unique ID"});
// }
//
// function postNewDocumentTest(answer) {
// var uuid = answer.id;
// answer.id = "<uuid>";
// deepEqual(answer, {
// "id": "<uuid>",
// "method": "post",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// }, "Post a new document");
// ok(util.isUuid(uuid), "New document id should look like " +
// "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
// shared.created_document_id = uuid;
// }
//
// function getCreatedDocument() {
// return jio.get({"_id": shared.created_document_id});
// }
//
// function getCreatedDocumentTest(answer) {
// deepEqual(answer, {
// "data": {
// "_id": shared.created_document_id,
// "title": "Unique ID"
// },
// "id": shared.created_document_id,
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get new document");
// }
//
// function postSpecificDocument() {
// return jio.post({"_id": "b", "title": "Bee"});
// }
//
// function postSpecificDocumentTest(answer) {
// deepEqual(answer, {
// "id": "b",
// "method": "post",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// }, "Post specific document");
// }
//
// function listDocument() {
// return jio.allDocs();
// }
//
// function list2DocumentsTest(answer) {
// if (answer && answer.data && Array.isArray(answer.data.rows)) {
// answer.data.rows.sort(function (a) {
// return a.id === "b" ? 1 : 0;
// });
// }
// deepEqual(answer, {
// "data": {
// "total_rows": 2,
// "rows": [{
// "id": shared.created_document_id,
// "value": {}
// }, {
// "id": "b",
// "value": {}
// }]
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "List 2 documents");
// }
//
// function listDocumentsWithMetadata() {
// return jio.allDocs({"include_docs": true});
// }
//
// function list2DocumentsWithMetadataTest(answer) {
// if (answer && answer.data && Array.isArray(answer.data.rows)) {
// answer.data.rows.sort(function (a) {
// return a.id === "b" ? 1 : 0;
// });
// }
// deepEqual(answer, {
// "data": {
// "total_rows": 2,
// "rows": [{
// "id": shared.created_document_id,
// "value": {},
// "doc": {
// "_id": shared.created_document_id,
// "title": "Unique ID"
// }
// }, {
// "id": "b",
// "value": {},
// "doc": {
// "_id": "b",
// "title": "Bee"
// }
// }]
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "List 2 documents with their metadata");
// }
//
// function removeCreatedDocument() {
// return jio.remove({"_id": shared.created_document_id});
// }
//
// function removeCreatedDocumentTest(answer) {
// deepEqual(answer, {
// "id": shared.created_document_id,
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove first document.");
// }
//
// function removeSpecificDocument() {
// return jio.remove({"_id": "b"});
// }
//
// function removeSpecificDocumentTest(answer) {
// deepEqual(answer, {
// "id": "b",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove second document.");
// }
//
// function listEmptyStorage() {
// return jio.allDocs();
// }
//
// function listEmptyStorageTest(answer) {
// deepEqual(answer, {
// "data": {
// "total_rows": 0,
// "rows": []
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "List empty storage");
// }
//
// function putNewDocument() {
// return jio.put({"_id": "a", "title": "Hey"});
// }
//
// function putNewDocumentTest(answer) {
// deepEqual(answer, {
// "id": "a",
// "method": "put",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// }, "Put new document");
// }
//
// function getCreatedDocument2() {
// return jio.get({"_id": "a"});
// }
//
// function getCreatedDocument2Test(answer) {
// deepEqual(answer, {
// "data": {
// "_id": "a",
// "title": "Hey"
// },
// "id": "a",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get new document");
// }
//
// function postSameDocument() {
// return success(jio.post({"_id": "a", "title": "Hoo"}));
// }
//
// function postSameDocumentTest(answer) {
// deepEqual(answer, {
// "error": "conflict",
// "id": "a",
// "message": "Command failed",
// "method": "post",
// "reason": "Document exists",
// "result": "error",
// "status": 409,
// "statusText": "Conflict"
// }, "Unable to post the same document (conflict)");
// }
//
// function putAttachmentToNonExistentDocument() {
// return success(jio.putAttachment({
// "_id": "ahaha",
// "_attachment": "aa",
// "_data": "aaa",
// "_content_type": "text/plain"
// }));
// }
//
// function putAttachmentToNonExistentDocumentTest(answer) {
// deepEqual(answer, {
// "attachment": "aa",
// "error": "not_found",
// "id": "ahaha",
// "message": "indexeddbStorage unable to put attachment",
// "method": "putAttachment",
// "reason": "Not Found",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Put attachment to a non existent document -> 404 Not Found");
// }
//
// function createAttachment() {
// return jio.putAttachment({
// "_id": "a",
// "_attachment": "aa",
// "_data": "aaa",
// "_content_type": "text/plain"
// });
// }
//
// function createAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "aa",
// "id": "a",
// "method": "putAttachment",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Create new attachment");
// }
//
// function updateAttachment() {
// return jio.putAttachment({
// "_id": "a",
// "_attachment": "aa",
// "_data": "aab",
// "_content_type": "text/plain"
// });
// }
//
// function updateAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "aa",
// "id": "a",
// "method": "putAttachment",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Update last attachment");
// }
//
// function createAnotherAttachment() {
// return jio.putAttachment({
// "_id": "a",
// "_attachment": "ab",
// "_data": "aba",
// "_content_type": "text/plain"
// });
// }
//
// function createAnotherAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "ab",
// "id": "a",
// "method": "putAttachment",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Create another attachment");
// }
//
//
// function updateLastDocument() {
// return jio.put({"_id": "a", "title": "Hoo"});
// }
//
// function updateLastDocumentTest(answer) {
// deepEqual(answer, {
// "id": "a",
// "method": "put",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Update document metadata");
// }
//
// function getFirstAttachment() {
// return jio.getAttachment({"_id": "a", "_attachment": "aa"});
// }
//
// function getFirstAttachmentTest(answer) {
// var blob = answer.data;
// answer.data = "<blob>";
// return jIO.util.readBlobAsText(blob).then(function (e) {
// deepEqual(blob.type, "text/plain", "Check blob type");
// deepEqual(e.target.result, "aab", "Check blob text content");
// deepEqual(answer, {
// "attachment": "aa",
// "data": "<blob>",
// "id": "a",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get first attachment");
// }, function (err) {
// deepEqual(err, "no error", "Check blob text content");
// });
// }
//
// function getFirstAttachmentRange1() {
// return jio.getAttachment({"_id": "a",
// "_attachment": "aa",
// "_start": 0});
// }
//
// function getFirstAttachmentRangeTest1(answer) {
// var blob = answer.data;
// answer.data = "<blob>";
// return jIO.util.readBlobAsText(blob).then(function (e) {
// deepEqual(blob.type, "text/plain", "Check blob type");
// deepEqual(e.target.result, "aab", "Check blob text content");
// deepEqual(answer, {
// "attachment": "aa",
// "data": "<blob>",
// "id": "a",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get first attachment with range :_start:0, _end:undefined");
// }, function (err) {
// deepEqual(err, "no error", "Check blob text content");
// });
// }
//
//
// function getFirstAttachmentRange2() {
// return jio.getAttachment({"_id": "a",
// "_attachment": "aa",
// "_start": 0,
// "_end": 1});
// }
//
// function getFirstAttachmentRangeTest2(answer) {
// var blob = answer.data;
// answer.data = "<blob>";
// return jIO.util.readBlobAsText(blob).then(function (e) {
// deepEqual(blob.type, "text/plain", "Check blob type");
// deepEqual(e.target.result, "a", "Check blob text content");
// deepEqual(answer, {
// "attachment": "aa",
// "data": "<blob>",
// "id": "a",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get first attachment with range :_start:0, _end:1");
// }, function (err) {
// deepEqual(err, "no error", "Check blob text content");
// });
// }
//
// function getFirstAttachmentRange3() {
// return jio.getAttachment({"_id": "a",
// "_attachment": "aa",
// "_start": 1,
// "_end": 3});
// }
// function getFirstAttachmentRangeTest3(answer) {
// var blob = answer.data;
// answer.data = "<blob>";
// return jIO.util.readBlobAsText(blob).then(function (e) {
// deepEqual(blob.type, "text/plain", "Check blob type");
// deepEqual(e.target.result, "ab", "Check blob text content");
// deepEqual(answer, {
// "attachment": "aa",
// "data": "<blob>",
// "id": "a",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get first attachment with range :_start:1, _end:3");
// }, function (err) {
// deepEqual(err, "no error", "Check blob text content");
// });
// }
//
//
// function getSecondAttachment() {
// return jio.getAttachment({"_id": "a", "_attachment": "ab"});
// }
//
// function getSecondAttachmentTest(answer) {
// var blob = answer.data;
// answer.data = "<blob>";
// return jIO.util.readBlobAsText(blob).then(function (e) {
// deepEqual(blob.type, "text/plain", "Check blob type");
// deepEqual(e.target.result, "aba", "Check blob text content");
// deepEqual(answer, {
// "attachment": "ab",
// "data": "<blob>",
// "id": "a",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get second attachment");
// }, function (err) {
// deepEqual(err, "no error", "Check blob text content");
// });
// }
//
//
//
//
//
// function getSecondAttachmentRange1() {
// return success(jio.getAttachment({"_id": "a",
// "_attachment": "ab",
// "_start": -1}));
// }
// function getSecondAttachmentRangeTest1(answer) {
// deepEqual(answer, {
// "attachment": "ab",
// "error": "not_found",
// "id": "a",
// "message": "_start and _end must be positive",
// "method": "getAttachment",
// "reason": "invalide _start, _end",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "get attachment with _start or _end negative -> 404 Not Found");
// }
//
//
//
// function getSecondAttachmentRange2() {
// return success(jio.getAttachment({"_id": "a",
// "_attachment": "ab",
// "_start": 1,
// "_end": 0}));
// }
// function getSecondAttachmentRangeTest2(answer) {
// deepEqual(answer, {
// "attachment": "ab",
// "error": "not_found",
// "id": "a",
// "message": "start is great then end",
// "method": "getAttachment",
// "reason": "invalide offset",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "get attachment with _start > _end -> 404 Not Found");
// }
// function getSecondAttachmentRange3() {
// return jio.getAttachment({"_id": "a",
// "_attachment": "ab",
// "_start": 1,
// "_end": 2});
// }
// function getSecondAttachmentRangeTest3(answer) {
// var blob = answer.data;
// answer.data = "<blob>";
// return jIO.util.readBlobAsText(blob).then(function (e) {
// deepEqual(blob.type, "text/plain", "Check blob type");
// deepEqual(e.target.result, "b", "Check blob text content");
// deepEqual(answer, {
// "attachment": "ab",
// "data": "<blob>",
// "id": "a",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get second attachment with range :_start:1, _end:3");
// }, function (err) {
// deepEqual(err, "no error", "Check blob text content");
// });
// }
//
//
//
// function getLastDocument() {
// return jio.get({"_id": "a"});
// }
//
// function getLastDocumentTest(answer) {
// deepEqual(answer, {
// "data": {
// "_id": "a",
// "title": "Hoo",
// "_attachment": {
// "aa": {
// "content_type": "text/plain",
// "length": 3
// },
// "ab": {
// "content_type": "text/plain",
// "length": 3
// }
// }
// },
// "id": "a",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get last document metadata");
// }
//
// function removeSecondAttachment() {
// return jio.removeAttachment({"_id": "a", "_attachment": "ab"});
// }
//
// function removeSecondAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "ab",
// "id": "a",
// "method": "removeAttachment",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove second document");
// }
//
// function getInexistentSecondAttachment() {
// return success(jio.getAttachment({"_id": "a", "_attachment": "ab"}));
// }
//
// function getInexistentSecondAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "ab",
// "error": "not_found",
// "id": "a",
// "message": "IndexeddbStorage, unable to get attachment.",
// "method": "getAttachment",
// "reason": "missing attachment",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Get inexistent second attachment");
// }
//
// function getOneAttachmentDocument() {
// return jio.get({"_id": "a"});
// }
//
// function getOneAttachmentDocumentTest(answer) {
// deepEqual(answer, {
// "data": {
// "_attachment": {
// "aa": {
// "content_type": "text/plain",
// "length": 3
// }
// },
// "_id": "a",
// "title": "Hoo"
// },
// "id": "a",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get document metadata");
// }
//
// function removeSecondAttachmentAgain() {
// return success(jio.removeAttachment({"_id": "a", "_attachment": "ab"}));
// }
//
// function removeSecondAttachmentAgainTest(answer) {
// deepEqual(answer, {
// "attachment": "ab",
// "error": "not_found",
// "id": "a",
// "message": "IndexeddbStorage, document attachment not found.",
// "method": "removeAttachment",
// "reason": "missing attachment",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Remove inexistent attachment");
// }
//
// function removeDocument() {
// return jio.remove({"_id": "a"});
// }
//
// function removeDocumentTest(answer) {
// deepEqual(answer, {
// "id": "a",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove document and its attachments");
// }
//
// function getInexistentFirstAttachment() {
// return success(jio.getAttachment({"_id": "a", "_attachment": "aa"}));
// }
//
// function getInexistentFirstAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "aa",
// "error": "not_found",
// "id": "a",
// "message": "IndexeddbStorage, unable to get attachment.",
// "method": "getAttachment",
// "reason": "missing attachment",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Get inexistent first attachment");
// }
//
// function getInexistentDocument() {
// return success(jio.get({"_id": "a"}));
// }
//
// function getInexistentDocumentTest(answer) {
// deepEqual(answer, {
// "error": "not_found",
// "id": "a",
// "message": "IndexeddbStorage, unable to get document.",
// "method": "get",
// "reason": "Not Found",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Get inexistent document");
// }
//
// function removeInexistentDocument() {
// return success(jio.remove({"_id": "a"}));
// }
//
// function removeInexistentDocumentTest(answer) {
// deepEqual(answer, {
// "error": "not_found",
// "id": "a",
// "message": "IndexeddbStorage, unable to get metadata.",
// "method": "remove",
// "reason": "Not Found",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Remove already removed document");
// }
//
// function unexpectedError(error) {
// if (error instanceof Error) {
// deepEqual([
// error.name + ": " + error.message,
// error
// ], "UNEXPECTED ERROR", "Unexpected error");
// } else {
// deepEqual(error, "UNEXPECTED ERROR", "Unexpected error");
// }
// }
//
// // # Post new documents, list them and remove them
// // post a 201
// postNewDocument().then(postNewDocumentTest).
// // get 200
// then(getCreatedDocument).then(getCreatedDocumentTest).
// // post b 201
// then(postSpecificDocument).then(postSpecificDocumentTest).
// // allD 200 2 documents
// then(listDocument).then(list2DocumentsTest).
// // allD+include_docs 200 2 documents
// then(listDocumentsWithMetadata).then(list2DocumentsWithMetadataTest).
// // remove a 204
// then(removeCreatedDocument).then(removeCreatedDocumentTest).
// // remove b 204
// then(removeSpecificDocument).then(removeSpecificDocumentTest).
// // allD 200 empty storage
// then(listEmptyStorage).then(listEmptyStorageTest).
// // # Create and update documents, and some attachment and remove them
// // put 201
// then(putNewDocument).then(putNewDocumentTest).
// // get 200
// then(getCreatedDocument2).then(getCreatedDocument2Test).
// // post 409
// then(postSameDocument).then(postSameDocumentTest).
// // putA 404
// then(putAttachmentToNonExistentDocument).
// then(putAttachmentToNonExistentDocumentTest).
// // putA a 204
// then(createAttachment).then(createAttachmentTest).
// // putA a 204
// then(updateAttachment).then(updateAttachmentTest).
// // putA b 204
// then(createAnotherAttachment).then(createAnotherAttachmentTest).
// // put 204
// then(updateLastDocument).then(updateLastDocumentTest).
// // getA a 200
// then(getFirstAttachment).then(getFirstAttachmentTest).
// then(getFirstAttachmentRange1).then(getFirstAttachmentRangeTest1).
// then(getFirstAttachmentRange2).then(getFirstAttachmentRangeTest2).
// then(getFirstAttachmentRange3).then(getFirstAttachmentRangeTest3).
// // getA b 200
// then(getSecondAttachment).then(getSecondAttachmentTest).
// then(getSecondAttachmentRange1).then(getSecondAttachmentRangeTest1).
// then(getSecondAttachmentRange2).then(getSecondAttachmentRangeTest2).
// then(getSecondAttachmentRange3).then(getSecondAttachmentRangeTest3).
// // get 200
// then(getLastDocument).then(getLastDocumentTest).
// // removeA b 204
// then(removeSecondAttachment).then(removeSecondAttachmentTest).
// // getA b 404
// then(getInexistentSecondAttachment).
// then(getInexistentSecondAttachmentTest).
// // get 200
// then(getOneAttachmentDocument).then(getOneAttachmentDocumentTest).
// // removeA b 404
// then(removeSecondAttachmentAgain).then(removeSecondAttachmentAgainTest).
// // remove 204
// then(removeDocument).then(removeDocumentTest).
// // getA a 404
// then(getInexistentFirstAttachment).then(getInexistentFirstAttachmentTest).
// // get 404
// then(getInexistentDocument).then(getInexistentDocumentTest).
// // remove 404
// then(removeInexistentDocument).then(removeInexistentDocumentTest).
// // end
// fail(unexpectedError).
// always(start).
// always(function () {
// server.restore();
// });
// });
// }));
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint maxlen: 120, nomen: true */
/*global window, define, module, test_util, RSVP, jIO, local_storage, test, ok, /*global localStorage, test_util, console, Blob*/
deepEqual, sinon, expect, stop, start, Blob */ (function (jIO, localStorage, QUnit) {
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(test_util, RSVP, jIO, local_storage);
}([
'test_util',
'rsvp',
'jio',
'localstorage',
'qunit'
], function (util, RSVP, jIO, local_storage) {
"use strict"; "use strict";
var test = QUnit.test,
module("LocalStorage"); stop = QUnit.stop,
start = QUnit.start,
local_storage.clear(); ok = QUnit.ok,
expect = QUnit.expect,
/** deepEqual = QUnit.deepEqual,
* all(promises): Promise equal = QUnit.equal,
* module = QUnit.module;
* Produces a promise that is resolved when all the given promises are
* fulfilled. The resolved value is an array of each of the answers of the module("localStorage", {
* given promises. setup: function () {
* localStorage.clear();
* @param {Array} promises The promises to use this.jio = jIO.createJIO({
* @return {Promise} A new promise "type": "local"
*/ });
function all(promises) {
var results = [], i, count = 0;
function cancel() {
var j;
for (j = 0; j < promises.length; j += 1) {
if (typeof promises[j].cancel === 'function') {
promises[j].cancel();
}
}
} }
return new RSVP.Promise(function (resolve, reject, notify) { });
/*jslint unparam: true */
function succeed(j) {
return function (answer) {
results[j] = answer;
count += 1;
if (count !== promises.length) {
return;
}
resolve(results);
};
}
function notified(j) {
return function (answer) {
notify({
"promise": promises[j],
"index": j,
"notified": answer
});
};
}
for (i = 0; i < promises.length; i += 1) {
promises[i].then(succeed(i), succeed(i), notified(i));
}
}, cancel);
}
test("Post & Get", function () {
expect(6);
var jio = jIO.createJIO({
"type": "local",
"username": "upost",
"application_name": "apost"
}, {
"workspace": {}
});
/////////////////////////////////////////////////////////////////
// localStorage.get
/////////////////////////////////////////////////////////////////
test("get inexistent document", function () {
stop(); stop();
expect(3);
all([ this.jio.get({"_id": "inexistent"})
.fail(function (error) {
// get inexistent document ok(error instanceof jIO.util.jIOError);
jio.get({"_id": "inexistent"}).always(function (answer) { equal(error.message, "Cannot find document");
equal(error.status_code, 404);
deepEqual(answer, {
"error": "not_found",
"id": "inexistent",
"message": "Cannot find document",
"method": "get",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent document");
}),
// post without id
jio.post({}).always(function (answer) {
var uuid = answer.id;
delete answer.id;
deepEqual(answer, {
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Post without id");
ok(util.isUuid(uuid), "Uuid should look like " +
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
}).then(function () {
// post non empty document
return jio.post({"_id": "post1", "title": "myPost1"});
}).always(function (answer) {
deepEqual(answer, {
"id": "post1",
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Post");
}).then(function () {
return jio.get({"_id": "post1"});
}).always(function (answer) {
deepEqual(answer, {
"data": {
"_id": "post1",
"title": "myPost1"
},
"id": "post1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get, Check document");
}).then(function () {
// post but document already exists
return jio.post({"_id": "post1", "title": "myPost2"});
}).always(function (answer) {
deepEqual(answer, {
"error": "conflict",
"id": "post1",
"message": "Cannot create a new document",
"method": "post",
"reason": "document exists",
"result": "error",
"status": 409,
"statusText": "Conflict"
}, "Post but document already exists");
}) })
.fail(function (error) {
]).always(start); console.error(error);
ok(false, error);
})
.always(function () {
start();
});
}); });
test("Put & Get", function () { test("get document", function () {
expect(4); var id = "post1";
var jio = jIO.createJIO({ localStorage[id] = JSON.stringify({
"type": "local", title: "myPost1"
"username": "uput",
"application_name": "aput"
}, {
"workspace": {}
}); });
stop(); stop();
expect(1);
// put non empty document this.jio.get({"_id": id})
jio.put({"_id": "put1", "title": "myPut1"}).always(function (answer) { .then(function (result) {
deepEqual(result, {
deepEqual(answer, { "title": "myPost1"
"id": "put1", }, "Check document");
"method": "put", })
"result": "success", .fail(function (error) {
"status": 201, ok(false, error);
"statusText": "Created" })
}, "Creates a document"); .always(function () {
start();
}).then(function () { });
});
return jio.get({"_id": "put1"});
}).always(function (answer) {
deepEqual(answer, {
"data": {
"_id": "put1",
"title": "myPut1"
},
"id": "put1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get, Check document");
}).then(function () {
// put but document already exists
return jio.put({"_id": "put1", "title": "myPut2"});
}).always(function (answer) {
deepEqual(answer, {
"id": "put1",
"method": "put",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Update the document");
}).then(function () { test("get document with attachment", function () {
var id = "putattmt1";
stop();
expect(1);
localStorage[id] = JSON.stringify({
"_id": id,
"_attachments": {
"putattmt2": {
content_type: "",
digest: "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
length: 0
}
}
});
return jio.get({"_id": "put1"}); this.jio.get({"_id": id})
.then(function (result) {
deepEqual(result, {
"_id": id,
"_attachments": {
"putattmt2": {
content_type: "",
digest: "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
length: 0
}
}
}, "Check document");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}).always(function (answer) { /////////////////////////////////////////////////////////////////
// localStorage.post
/////////////////////////////////////////////////////////////////
test("post without id", function () {
expect(1);
stop();
deepEqual(answer, { this.jio.post({})
"data": { .then(function (uuid) {
"_id": "put1", // ok(util.isUuid(uuid), "Uuid should look like " +
"title": "myPut2" // "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
}, equal(localStorage[uuid], JSON.stringify({
"id": "put1", "_id": uuid
"method": "get", }));
"result": "success", })
"status": 200, .fail(function (error) {
"statusText": "Ok" ok(false, error);
}, "Get, Check document"); })
.always(function () {
start();
});
});
}).always(start); test("post non empty document", function () {
expect(2);
stop();
this.jio.post({"_id": "post1", "title": "myPost1"})
.then(function (uuid) {
equal(uuid, "post1");
equal(localStorage.post1, JSON.stringify({
"_id": "post1",
"title": "myPost1"
}));
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
}); });
test("PutAttachment & Get & GetAttachment", function () { test("post but document already exists", function () {
expect(9); var id = "post1";
var jio = jIO.createJIO({ localStorage[id] = JSON.stringify({
"type": "local", "_id": id,
"username": "uputattmt", title: "myPost1"
"application_name": "aputattmt"
}, {
"workspace": {}
}); });
expect(4);
stop(); stop();
all([ this.jio.post({"_id": "post1", "title": "myPost2"})
.then(function (result) {
// get an attachment from an inexistent document ok(false, result);
jio.getAttachment({
"_id": "inexistent",
"_attachment": "a"
}).always(function (answer) {
deepEqual(answer, {
"attachment": "a",
"error": "not_found",
"id": "inexistent",
"message": "Cannot find document",
"method": "getAttachment",
"reason": "missing document",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "GetAttachment from inexistent document");
}),
// put a document then get an attachment from the empty document
jio.put({"_id": "b"}).then(function () {
return jio.getAttachment({"_id": "b", "_attachment": "inexistent"});
}).always(function (answer) {
deepEqual(answer, {
"attachment": "inexistent",
"error": "not_found",
"id": "b",
"message": "Cannot find attachment",
"method": "getAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent attachment");
}),
// put an attachment to an inexistent document
jio.putAttachment({
"_id": "inexistent",
"_attachment": "putattmt2",
"_data": ""
}).always(function (answer) {
deepEqual(answer, {
"attachment": "putattmt2",
"error": "not_found",
"id": "inexistent",
"message": "Impossible to add attachment",
"method": "putAttachment",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "PutAttachment to inexistent document");
}),
// add a document to the storage
// don't need to be tested
jio.put({"_id": "putattmt1", "title": "myPutAttmt1"}).then(function () {
return jio.putAttachment({
"_id": "putattmt1",
"_attachment": "putattmt2",
"_data": ""
});
}).always(function (answer) {
deepEqual(answer, {
"attachment": "putattmt2",
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855", // hex_sha256("")
"id": "putattmt1",
"method": "putAttachment",
"result": "success",
"status": 201,
"statusText": "Created"
}, "PutAttachment to a document, without data");
}).then(function () {
// check document and attachment
return all([
jio.get({"_id": "putattmt1"}),
jio.getAttachment({"_id": "putattmt1", "_attachment": "putattmt2"})
]);
// XXX check attachment with a getAttachment
}).always(function (answers) {
deepEqual(answers[0], {
"data": {
"_attachments": {
"putattmt2": {
"content_type": "",
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
"length": 0
}
},
"_id": "putattmt1",
"title": "myPutAttmt1"
},
"id": "putattmt1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get, Check document");
ok(answers[1].data instanceof Blob, "Data is Blob");
deepEqual(answers[1].data.type, "", "Check mimetype");
deepEqual(answers[1].data.size, 0, "Check size");
delete answers[1].data;
deepEqual(answers[1], {
"attachment": "putattmt2",
"id": "putattmt1",
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get Attachment, Check Response");
}) })
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot create a new document");
equal(error.status_code, 409);
equal(localStorage.post1, JSON.stringify({
"_id": "post1",
"title": "myPost1"
}));
})
.always(function () {
start();
});
});
]).always(start); /////////////////////////////////////////////////////////////////
// localStorage.put
/////////////////////////////////////////////////////////////////
test("put non empty document", function () {
expect(2);
stop();
this.jio.put({"_id": "put1", "title": "myPut1"})
.then(function (uuid) {
equal(uuid, "put1");
equal(localStorage.put1, JSON.stringify({
"_id": "put1",
"title": "myPut1"
}));
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
}); });
test("Remove & RemoveAttachment", function () { test("put when document already exists", function () {
expect(4); var id = "put1";
var jio = jIO.createJIO({ localStorage[id] = JSON.stringify({
"type": "local", "_id": id,
"username": "uremove", title: "myPut1"
"application_name": "aremove"
}, {
"workspace": {}
}); });
expect(2);
stop(); stop();
jio.put({"_id": "a"}).then(function () { this.jio.put({"_id": id, "title": "myPut2"})
.then(function (uuid) {
return jio.putAttachment({"_id": "a", "_attachment": "b", "_data": "c"}); equal(uuid, "put1");
equal(localStorage.put1, JSON.stringify({
}).then(function () { "_id": "put1",
"title": "myPut2"
return jio.removeAttachment({"_id": "a", "_attachment": "b"}); }));
})
}).always(function (answer) { .fail(function (error) {
ok(false, error);
deepEqual(answer, { })
"attachment": "b", .always(function () {
"id": "a", start();
"method": "removeAttachment", });
"result": "success", });
"status": 204,
"statusText": "No Content"
}, "Remove existent attachment");
}).then(function () {
// Promise.all always return success
return all([jio.removeAttachment({
"_id": "a",
"_attachment": "b"
})]);
}).always(function (answers) {
deepEqual(answers[0], {
"attachment": "b",
"error": "not_found",
"id": "a",
"message": "Attachment not found",
"method": "removeAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Remove removed attachment");
}).then(function () {
return jio.remove({"_id": "a"});
}).always(function (answer) {
deepEqual(answer, {
"id": "a",
"method": "remove",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Remove existent document");
}).then(function () {
return jio.remove({"_id": "a"});
}).always(function (answer) {
deepEqual(answer, {
"error": "not_found",
"id": "a",
"message": "Document not found",
"method": "remove",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Remove removed document");
}).always(start); /////////////////////////////////////////////////////////////////
// localStorage.getAttachment
/////////////////////////////////////////////////////////////////
test("get attachment from inexistent document", function () {
stop();
expect(3);
this.jio.getAttachment({
"_id": "inexistent",
"_attachment": "a"
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document");
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
}); });
test("AllDocs", function () { test("get inexistent attachment from document", function () {
var id = "b";
stop();
expect(3); expect(3);
var o = {}, jio = jIO.createJIO({
"type": "local", localStorage[id] = JSON.stringify({
"username": "ualldocs", "_id": id
"application_name": "aalldocs"
}, {
"workspace": {}
}); });
this.jio.getAttachment({
"_id": id,
"_attachment": "inexistent"
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find attachment");
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get attachment from document", function () {
var id = "putattmt1",
attachment = "putattmt2";
stop(); stop();
expect(4);
o.date_a = new Date(0); localStorage[id] = JSON.stringify({
o.date_b = new Date(); "_id": id,
"_attachments": {
// put some document before list them "putattmt2": {
all([ content_type: "",
jio.put({ digest: "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"_id": "a", "7ae41e4649b934ca495991b7852b855",
"title": "one", length: 0
"date": o.date_a }
}).then(function () {
return jio.putAttachment({
"_id": "a",
"_attachment": "aa",
"_data": "aaa"
});
}),
jio.put({"_id": "b", "title": "two", "date": o.date_a}),
jio.put({"_id": "c", "title": "one", "date": o.date_b}),
jio.put({"_id": "d", "title": "two", "date": o.date_b})
]).then(function () {
// get a list of documents
return jio.allDocs();
}).always(function (answer) {
// sort answer rows for comparison
if (answer.data && answer.data.rows) {
answer.data.rows.sort(function (a, b) {
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
});
} }
});
deepEqual(answer, { localStorage[id + '/' + attachment] = JSON.stringify("");
"data": {
"rows": [{ this.jio.getAttachment({
"id": "a", "_id": id,
"key": "a", "_attachment": attachment
"value": {} })
}, { .then(function (result) {
"id": "b", ok(result.data instanceof Blob, "Data is Blob");
"key": "b", deepEqual(result.data.type, "", "Check mimetype");
"value": {} deepEqual(result.data.size, 0, "Check size");
}, {
"id": "c", delete result.data;
"key": "c", deepEqual(result, {
"value": {} "digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
}, { "7ae41e4649b934ca495991b7852b855"
"id": "d", }, "Get Attachment, Check Response");
"key": "d", })
"value": {} .fail(function (error) {
}], ok(false, error);
"total_rows": 4 })
}, .always(function () {
"method": "allDocs", start();
"result": "success",
"status": 200,
"statusText": "Ok"
}, "AllDocs");
}).then(function () {
// get a list of documents
return jio.allDocs({
"include_docs": true,
"sort_on": [['title', 'ascending'], ['date', 'descending']],
"select_list": ['title', 'date'],
"limit": [1] // ==> equal [1, 3] in this case
}); });
});
}).always(function (answer) {
deepEqual(answer, {
"data": {
"rows": [{
"doc": {
"_attachments": {
"aa": {
"content_type": "",
"digest": "sha256-9834876dcfb05cb167a5c24953eba58c4" +
"ac89b1adf57f28f2f9d09af107ee8f0",
"length": 3
}
},
"_id": "a",
"date": o.date_a.toJSON(),
"title": "one"
},
"id": "a",
"key": "a",
"value": {
"date": o.date_a.toJSON(),
"title": "one"
}
}, {
"doc": {
"_id": "d",
"date": o.date_b.toJSON(),
"title": "two"
},
"id": "d",
"key": "d",
"value": {
"date": o.date_b.toJSON(),
"title": "two"
}
}, {
"doc": {
"_id": "b",
"date": o.date_a.toJSON(),
"title": "two"
},
"id": "b",
"key": "b",
"value": {
"date": o.date_a.toJSON(),
"title": "two"
}
}],
"total_rows": 3
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "AllDocs include docs + sort on + limit + select_list");
}).then(function () {
// use a query
return jio.allDocs({
"query": "title: \"two\"",
"sort_on": [["date", "descending"]]
});
}).always(function (answer) { /////////////////////////////////////////////////////////////////
// localStorage.putAttachment
deepEqual(answer, { /////////////////////////////////////////////////////////////////
"data": { test("put an attachment to an inexistent document", function () {
"rows": [{ stop();
"id": "d", expect(3);
"key": "d",
"value": {}
}, {
"id": "b",
"key": "b",
"value": {}
}],
"total_rows": 2
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "AllDocs sort on + query");
}).always(start);
this.jio.putAttachment({
"_id": "inexistent",
"_attachment": "putattmt2",
"_data": ""
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document");
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
}); });
test("Check & Repair", function () { test("put an attachment to a document", function () {
expect(18); var id = "putattmt1";
var o = {}, jio = jIO.createJIO({ localStorage[id] = JSON.stringify({
"type": "local", "_id": id,
"username": "urepair", "title": "myPutAttmt1"
"application_name": "arepair"
}, {
"workspace": {}
}); });
stop(); stop();
expect(3);
o.putCorruptedDocuments = function () { this.jio.putAttachment({
// put a document with a wrong attachment reference "_id": id,
util.json_local_storage.setItem( "_attachment": "putattmt2",
"jio/localstorage/urepair/arepair/war", "_data": ""
{"_id": "war", "title": "b", "_attachments": {"aa": {}}} })
); .then(function (result) {
deepEqual(result, {"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
// put a document with a wrong metadata "7ae41e4649b934ca495991b7852b855"
util.json_local_storage.setItem( });
"jio/localstorage/urepair/arepair/meta", equal(localStorage[id], JSON.stringify({
{"_id": "meta", "title": ["b", ["c", {}], {"blue": "blue"}]} "_id": id,
); "title": "myPutAttmt1",
"_attachments": {
// put a corrupted document "putattmt2": {
util.json_local_storage.setItem( content_type: "",
"jio/localstorage/urepair/arepair/cor", digest: "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"blue" "7ae41e4649b934ca495991b7852b855",
); length: 0
}; }
}
// put an unreferenced attachment }));
util.json_local_storage.setItem( equal(localStorage[id + '/putattmt2'], JSON.stringify(""));
"jio/localstorage/urepair/arepair/unref/aa", })
"attachment content" .fail(function (error) {
); ok(false, error);
o.putCorruptedDocuments(); })
all([
jio.check({"_id": "war"}),
jio.check({"_id": "meta"}),
jio.check({"_id": "cor"}),
jio.check({"_id": "inexistent"})
]).always(function (answers) {
deepEqual(answers[0], {
"error": "conflict",
"id": "war",
"message": "Attachment \"aa\" of \"war\" is missing",
"method": "check",
"reason": "missing attachment",
"result": "error",
"status": 409,
"statusText": "Conflict"
}, "Check a document with one missing attachment");
deepEqual(answers[1], {
"error": "conflict",
"id": "meta",
"message": "Some metadata might be lost",
"method": "check",
"reason": "corrupted",
"result": "error",
"status": 409,
"statusText": "Conflict"
}, "Check document with wrong metadata");
deepEqual(answers[2], {
"error": "conflict",
"id": "cor",
"message": "Document is unrecoverable",
"method": "check",
"reason": "corrupted",
"result": "error",
"status": 409,
"statusText": "Conflict"
}, "Check corrupted document");
deepEqual(answers[3], {
"id": "inexistent",
"method": "check",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Check inexistent document");
}).then(function () {
return all([
jio.repair({"_id": "war"}),
jio.repair({"_id": "meta"}),
jio.repair({"_id": "cor"}),
jio.repair({"_id": "inexistent"})
]);
}).always(function (answers) {
deepEqual(answers[0], {
"id": "war",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair a document with one missing attachment");
deepEqual(answers[1], {
"id": "meta",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair document with wrong metadata");
deepEqual(answers[2], {
"id": "cor",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair corrupted document");
deepEqual(answers[3], {
"id": "inexistent",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair inexistent document");
}).then(function () {
o.getCorruptedDocuments = function () {
return all([
jio.get({"_id": "war"}),
jio.get({"_id": "meta"}),
jio.get({"_id": "cor"}),
jio.get({"_id": "inexistent"})
]);
};
return o.getCorruptedDocuments();
}).always(function (answers) {
o.testGetAnswers = function (answers) {
deepEqual(answers[0], {
"data": {
"_id": "war",
"title": "b"
},
"id": "war",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get repaired document with one missing attachment");
deepEqual(answers[1], {
"data": {
"_id": "meta",
"title": "b"
},
"id": "meta",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get repaired document with wrong metadata");
deepEqual(answers[2], {
"error": "not_found",
"id": "cor",
"message": "Cannot find document",
"method": "get",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get repaired corrupted document");
deepEqual(answers[3], {
"error": "not_found",
"id": "inexistent",
"message": "Cannot find document",
"method": "get",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get repaired inexistent document");
};
o.testGetAnswers(answers);
}).then(function () {
o.putCorruptedDocuments();
return jio.repair({});
}).always(function (answer) {
deepEqual(answer, {
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair all the database");
}).then(function () {
return o.getCorruptedDocuments();
}).always(function (answers) {
o.testGetAnswers(answers);
}).then(function () {
// unreferenced attachment must be removed
deepEqual(util.json_local_storage.getItem(
"jio/localstorage/urepair/arepair/unref/aa"
), null, "Unreferenced attachment removed");
}).always(start);
.always(function () {
start();
});
}); });
})); // test("Remove & RemoveAttachment", function () {
// expect(4);
// var jio = jIO.createJIO({
// "type": "local",
// "username": "uremove",
// "application_name": "aremove"
// }, {
// "workspace": {}
// });
//
// stop();
//
// jio.put({"_id": "a"}).then(function () {
//
// return jio.putAttachment({"_id": "a", "_attachment": "b", "_data": "c"});
//
// }).then(function () {
//
// return jio.removeAttachment({"_id": "a", "_attachment": "b"});
//
// }).always(function (answer) {
//
// deepEqual(answer, {
// "attachment": "b",
// "id": "a",
// "method": "removeAttachment",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove existent attachment");
//
// }).then(function () {
//
// // Promise.all always return success
// return RSPV.all([jio.removeAttachment({
// "_id": "a",
// "_attachment": "b"
// })]);
//
// }).always(function (answers) {
//
// deepEqual(answers[0], {
// "attachment": "b",
// "error": "not_found",
// "id": "a",
// "message": "Attachment not found",
// "method": "removeAttachment",
// "reason": "missing attachment",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Remove removed attachment");
//
// }).then(function () {
//
// return jio.remove({"_id": "a"});
//
// }).always(function (answer) {
//
// deepEqual(answer, {
// "id": "a",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove existent document");
//
// }).then(function () {
//
// return jio.remove({"_id": "a"});
//
// }).always(function (answer) {
//
// deepEqual(answer, {
// "error": "not_found",
// "id": "a",
// "message": "Document not found",
// "method": "remove",
// "reason": "missing",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Remove removed document");
//
// }).always(start);
//
// });
//
// test("AllDocs", function () {
// expect(3);
// var o = {}, jio = jIO.createJIO({
// "type": "local",
// "username": "ualldocs",
// "application_name": "aalldocs"
// }, {
// "workspace": {}
// });
//
// stop();
//
// o.date_a = new Date(0);
// o.date_b = new Date();
//
// // put some document before list them
// RSVP.all([
// jio.put({
// "_id": "a",
// "title": "one",
// "date": o.date_a
// }).then(function () {
// return jio.putAttachment({
// "_id": "a",
// "_attachment": "aa",
// "_data": "aaa"
// });
// }),
// jio.put({"_id": "b", "title": "two", "date": o.date_a}),
// jio.put({"_id": "c", "title": "one", "date": o.date_b}),
// jio.put({"_id": "d", "title": "two", "date": o.date_b})
// ]).then(function () {
//
// // get a list of documents
// return jio.allDocs();
//
// }).always(function (answer) {
//
// // sort answer rows for comparison
// if (answer.data && answer.data.rows) {
// answer.data.rows.sort(function (a, b) {
// return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
// });
// }
//
// deepEqual(answer, {
// "data": {
// "rows": [{
// "id": "a",
// "key": "a",
// "value": {}
// }, {
// "id": "b",
// "key": "b",
// "value": {}
// }, {
// "id": "c",
// "key": "c",
// "value": {}
// }, {
// "id": "d",
// "key": "d",
// "value": {}
// }],
// "total_rows": 4
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "AllDocs");
//
// }).then(function () {
//
// // get a list of documents
// return jio.allDocs({
// "include_docs": true,
// "sort_on": [['title', 'ascending'], ['date', 'descending']],
// "select_list": ['title', 'date'],
// "limit": [1] // ==> equal [1, 3] in this case
// });
//
// }).always(function (answer) {
//
// deepEqual(answer, {
// "data": {
// "rows": [{
// "doc": {
// "_attachments": {
// "aa": {
// "content_type": "",
// "digest": "sha256-9834876dcfb05cb167a5c24953eba58c4" +
// "ac89b1adf57f28f2f9d09af107ee8f0",
// "length": 3
// }
// },
// "_id": "a",
// "date": o.date_a.toJSON(),
// "title": "one"
// },
// "id": "a",
// "key": "a",
// "value": {
// "date": o.date_a.toJSON(),
// "title": "one"
// }
// }, {
// "doc": {
// "_id": "d",
// "date": o.date_b.toJSON(),
// "title": "two"
// },
// "id": "d",
// "key": "d",
// "value": {
// "date": o.date_b.toJSON(),
// "title": "two"
// }
// }, {
// "doc": {
// "_id": "b",
// "date": o.date_a.toJSON(),
// "title": "two"
// },
// "id": "b",
// "key": "b",
// "value": {
// "date": o.date_a.toJSON(),
// "title": "two"
// }
// }],
// "total_rows": 3
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "AllDocs include docs + sort on + limit + select_list");
//
// }).then(function () {
//
// // use a query
// return jio.allDocs({
// "query": "title: \"two\"",
// "sort_on": [["date", "descending"]]
// });
//
// }).always(function (answer) {
//
// deepEqual(answer, {
// "data": {
// "rows": [{
// "id": "d",
// "key": "d",
// "value": {}
// }, {
// "id": "b",
// "key": "b",
// "value": {}
// }],
// "total_rows": 2
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "AllDocs sort on + query");
//
// }).always(start);
//
// });
//
// test("Check & Repair", function () {
// expect(18);
// var o = {}, jio = jIO.createJIO({
// "type": "local",
// "username": "urepair",
// "application_name": "arepair"
// }, {
// "workspace": {}
// });
//
// stop();
//
// o.putCorruptedDocuments = function () {
// // put a document with a wrong attachment reference
// util.json_localStorage.setItem(
// "jio/localstorage/urepair/arepair/war",
// {"_id": "war", "title": "b", "_attachments": {"aa": {}}}
// );
//
// // put a document with a wrong metadata
// util.json_localStorage.setItem(
// "jio/localstorage/urepair/arepair/meta",
// {"_id": "meta", "title": ["b", ["c", {}], {"blue": "blue"}]}
// );
//
// // put a corrupted document
// util.json_localStorage.setItem(
// "jio/localstorage/urepair/arepair/cor",
// "blue"
// );
// };
//
// // put an unreferenced attachment
// util.json_localStorage.setItem(
// "jio/localstorage/urepair/arepair/unref/aa",
// "attachment content"
// );
// o.putCorruptedDocuments();
//
// RSVP.all([
// jio.check({"_id": "war"}),
// jio.check({"_id": "meta"}),
// jio.check({"_id": "cor"}),
// jio.check({"_id": "inexistent"})
// ]).always(function (answers) {
//
// deepEqual(answers[0], {
// "error": "conflict",
// "id": "war",
// "message": "Attachment \"aa\" of \"war\" is missing",
// "method": "check",
// "reason": "missing attachment",
// "result": "error",
// "status": 409,
// "statusText": "Conflict"
// }, "Check a document with one missing attachment");
//
// deepEqual(answers[1], {
// "error": "conflict",
// "id": "meta",
// "message": "Some metadata might be lost",
// "method": "check",
// "reason": "corrupted",
// "result": "error",
// "status": 409,
// "statusText": "Conflict"
// }, "Check document with wrong metadata");
//
// deepEqual(answers[2], {
// "error": "conflict",
// "id": "cor",
// "message": "Document is unrecoverable",
// "method": "check",
// "reason": "corrupted",
// "result": "error",
// "status": 409,
// "statusText": "Conflict"
// }, "Check corrupted document");
//
// deepEqual(answers[3], {
// "id": "inexistent",
// "method": "check",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Check inexistent document");
//
// }).then(function () {
//
// return RSVP.all([
// jio.repair({"_id": "war"}),
// jio.repair({"_id": "meta"}),
// jio.repair({"_id": "cor"}),
// jio.repair({"_id": "inexistent"})
// ]);
//
// }).always(function (answers) {
//
// deepEqual(answers[0], {
// "id": "war",
// "method": "repair",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Repair a document with one missing attachment");
//
// deepEqual(answers[1], {
// "id": "meta",
// "method": "repair",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Repair document with wrong metadata");
//
// deepEqual(answers[2], {
// "id": "cor",
// "method": "repair",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Repair corrupted document");
//
// deepEqual(answers[3], {
// "id": "inexistent",
// "method": "repair",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Repair inexistent document");
//
// }).then(function () {
//
// o.getCorruptedDocuments = function () {
// return RSVP.all([
// jio.get({"_id": "war"}),
// jio.get({"_id": "meta"}),
// jio.get({"_id": "cor"}),
// jio.get({"_id": "inexistent"})
// ]);
// };
//
// return o.getCorruptedDocuments();
//
// }).always(function (answers) {
//
// o.testGetAnswers = function (answers) {
//
// deepEqual(answers[0], {
// "data": {
// "_id": "war",
// "title": "b"
// },
// "id": "war",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get repaired document with one missing attachment");
//
// deepEqual(answers[1], {
// "data": {
// "_id": "meta",
// "title": "b"
// },
// "id": "meta",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get repaired document with wrong metadata");
//
// deepEqual(answers[2], {
// "error": "not_found",
// "id": "cor",
// "message": "Cannot find document",
// "method": "get",
// "reason": "missing",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Get repaired corrupted document");
//
// deepEqual(answers[3], {
// "error": "not_found",
// "id": "inexistent",
// "message": "Cannot find document",
// "method": "get",
// "reason": "missing",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Get repaired inexistent document");
//
// };
//
// o.testGetAnswers(answers);
//
// }).then(function () {
//
// o.putCorruptedDocuments();
//
// return jio.repair({});
//
// }).always(function (answer) {
//
// deepEqual(answer, {
// "method": "repair",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Repair all the database");
//
// }).then(function () {
//
// return o.getCorruptedDocuments();
//
// }).always(function (answers) {
//
// o.testGetAnswers(answers);
//
// }).then(function () {
//
// // unreferenced attachment must be removed
// deepEqual(util.json_localStorage.getItem(
// "jio/localstorage/urepair/arepair/unref/aa"
// ), null, "Unreferenced attachment removed");
//
// }).always(start);
//
// });
}(jIO, localStorage, QUnit));
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint nomen: true, maxlen: 200 */
/*global window, define, module, test_util, RSVP, jIO, local_storage, test, ok, /*global Blob, test_util, console*/
deepEqual, sinon, expect, stop, start, Blob, query_storage */ (function (jIO, QUnit) {
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(test_util, RSVP, jIO, local_storage);
}([
'test_util',
'rsvp',
'jio',
'localstorage',
'qunit',
'querystorage'
], function (test_util, RSVP, jIO, local_storage) {
"use strict"; "use strict";
var test = QUnit.test,
module("QueryStorage"); stop = QUnit.stop,
start = QUnit.start,
function createQueryStorage(name, key_schema) { ok = QUnit.ok,
var local_description = local_storage.createDescription(name, expect = QUnit.expect,
name, deepEqual = QUnit.deepEqual,
'memory'); equal = QUnit.equal,
return jIO.createJIO({ module = QUnit.module;
type: 'query',
sub_storage: local_description, /////////////////////////////////////////////////////////////////
key_schema: key_schema // Custom test substorage definition
}, { /////////////////////////////////////////////////////////////////
workspace: {} function Storage200() {
}); return this;
} }
Storage200.prototype.get = function (param) {
equal(param._id, "bar", "get 200 called");
return {title: "foo"};
/* };
* What follows is almost a replica of the local storage tests, Storage200.prototype.put = function (param) {
* plus a couple of schema queries. deepEqual(param, {"_id": "bar", "title": "foo"}, "put 200 called");
* This is redundant, but guarantees that the storage is working return param._id;
* under all circumstances. };
*/ Storage200.prototype.remove = function (param) {
deepEqual(param, {"_id": "bar"}, "remove 200 called");
return param._id;
function success(promise) { };
return new RSVP.Promise(function (resolve, reject, notify) { Storage200.prototype.post = function (param) {
/*jslint unparam: true*/ deepEqual(param, {"_id": "bar", "title": "foo"}, "post 200 called");
promise.then(resolve, resolve, notify); return param._id;
}, function () { };
promise.cancel(); Storage200.prototype.allDocs = function (options) {
}); deepEqual(options, {"_id": "bar", "title": "foo"}, "post 200 called");
} return options._id;
};
jIO.addStorage('querystorage200', Storage200);
function unexpectedError(error) {
if (error instanceof Error) { /////////////////////////////////////////////////////////////////
deepEqual([ // queryStorage.get
error.name + ": " + error.message, /////////////////////////////////////////////////////////////////
error module("queryStorage.get");
], "UNEXPECTED ERROR", "Unexpected error"); test("get called substorage get", function () {
} else {
deepEqual(error, "UNEXPECTED ERROR", "Unexpected error");
}
}
test("post & get", 6, function () {
var jio = createQueryStorage('post-get');
stop(); stop();
expect(2);
function getMissingDocument() { var jio = jIO.createJIO({
return success(jio.get({_id: 'inexistent'})); type: "query",
} sub_storage: {
type: "querystorage200"
function getMissingDocumentTest(answer) { }
deepEqual(answer, { });
"error": "not_found",
"id": "inexistent",
"message": "Cannot find document",
"method": "get",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent document");
}
function postWithoutID() {
return jio.post({});
}
function postWithoutIDTest(answer) {
var uuid = answer.id;
delete answer.id;
deepEqual(answer, {
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
});
ok(test_util.isUuid(uuid), "Uuid should look like " +
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
}
function postNonEmptyDocument() {
return jio.post({"_id": "post1", "title": "myPost1"});
}
function postNonEmptyDocumentTest(answer) {
deepEqual(answer, {
"id": "post1",
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
});
}
function getNonEmptyDocument() {
return jio.get({"_id": "post1"});
}
function getNonEmptyDocumentTest(answer) {
deepEqual(answer, {
"data": {
"_id": "post1",
"title": "myPost1"
},
"id": "post1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
});
}
function postExistingDocument() {
return success(jio.post({"_id": "post1", "title": "myPost2"}));
}
function postExistingDocumentTest(answer) { jio.get({"_id": "bar"})
deepEqual(answer, { .then(function (result) {
"error": "conflict", deepEqual(result, {
"id": "post1", "title": "foo"
"message": "Cannot create a new document", }, "Check document");
"method": "post", })
"reason": "document exists", .fail(function (error) {
"result": "error", ok(false, error);
"status": 409, })
"statusText": "Conflict" .always(function () {
start();
}); });
}
getMissingDocument().then(getMissingDocumentTest).
then(postWithoutID).then(postWithoutIDTest).
then(postNonEmptyDocument).then(postNonEmptyDocumentTest).
then(getNonEmptyDocument).then(getNonEmptyDocumentTest).
then(postExistingDocument).then(postExistingDocumentTest).
fail(unexpectedError).
always(start);
}); });
/////////////////////////////////////////////////////////////////
// queryStorage.post
test("put & get", 4, function () { /////////////////////////////////////////////////////////////////
var jio = createQueryStorage('put-get'); module("queryStorage.post");
test("post called substorage post", function () {
stop(); stop();
expect(2);
function putNonEmptyDocument() { var jio = jIO.createJIO({
return jio.put({"_id": "put1", "title": "myPut1"}); type: "query",
} sub_storage: {
type: "querystorage200"
function putNonEmptyDocumentTest(answer) { }
deepEqual(answer, { });
"id": "put1",
"method": "put",
"result": "success",
"status": 201,
"statusText": "Created"
});
}
function getNonEmptyDocument() {
return jio.get({"_id": "put1"});
}
function getNonEmptyDocumentTest(answer) {
deepEqual(answer, {
"data": {
"_id": "put1",
"title": "myPut1"
},
"id": "put1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
});
}
function putExistingDocument() {
return success(jio.put({"_id": "put1", "title": "myPut2"}));
}
function putExistingDocumentTest(answer) {
deepEqual(answer, {
"id": "put1",
"method": "put",
"result": "success",
"status": 204,
"statusText": "No Content"
});
}
function getNonEmptyDocument2() {
return jio.get({"_id": "put1"});
}
function getNonEmptyDocument2Test(answer) { jio.post({"_id": "bar", "title": "foo"})
deepEqual(answer, { .then(function (result) {
"data": { equal(result, "bar");
"_id": "put1", })
"title": "myPut2" .fail(function (error) {
}, ok(false, error);
"id": "put1", })
"method": "get", .always(function () {
"result": "success", start();
"status": 200,
"statusText": "Ok"
}); });
}
putNonEmptyDocument().then(putNonEmptyDocumentTest).
then(getNonEmptyDocument).then(getNonEmptyDocumentTest).
then(putExistingDocument).then(putExistingDocumentTest).
then(getNonEmptyDocument2).then(getNonEmptyDocument2Test).
fail(unexpectedError).
always(start);
}); });
/////////////////////////////////////////////////////////////////
test("putAttachment & get & getAttachment", 9, function () { // queryStorage.put
var jio = createQueryStorage('putattachment-get-getattachment'); /////////////////////////////////////////////////////////////////
module("queryStorage.put");
test("put called substorage put", function () {
stop(); stop();
expect(2);
function getAttachmentMissingDocument() { var jio = jIO.createJIO({
return success(jio.getAttachment({ type: "query",
"_id": "inexistent", sub_storage: {
"_attachment": "a" type: "querystorage200"
})); }
} });
function getAttachmentMissingDocumentTest(answer) {
deepEqual(answer, {
"attachment": "a",
"error": "not_found",
"id": "inexistent",
"message": "Cannot find document",
"method": "getAttachment",
"reason": "missing document",
"result": "error",
"status": 404,
"statusText": "Not Found"
});
}
function getAttachmentFromEmptyDocument() {
var promise = jio.put({"_id": "b"}).
then(function () {
return jio.getAttachment({"_id": "b", "_attachment": "inexistent"});
});
return success(promise);
}
function getAttachmentFromEmptyDocumentTest(answer) {
deepEqual(answer, {
"attachment": "inexistent",
"error": "not_found",
"id": "b",
"message": "Cannot find attachment",
"method": "getAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
});
}
function putAttachmentMissingDocument() {
return success(jio.putAttachment({
"_id": "inexistent",
"_attachment": "putattmt2",
"_data": ""
}));
}
function putAttachmentMissingDocumentTest(answer) {
deepEqual(answer, {
"attachment": "putattmt2",
"error": "not_found",
"id": "inexistent",
"message": "Impossible to add attachment",
"method": "putAttachment",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
});
}
function addAttachment() {
var promise = jio.put({"_id": "putattmt1", "title": "myPutAttmt1"}).
then(function () {
return jio.putAttachment({
"_id": "putattmt1",
"_attachment": "putattmt2",
"_data": ""
});
});
return success(promise);
}
function addAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "putattmt2",
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b93" +
"4ca495991b7852b855",
"id": "putattmt1",
"method": "putAttachment",
"result": "success",
"status": 201,
"statusText": "Created"
});
}
function checkDocumentAndAttachment() {
return RSVP.all([
jio.get({"_id": "putattmt1"}),
jio.getAttachment({"_id": "putattmt1", "_attachment": "putattmt2"})
]);
}
function checkDocumentAndAttachmentTest(answers) { jio.put({"_id": "bar", "title": "foo"})
deepEqual(answers[0], { .then(function (result) {
"data": { equal(result, "bar");
"_attachments": { })
"putattmt2": { .fail(function (error) {
"content_type": "", ok(false, error);
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4" + })
"649b934ca495991b7852b855", .always(function () {
"length": 0 start();
}
},
"_id": "putattmt1",
"title": "myPutAttmt1"
},
"id": "putattmt1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}); });
ok(answers[1].data instanceof Blob, "Data is Blob");
deepEqual(answers[1].data.type, "", "Check mimetype");
deepEqual(answers[1].data.size, 0, "Check size");
delete answers[1].data;
deepEqual(answers[1], {
"attachment": "putattmt2",
"id": "putattmt1",
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get Attachment, Check Response");
}
getAttachmentMissingDocument().then(getAttachmentMissingDocumentTest).
then(getAttachmentFromEmptyDocument).
then(getAttachmentFromEmptyDocumentTest).
then(putAttachmentMissingDocument).
then(putAttachmentMissingDocumentTest).
then(addAttachment).then(addAttachmentTest).
then(checkDocumentAndAttachment).then(checkDocumentAndAttachmentTest).
fail(unexpectedError).
always(start);
}); });
/////////////////////////////////////////////////////////////////
test("remove & removeAttachment", 5, function () { // queryStorage.remove
var jio = createQueryStorage('remove-removeattachment'); /////////////////////////////////////////////////////////////////
module("queryStorage.remove");
test("remove called substorage remove", function () {
stop(); stop();
expect(2);
function putAttachment() { var jio = jIO.createJIO({
var promise = jio.put({"_id": "a"}). type: "query",
then(function () { sub_storage: {
return jio.putAttachment({ type: "querystorage200"
"_id": "a", }
"_attachment": "b", });
"_data": "c"
});
});
return promise;
}
function putAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "b",
"digest": "sha256-2e7d2c03a9507ae265ecf5b5356885a53" +
"393a2029d241394997265a1a25aefc6",
"id": "a",
"method": "putAttachment",
"result": "success",
"status": 201,
"statusText": "Created"
});
}
function removeAttachment() {
return success(jio.removeAttachment({"_id": "a", "_attachment": "b"}));
}
function removeAttachmentTest(answer) {
deepEqual(answer, {
"attachment": "b",
"id": "a",
"method": "removeAttachment",
"result": "success",
"status": 204,
"statusText": "No Content"
});
}
function removeAttachmentAgainTest(answer) {
deepEqual(answer, {
"attachment": "b",
"error": "not_found",
"id": "a",
"message": "Attachment not found",
"method": "removeAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
});
}
function removeDocument() {
return success(jio.remove({"_id": "a"}));
}
function removeDocumentTest(answer) {
deepEqual(answer, {
"id": "a",
"method": "remove",
"result": "success",
"status": 204,
"statusText": "No Content"
});
}
function removeDocumentAgainTest(answer) { jio.remove({"_id": "bar"})
deepEqual(answer, { .then(function (result) {
"error": "not_found", equal(result, "bar");
"id": "a", })
"message": "Document not found", .fail(function (error) {
"method": "remove", ok(false, error);
"reason": "missing", })
"result": "error", .always(function () {
"status": 404, start();
"statusText": "Not Found"
}); });
}
putAttachment().then(putAttachmentTest).
then(removeAttachment).then(removeAttachmentTest).
then(removeAttachment).then(removeAttachmentAgainTest).
then(removeDocument).then(removeDocumentTest).
then(removeDocument).then(removeDocumentAgainTest).
fail(unexpectedError).
always(start);
}); });
/////////////////////////////////////////////////////////////////
test("allDocs", 5, function () { // queryStorage.allDocs
var jio = createQueryStorage('alldocs'), /////////////////////////////////////////////////////////////////
key_schema = { module("queryStorage.allDocs");
key_set: { test("allDocs called substorage allDocs", function () {
case_insensitive_title: {
read_from: 'title',
equal_match: function (object_value, value) {
return (object_value.toLowerCase() === value.toLowerCase());
}
}
}
};
stop(); stop();
expect(2);
var jio = jIO.createJIO({
function putDocuments() { type: "query",
var date_a = new Date(0), sub_storage: {
date_b = new Date(1234567890000); type: "querystorage200"
return RSVP.all([
jio.put({
"_id": "a",
"title": "one",
"date": date_a
}).then(function () {
return jio.putAttachment({
"_id": "a",
"_attachment": "aa",
"_data": "aaa"
});
}),
jio.put({"_id": "b", "title": "two", "date": date_a}),
jio.put({"_id": "c", "title": "one", "date": date_b}),
jio.put({"_id": "d", "title": "two", "date": date_b})
]);
}
function putDocumentsTest(answer) {
// sort answer rows for comparison
if (answer.data && answer.data.rows) {
answer.data.rows.sort(function (a, b) {
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
});
} }
});
deepEqual(answer, [ jio.allDocs({
{ include_docs: true,
"attachment": "aa", query: 'title: "two"'
"digest": "sha256-9834876dcfb05cb167a5c24953eba58" + })
"c4ac89b1adf57f28f2f9d09af107ee8f0", .then(function (result) {
"id": "a", equal(result, "bar");
"method": "putAttachment", })
"result": "success", .fail(function (error) {
"status": 201, console.error(error);
"statusText": "Created" console.error(error.stack);
}, ok(false, error);
{ })
"id": "b", .always(function () {
"method": "put", start();
"result": "success",
"status": 201,
"statusText": "Created"
},
{
"id": "c",
"method": "put",
"result": "success",
"status": 201,
"statusText": "Created"
},
{
"id": "d",
"method": "put",
"result": "success",
"status": 201,
"statusText": "Created"
}
]);
}
function listDocuments() {
return jio.allDocs();
}
function listDocumentsTest(answer) {
deepEqual(answer, {
"data": {
"rows": [
{
"id": "a",
"key": "a",
"value": {}
},
{
"id": "b",
"key": "b",
"value": {}
},
{
"id": "c",
"key": "c",
"value": {}
},
{
"id": "d",
"key": "d",
"value": {}
}
],
"total_rows": 4
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
});
}
function listDocumentsQuery() {
return jio.allDocs({
"include_docs": true,
"query": "title: \"two\""
});
}
function listDocumentsQueryTest(answer) {
deepEqual(answer, {
"data": {
"rows": [
{
"doc": {
"_id": "b",
"date": "1970-01-01T00:00:00.000Z",
"title": "two"
},
"id": "b",
"value": {}
},
{
"doc": {
"_id": "d",
"date": "2009-02-13T23:31:30.000Z",
"title": "two"
},
"id": "d",
"value": {}
}
],
"total_rows": 2
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
});
}
function listDocumentSchemaQueryNoDocs() {
var jio_schema = createQueryStorage('alldocs', key_schema);
return jio_schema.allDocs({
"include_docs": false,
"query": "case_insensitive_title: \"oNe\""
});
}
function listDocumentSchemaQueryNoDocsTest(answer) {
deepEqual(answer, {
"data": {
"rows": [
{
"id": "a",
"value": {}
},
{
"id": "c",
"value": {}
}
],
"total_rows": 2
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
});
}
function listDocumentSchemaQueryWithDocs() {
var jio_schema = createQueryStorage('alldocs', key_schema);
return jio_schema.allDocs({
"include_docs": true,
"query": "case_insensitive_title: \"oNe\""
});
}
function listDocumentSchemaQueryWithDocsTest(answer) {
deepEqual(answer, {
"data": {
"rows": [
{
"doc": {
"_attachments": {
"aa": {
"content_type": "",
"digest": "sha256-9834876dcfb05cb167a5c24953eba58c" +
"4ac89b1adf57f28f2f9d09af107ee8f0",
"length": 3
}
},
"_id": "a",
"date": "1970-01-01T00:00:00.000Z",
"title": "one"
},
"id": "a",
"value": {}
},
{
"doc": {
"_id": "c",
"date": "2009-02-13T23:31:30.000Z",
"title": "one"
},
"id": "c",
"value": {}
}
],
"total_rows": 2
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}); });
}
putDocuments().then(putDocumentsTest).
then(listDocuments).then(listDocumentsTest).
then(listDocumentsQuery).then(listDocumentsQueryTest).
then(listDocumentSchemaQueryNoDocs).
then(listDocumentSchemaQueryNoDocsTest).
then(listDocumentSchemaQueryWithDocs).
then(listDocumentSchemaQueryWithDocsTest).
fail(unexpectedError).
always(start);
}); });
// function createQueryStorage(name, key_schema) {
// // var local_description = local_storage.createDescription(name,
// XXX check/repair not tested yet, may change soon btw // // name,
// test("check & repair", 18, function () { // // 'memory');
// }) // return jIO.createJIO({
// type: 'query',
})); // sub_storage: {
// type: name
// },
// key_schema: key_schema
// }, {
// workspace: {}
// });
// }
//
//
//
// /*
// * What follows is almost a replica of the local storage tests,
// * plus a couple of schema queries.
// * This is redundant, but guarantees that the storage is working
// * under all circumstances.
// */
//
//
// function success(promise) {
// return new RSVP.Promise(function (resolve, reject, notify) {
// /*jslint unparam: true*/
// promise.then(resolve, resolve, notify);
// }, function () {
// promise.cancel();
// });
// }
//
//
// function unexpectedError(error) {
// if (error instanceof Error) {
// deepEqual([
// error.name + ": " + error.message,
// error
// ], "UNEXPECTED ERROR", "Unexpected error");
// } else {
// deepEqual(error, "UNEXPECTED ERROR", "Unexpected error");
// }
// }
//
//
// test("post & get", 6, function () {
// var jio = createQueryStorage('post-get');
//
// stop();
//
// function getMissingDocument() {
// return success(jio.get({_id: 'inexistent'}));
// }
//
// function getMissingDocumentTest(answer) {
// deepEqual(answer, {
// "error": "not_found",
// "id": "inexistent",
// "message": "Cannot find document",
// "method": "get",
// "reason": "missing",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// }, "Get inexistent document");
// }
//
// function postWithoutID() {
// return jio.post({});
// }
//
// function postWithoutIDTest(answer) {
// var uuid = answer.id;
//
// delete answer.id;
// deepEqual(answer, {
// "method": "post",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// });
// ok(test_util.isUuid(uuid), "Uuid should look like " +
// "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
// }
//
// function postNonEmptyDocument() {
// return jio.post({"_id": "post1", "title": "myPost1"});
// }
//
// function postNonEmptyDocumentTest(answer) {
// deepEqual(answer, {
// "id": "post1",
// "method": "post",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// });
// }
//
// function getNonEmptyDocument() {
// return jio.get({"_id": "post1"});
// }
//
// function getNonEmptyDocumentTest(answer) {
// deepEqual(answer, {
// "data": {
// "_id": "post1",
// "title": "myPost1"
// },
// "id": "post1",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
// function postExistingDocument() {
// return success(jio.post({"_id": "post1", "title": "myPost2"}));
// }
//
// function postExistingDocumentTest(answer) {
// deepEqual(answer, {
// "error": "conflict",
// "id": "post1",
// "message": "Cannot create a new document",
// "method": "post",
// "reason": "document exists",
// "result": "error",
// "status": 409,
// "statusText": "Conflict"
// });
// }
//
// getMissingDocument().then(getMissingDocumentTest).
// then(postWithoutID).then(postWithoutIDTest).
// then(postNonEmptyDocument).then(postNonEmptyDocumentTest).
// then(getNonEmptyDocument).then(getNonEmptyDocumentTest).
// then(postExistingDocument).then(postExistingDocumentTest).
// fail(unexpectedError).
// always(start);
// });
//
//
//
// test("put & get", 4, function () {
// var jio = createQueryStorage('put-get');
//
// stop();
//
// function putNonEmptyDocument() {
// return jio.put({"_id": "put1", "title": "myPut1"});
// }
//
// function putNonEmptyDocumentTest(answer) {
// deepEqual(answer, {
// "id": "put1",
// "method": "put",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// });
// }
//
// function getNonEmptyDocument() {
// return jio.get({"_id": "put1"});
// }
//
// function getNonEmptyDocumentTest(answer) {
// deepEqual(answer, {
// "data": {
// "_id": "put1",
// "title": "myPut1"
// },
// "id": "put1",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
// function putExistingDocument() {
// return success(jio.put({"_id": "put1", "title": "myPut2"}));
// }
//
// function putExistingDocumentTest(answer) {
// deepEqual(answer, {
// "id": "put1",
// "method": "put",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// });
// }
//
// function getNonEmptyDocument2() {
// return jio.get({"_id": "put1"});
// }
//
// function getNonEmptyDocument2Test(answer) {
// deepEqual(answer, {
// "data": {
// "_id": "put1",
// "title": "myPut2"
// },
// "id": "put1",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
// putNonEmptyDocument().then(putNonEmptyDocumentTest).
// then(getNonEmptyDocument).then(getNonEmptyDocumentTest).
// then(putExistingDocument).then(putExistingDocumentTest).
// then(getNonEmptyDocument2).then(getNonEmptyDocument2Test).
// fail(unexpectedError).
// always(start);
// });
//
//
// test("putAttachment & get & getAttachment", 9, function () {
// var jio = createQueryStorage('putattachment-get-getattachment');
//
// stop();
//
// function getAttachmentMissingDocument() {
// return success(jio.getAttachment({
// "_id": "inexistent",
// "_attachment": "a"
// }));
// }
//
// function getAttachmentMissingDocumentTest(answer) {
// deepEqual(answer, {
// "attachment": "a",
// "error": "not_found",
// "id": "inexistent",
// "message": "Cannot find document",
// "method": "getAttachment",
// "reason": "missing document",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// });
// }
//
// function getAttachmentFromEmptyDocument() {
// var promise = jio.put({"_id": "b"}).
// then(function () {
// return jio.getAttachment({"_id": "b", "_attachment": "inexistent"});
// });
// return success(promise);
// }
//
// function getAttachmentFromEmptyDocumentTest(answer) {
// deepEqual(answer, {
// "attachment": "inexistent",
// "error": "not_found",
// "id": "b",
// "message": "Cannot find attachment",
// "method": "getAttachment",
// "reason": "missing attachment",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// });
// }
//
// function putAttachmentMissingDocument() {
// return success(jio.putAttachment({
// "_id": "inexistent",
// "_attachment": "putattmt2",
// "_data": ""
// }));
// }
//
// function putAttachmentMissingDocumentTest(answer) {
// deepEqual(answer, {
// "attachment": "putattmt2",
// "error": "not_found",
// "id": "inexistent",
// "message": "Impossible to add attachment",
// "method": "putAttachment",
// "reason": "missing",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// });
// }
//
// function addAttachment() {
// var promise = jio.put({"_id": "putattmt1", "title": "myPutAttmt1"}).
// then(function () {
// return jio.putAttachment({
// "_id": "putattmt1",
// "_attachment": "putattmt2",
// "_data": ""
// });
// });
// return success(promise);
// }
//
// function addAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "putattmt2",
// "digest": "sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b93" +
// "4ca495991b7852b855",
// "id": "putattmt1",
// "method": "putAttachment",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// });
// }
//
// function checkDocumentAndAttachment() {
// return RSVP.all([
// jio.get({"_id": "putattmt1"}),
// jio.getAttachment({"_id": "putattmt1", "_attachment": "putattmt2"})
// ]);
// }
//
// function checkDocumentAndAttachmentTest(answers) {
// deepEqual(answers[0], {
// "data": {
// "_attachments": {
// "putattmt2": {
// "content_type": "",
// "digest": "sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4" +
// "649b934ca495991b7852b855",
// "length": 0
// }
// },
// "_id": "putattmt1",
// "title": "myPutAttmt1"
// },
// "id": "putattmt1",
// "method": "get",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
//
// ok(answers[1].data instanceof Blob, "Data is Blob");
// deepEqual(answers[1].data.type, "", "Check mimetype");
// deepEqual(answers[1].data.size, 0, "Check size");
//
// delete answers[1].data;
// deepEqual(answers[1], {
// "attachment": "putattmt2",
// "id": "putattmt1",
// "digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
// "7ae41e4649b934ca495991b7852b855",
// "method": "getAttachment",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "Get Attachment, Check Response");
// }
//
// getAttachmentMissingDocument().then(getAttachmentMissingDocumentTest).
// then(getAttachmentFromEmptyDocument).
// then(getAttachmentFromEmptyDocumentTest).
// then(putAttachmentMissingDocument).
// then(putAttachmentMissingDocumentTest).
// then(addAttachment).then(addAttachmentTest).
// then(checkDocumentAndAttachment).then(checkDocumentAndAttachmentTest).
// fail(unexpectedError).
// always(start);
// });
//
//
// test("remove & removeAttachment", 5, function () {
// var jio = createQueryStorage('remove-removeattachment');
//
// stop();
//
// function putAttachment() {
// var promise = jio.put({"_id": "a"}).
// then(function () {
// return jio.putAttachment({
// "_id": "a",
// "_attachment": "b",
// "_data": "c"
// });
// });
//
// return promise;
// }
//
// function putAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "b",
// "digest": "sha256-2e7d2c03a9507ae265ecf5b5356885a53" +
// "393a2029d241394997265a1a25aefc6",
// "id": "a",
// "method": "putAttachment",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// });
// }
//
// function removeAttachment() {
// return success(jio.removeAttachment({"_id": "a", "_attachment": "b"}));
// }
//
// function removeAttachmentTest(answer) {
// deepEqual(answer, {
// "attachment": "b",
// "id": "a",
// "method": "removeAttachment",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// });
// }
//
// function removeAttachmentAgainTest(answer) {
// deepEqual(answer, {
// "attachment": "b",
// "error": "not_found",
// "id": "a",
// "message": "Attachment not found",
// "method": "removeAttachment",
// "reason": "missing attachment",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// });
// }
//
// function removeDocument() {
// return success(jio.remove({"_id": "a"}));
// }
//
// function removeDocumentTest(answer) {
// deepEqual(answer, {
// "id": "a",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// });
// }
//
// function removeDocumentAgainTest(answer) {
// deepEqual(answer, {
// "error": "not_found",
// "id": "a",
// "message": "Document not found",
// "method": "remove",
// "reason": "missing",
// "result": "error",
// "status": 404,
// "statusText": "Not Found"
// });
// }
//
// putAttachment().then(putAttachmentTest).
// then(removeAttachment).then(removeAttachmentTest).
// then(removeAttachment).then(removeAttachmentAgainTest).
// then(removeDocument).then(removeDocumentTest).
// then(removeDocument).then(removeDocumentAgainTest).
// fail(unexpectedError).
// always(start);
// });
//
//
// test("allDocs", 5, function () {
// var jio = createQueryStorage('alldocs'),
// key_schema = {
// key_set: {
// case_insensitive_title: {
// read_from: 'title',
// equal_match: function (object_value, value) {
// return (object_value.toLowerCase() === value.toLowerCase());
// }
// }
// }
// };
//
// stop();
//
//
// function putDocuments() {
// var date_a = new Date(0),
// date_b = new Date(1234567890000);
//
// return RSVP.all([
// jio.put({
// "_id": "a",
// "title": "one",
// "date": date_a
// }).then(function () {
// return jio.putAttachment({
// "_id": "a",
// "_attachment": "aa",
// "_data": "aaa"
// });
// }),
// jio.put({"_id": "b", "title": "two", "date": date_a}),
// jio.put({"_id": "c", "title": "one", "date": date_b}),
// jio.put({"_id": "d", "title": "two", "date": date_b})
// ]);
// }
//
// function putDocumentsTest(answer) {
// // sort answer rows for comparison
// if (answer.data && answer.data.rows) {
// answer.data.rows.sort(function (a, b) {
// return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
// });
// }
//
// deepEqual(answer, [
// {
// "attachment": "aa",
// "digest": "sha256-9834876dcfb05cb167a5c24953eba58" +
// "c4ac89b1adf57f28f2f9d09af107ee8f0",
// "id": "a",
// "method": "putAttachment",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// },
// {
// "id": "b",
// "method": "put",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// },
// {
// "id": "c",
// "method": "put",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// },
// {
// "id": "d",
// "method": "put",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// }
// ]);
//
// }
//
//
// function listDocuments() {
// return jio.allDocs();
// }
//
// function listDocumentsTest(answer) {
// deepEqual(answer, {
// "data": {
// "rows": [
// {
// "id": "a",
// "key": "a",
// "value": {}
// },
// {
// "id": "b",
// "key": "b",
// "value": {}
// },
// {
// "id": "c",
// "key": "c",
// "value": {}
// },
// {
// "id": "d",
// "key": "d",
// "value": {}
// }
// ],
// "total_rows": 4
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
// function listDocumentsQuery() {
// return jio.allDocs({
// "include_docs": true,
// "query": "title: \"two\""
// });
// }
//
//
// function listDocumentsQueryTest(answer) {
// deepEqual(answer, {
// "data": {
// "rows": [
// {
// "doc": {
// "_id": "b",
// "date": "1970-01-01T00:00:00.000Z",
// "title": "two"
// },
// "id": "b",
// "value": {}
// },
// {
// "doc": {
// "_id": "d",
// "date": "2009-02-13T23:31:30.000Z",
// "title": "two"
// },
// "id": "d",
// "value": {}
// }
// ],
// "total_rows": 2
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
// function listDocumentSchemaQueryNoDocs() {
// var jio_schema = createQueryStorage('alldocs', key_schema);
// return jio_schema.allDocs({
// "include_docs": false,
// "query": "case_insensitive_title: \"oNe\""
// });
// }
//
// function listDocumentSchemaQueryNoDocsTest(answer) {
// deepEqual(answer, {
// "data": {
// "rows": [
// {
// "id": "a",
// "value": {}
// },
// {
// "id": "c",
// "value": {}
// }
// ],
// "total_rows": 2
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
//
// function listDocumentSchemaQueryWithDocs() {
// var jio_schema = createQueryStorage('alldocs', key_schema);
// return jio_schema.allDocs({
// "include_docs": true,
// "query": "case_insensitive_title: \"oNe\""
// });
// }
//
// function listDocumentSchemaQueryWithDocsTest(answer) {
// deepEqual(answer, {
// "data": {
// "rows": [
// {
// "doc": {
// "_attachments": {
// "aa": {
// "content_type": "",
// "digest": "sha256-9834876dcfb05cb167a5c24953eba58c" +
// "4ac89b1adf57f28f2f9d09af107ee8f0",
// "length": 3
// }
// },
// "_id": "a",
// "date": "1970-01-01T00:00:00.000Z",
// "title": "one"
// },
// "id": "a",
// "value": {}
// },
// {
// "doc": {
// "_id": "c",
// "date": "2009-02-13T23:31:30.000Z",
// "title": "one"
// },
// "id": "c",
// "value": {}
// }
// ],
// "total_rows": 2
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// });
// }
//
//
//
// putDocuments().then(putDocumentsTest).
// then(listDocuments).then(listDocumentsTest).
// then(listDocumentsQuery).then(listDocumentsQueryTest).
// then(listDocumentSchemaQueryNoDocs).
// then(listDocumentSchemaQueryNoDocsTest).
// then(listDocumentSchemaQueryWithDocs).
// then(listDocumentSchemaQueryWithDocsTest).
// fail(unexpectedError).
// always(start);
// });
//
//
//
// // XXX check/repair not tested yet, may change soon btw
// // test("check & repair", 18, function () {
// // })
}(jIO, QUnit));
/*jslint nomen: true */
(function (jIO, QUnit) {
"use strict";
var test = QUnit.test,
stop = QUnit.stop,
start = QUnit.start,
ok = QUnit.ok,
expect = QUnit.expect,
deepEqual = QUnit.deepEqual,
equal = QUnit.equal,
module = QUnit.module;
/////////////////////////////////////////////////////////////////
// Custom test substorage definition
/////////////////////////////////////////////////////////////////
function Storage404() {
return this;
}
function generate404Error(param) {
equal(param._id, "bar", "get 404 called");
throw new jIO.util.jIOError("Cannot find document", 404);
}
Storage404.prototype.get = generate404Error;
jIO.addStorage('storage404', Storage404);
function Storage200() {
return this;
}
Storage200.prototype.get = function (param) {
equal(param._id, "bar", "get 200 called");
return {title: "foo"};
};
Storage200.prototype.put = function (param) {
deepEqual(param, {"_id": "bar", "title": "foo"}, "put 200 called");
return param._id;
};
Storage200.prototype.remove = function (param) {
deepEqual(param, {"_id": "bar"}, "remove 200 called");
return param._id;
};
Storage200.prototype.post = function (param) {
deepEqual(param, {"_id": "bar", "title": "foo"}, "post 200 called");
return param._id;
};
jIO.addStorage('storage200', Storage200);
function Storage500() {
return this;
}
function generateError() {
ok(true, "Error generation called");
throw new Error("manually triggered error");
}
Storage500.prototype.get = generateError;
Storage500.prototype.post = generateError;
jIO.addStorage('storage500', Storage500);
/////////////////////////////////////////////////////////////////
// unionStorage.get
/////////////////////////////////////////////////////////////////
module("unionStorage.get");
test("get inexistent document", function () {
stop();
expect(5);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage404"
}, {
type: "storage404"
}]
});
jio.get({"_id": "bar"})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document");
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get document on first storage", function () {
stop();
expect(2);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage200"
}, {
type: "storage404"
}]
});
jio.get({"_id": "bar"})
.then(function (result) {
deepEqual(result, {
"title": "foo"
}, "Check document");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get document on second storage", function () {
stop();
expect(3);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage404"
}, {
type: "storage200"
}]
});
jio.get({"_id": "bar"})
.then(function (result) {
deepEqual(result, {
"title": "foo"
}, "Check document");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get error on first storage", function () {
stop();
expect(4);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage500"
}, {
type: "storage200"
}]
});
jio.get({"_id": "bar"})
.fail(function (error) {
ok(error instanceof Error);
equal(error.message, "manually triggered error");
equal(error.status_code, undefined);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get error on second storage", function () {
stop();
expect(5);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage404"
}, {
type: "storage500"
}]
});
jio.get({"_id": "bar"})
.fail(function (error) {
ok(error instanceof Error);
equal(error.message, "manually triggered error");
equal(error.status_code, undefined);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// unionStorage.post
/////////////////////////////////////////////////////////////////
module("unionStorage.post");
test("post generate an error", function () {
stop();
expect(4);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage500"
}, {
type: "storage200"
}]
});
jio.post({"_id": "bar", "title": "foo"})
.fail(function (error) {
ok(error instanceof Error);
equal(error.message, "manually triggered error");
equal(error.status_code, undefined);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("post store on first storage", function () {
stop();
expect(2);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage200"
}, {
type: "storage500"
}]
});
jio.post({"_id": "bar", "title": "foo"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// unionStorage.put
/////////////////////////////////////////////////////////////////
module("unionStorage.put");
test("put generate an error", function () {
stop();
expect(4);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage500"
}, {
type: "storage200"
}]
});
jio.put({"_id": "bar", "title": "foo"})
.fail(function (error) {
ok(error instanceof Error);
equal(error.message, "manually triggered error");
equal(error.status_code, undefined);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("put on first storage", function () {
stop();
expect(3);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage200"
}, {
type: "storage500"
}]
});
jio.put({"_id": "bar", "title": "foo"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("put on second storage", function () {
stop();
expect(4);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage404"
}, {
type: "storage200"
}]
});
jio.put({"_id": "bar", "title": "foo"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// unionStorage.remove
/////////////////////////////////////////////////////////////////
module("unionStorage.remove");
test("remove generate an error", function () {
stop();
expect(4);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage500"
}, {
type: "storage200"
}]
});
jio.remove({"_id": "bar"})
.fail(function (error) {
ok(error instanceof Error);
equal(error.message, "manually triggered error");
equal(error.status_code, undefined);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("remove on first storage", function () {
stop();
expect(3);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage200"
}, {
type: "storage500"
}]
});
jio.remove({"_id": "bar"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("remove on second storage", function () {
stop();
expect(4);
var jio = jIO.createJIO({
type: "union",
storage_list: [{
type: "storage404"
}, {
type: "storage200"
}]
});
jio.remove({"_id": "bar"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}(jIO, QUnit));
/*jslint indent: 2, newcap: true */ /*jslint nomen: true, newcap: true */
/*global define, exports, require, test, ok, strictEqual, equal, throws, jiodate, moment, module */ /*global jiodate, moment, console*/
(function (QUnit, jiodate, moment) {
// 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(require('jiodate'), require('moment'));
}
module(jiodate, moment);
}(['jiodate', 'moment', 'qunit'], function (jiodate, moment) {
"use strict"; "use strict";
var test = QUnit.test,
ok = QUnit.ok,
strictEqual = QUnit.strictEqual,
equal = QUnit.equal,
module = QUnit.module,
JIODate = jiodate.JIODate,
throws = QUnit.throws;
module('JIODate'); module('JIODate');
var JIODate = jiodate.JIODate;
test("A JIODate can be instantiated without parameters (=now)", function () { test("A JIODate can be instantiated without parameters (=now)", function () {
ok((new JIODate()) instanceof JIODate); ok((new JIODate()) instanceof JIODate);
...@@ -47,17 +41,18 @@ ...@@ -47,17 +41,18 @@
}); });
test("By default, maximum precision is kept, but it can be changed later", function () { test("By default, maximum precision is kept, " +
var d = new JIODate(); "but it can be changed later", function () {
var d = new JIODate();
equal(d.getPrecision(), jiodate.MSEC); equal(d.getPrecision(), jiodate.MSEC);
d.setPrecision(jiodate.SEC); d.setPrecision(jiodate.SEC);
equal(d.getPrecision(), jiodate.SEC); equal(d.getPrecision(), jiodate.SEC);
d.setPrecision(jiodate.DAY); d.setPrecision(jiodate.DAY);
equal(d.getPrecision(), jiodate.DAY); equal(d.getPrecision(), jiodate.DAY);
d.setPrecision(jiodate.MONTH); d.setPrecision(jiodate.MONTH);
equal(d.getPrecision(), jiodate.MONTH); equal(d.getPrecision(), jiodate.MONTH);
}); });
test("Passing a JIODate object to the constructor clones it", function () { test("Passing a JIODate object to the constructor clones it", function () {
...@@ -222,69 +217,68 @@ ...@@ -222,69 +217,68 @@
}); });
test("Comparison between heterogeneous values is done with the lesser precision", function () { test("Comparison between heterogeneous values is done with " +
var dmsec = JIODate('2012-05-02 06:07:08.989'), "the lesser precision", function () {
dsec = JIODate('2012-05-02 06:07:08'), var dmsec = JIODate('2012-05-02 06:07:08.989'),
dmin = JIODate('2012-05-02 06:07'), dsec = JIODate('2012-05-02 06:07:08'),
dhour = JIODate('2012-05-02 06'), dmin = JIODate('2012-05-02 06:07'),
dday = JIODate('2012-05-02'), dhour = JIODate('2012-05-02 06'),
dmonth = JIODate('2012-05'), dday = JIODate('2012-05-02'),
dyear = JIODate('2012'); dmonth = JIODate('2012-05'),
dyear = JIODate('2012');
strictEqual(dmsec.cmp(dsec), 0);
strictEqual(dmsec.cmp(dmin), 0); strictEqual(dmsec.cmp(dsec), 0);
strictEqual(dmsec.cmp(dhour), 0); strictEqual(dmsec.cmp(dmin), 0);
strictEqual(dmsec.cmp(dday), 0); strictEqual(dmsec.cmp(dhour), 0);
strictEqual(dmsec.cmp(dmonth), 0); strictEqual(dmsec.cmp(dday), 0);
strictEqual(dmsec.cmp(dyear), 0); strictEqual(dmsec.cmp(dmonth), 0);
strictEqual(dmsec.cmp(dyear), 0);
strictEqual(dsec.cmp(dmsec), 0);
strictEqual(dsec.cmp(dmin), 0); strictEqual(dsec.cmp(dmsec), 0);
strictEqual(dsec.cmp(dhour), 0); strictEqual(dsec.cmp(dmin), 0);
strictEqual(dsec.cmp(dday), 0); strictEqual(dsec.cmp(dhour), 0);
strictEqual(dsec.cmp(dmonth), 0); strictEqual(dsec.cmp(dday), 0);
strictEqual(dsec.cmp(dyear), 0); strictEqual(dsec.cmp(dmonth), 0);
strictEqual(dsec.cmp(dyear), 0);
strictEqual(dmin.cmp(dmsec), 0);
strictEqual(dmin.cmp(dsec), 0); strictEqual(dmin.cmp(dmsec), 0);
strictEqual(dmin.cmp(dhour), 0); strictEqual(dmin.cmp(dsec), 0);
strictEqual(dmin.cmp(dday), 0); strictEqual(dmin.cmp(dhour), 0);
strictEqual(dmin.cmp(dmonth), 0); strictEqual(dmin.cmp(dday), 0);
strictEqual(dmin.cmp(dyear), 0); strictEqual(dmin.cmp(dmonth), 0);
strictEqual(dmin.cmp(dyear), 0);
strictEqual(dhour.cmp(dmsec), 0);
strictEqual(dhour.cmp(dsec), 0); strictEqual(dhour.cmp(dmsec), 0);
strictEqual(dhour.cmp(dmin), 0); strictEqual(dhour.cmp(dsec), 0);
strictEqual(dhour.cmp(dday), 0); strictEqual(dhour.cmp(dmin), 0);
strictEqual(dhour.cmp(dmonth), 0); strictEqual(dhour.cmp(dday), 0);
strictEqual(dhour.cmp(dyear), 0); strictEqual(dhour.cmp(dmonth), 0);
strictEqual(dhour.cmp(dyear), 0);
strictEqual(dday.cmp(dmsec), 0);
strictEqual(dday.cmp(dsec), 0); strictEqual(dday.cmp(dmsec), 0);
strictEqual(dday.cmp(dmin), 0); strictEqual(dday.cmp(dsec), 0);
strictEqual(dday.cmp(dhour), 0); strictEqual(dday.cmp(dmin), 0);
strictEqual(dday.cmp(dmonth), 0); strictEqual(dday.cmp(dhour), 0);
strictEqual(dday.cmp(dyear), 0); strictEqual(dday.cmp(dmonth), 0);
strictEqual(dday.cmp(dyear), 0);
strictEqual(dmonth.cmp(dmsec), 0);
strictEqual(dmonth.cmp(dsec), 0); strictEqual(dmonth.cmp(dmsec), 0);
strictEqual(dmonth.cmp(dmin), 0); strictEqual(dmonth.cmp(dsec), 0);
strictEqual(dmonth.cmp(dhour), 0); strictEqual(dmonth.cmp(dmin), 0);
strictEqual(dmonth.cmp(dday), 0); strictEqual(dmonth.cmp(dhour), 0);
strictEqual(dmonth.cmp(dyear), 0); strictEqual(dmonth.cmp(dday), 0);
strictEqual(dmonth.cmp(dyear), 0);
strictEqual(dyear.cmp(dmsec), 0);
strictEqual(dyear.cmp(dsec), 0); strictEqual(dyear.cmp(dmsec), 0);
strictEqual(dyear.cmp(dmin), 0); strictEqual(dyear.cmp(dsec), 0);
strictEqual(dyear.cmp(dhour), 0); strictEqual(dyear.cmp(dmin), 0);
strictEqual(dyear.cmp(dday), 0); strictEqual(dyear.cmp(dhour), 0);
strictEqual(dyear.cmp(dmonth), 0); strictEqual(dyear.cmp(dday), 0);
strictEqual(dyear.cmp(dmonth), 0);
strictEqual(dmsec.cmp(JIODate('2012-05-02 06:07:07')), +1);
strictEqual(dmsec.cmp(JIODate('2012-05-02 06:07:08')), 0); strictEqual(dmsec.cmp(JIODate('2012-05-02 06:07:07')), +1);
strictEqual(dmsec.cmp(JIODate('2012-05-02 06:07:09')), -1); strictEqual(dmsec.cmp(JIODate('2012-05-02 06:07:08')), 0);
}); strictEqual(dmsec.cmp(JIODate('2012-05-02 06:07:09')), -1);
});
})); }(QUnit, jiodate, moment));
/*jslint indent: 2, maxlen: 100, nomen: true */ /*jslint maxlen: 120, nomen: true */
/*global window, define, module, test_util, RSVP, jIO, local_storage, test, ok, /*global localStorage, test_util, console*/
deepEqual, sinon, expect, stop, start, Blob, console */ (function (jIO, localStorage, QUnit) {
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(RSVP, jIO, local_storage);
}([
'rsvp',
'jio',
'localstorage',
'qunit'
], function (RSVP, jIO, local_storage) {
"use strict"; "use strict";
var test = QUnit.test,
module("LocalStorage"); stop = QUnit.stop,
start = QUnit.start,
local_storage.clear(); ok = QUnit.ok,
expect = QUnit.expect,
var key_schema = { // deepEqual = QUnit.deepEqual,
module = QUnit.module,
key_schema;
key_schema = {
cast_lookup: { cast_lookup: {
dateType: function (obj) { dateType: function (obj) {
if (Object.prototype.toString.call(obj) === '[object Date]') { if (Object.prototype.toString.call(obj) === '[object Date]') {
...@@ -39,6 +29,17 @@ ...@@ -39,6 +29,17 @@
} }
}; };
module("localStorage", {
setup: function () {
localStorage.clear();
this.jio = jIO.createJIO({
"type": "local"
}, {
"workspace": {}
});
}
});
test("AllDocs", function () { test("AllDocs", function () {
expect(3); expect(3);
...@@ -72,147 +73,159 @@ ...@@ -72,147 +73,159 @@
jio.put({"_id": "b", "title": "two", "date": o.date_a}), jio.put({"_id": "b", "title": "two", "date": o.date_a}),
jio.put({"_id": "c", "title": "one", "date": o.date_b}), jio.put({"_id": "c", "title": "one", "date": o.date_b}),
jio.put({"_id": "d", "title": "two", "date": o.date_b}) jio.put({"_id": "d", "title": "two", "date": o.date_b})
]).then(function () { ])
// .then(function () {
// get a list of documents //
return jio.allDocs(); // // get a list of documents
// return jio.allDocs();
}).always(function (answer) { //
// })
// sort answer rows for comparison // .then(function (answer) {
if (answer.data && answer.data.rows) { //
answer.data.rows.sort(function (a, b) { // // sort answer rows for comparison
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; // if (answer.data && answer.data.rows) {
}); // answer.data.rows.sort(function (a, b) {
} // return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
// });
deepEqual(answer, { // }
"data": { //
"rows": [{ // deepEqual(answer, {
"id": "a", // "data": {
"key": "a", // "rows": [{
"value": {} // "id": "a",
}, { // "key": "a",
"id": "b", // "value": {}
"key": "b", // }, {
"value": {} // "id": "b",
}, { // "key": "b",
"id": "c", // "value": {}
"key": "c", // }, {
"value": {} // "id": "c",
}, { // "key": "c",
"id": "d", // "value": {}
"key": "d", // }, {
"value": {} // "id": "d",
}], // "key": "d",
"total_rows": 4 // "value": {}
}, // }],
"method": "allDocs", // "total_rows": 4
"result": "success", // },
"status": 200, // "method": "allDocs",
"statusText": "Ok" // "result": "success",
}, "AllDocs"); // "status": 200,
// "statusText": "Ok"
}).then(function () { // }, "AllDocs");
//
// get a list of documents // })
return jio.allDocs({ // .then(function () {
"include_docs": true, //
"sort_on": [['title', 'ascending'], ['date', 'descending']], // // get a list of documents
"select_list": ['title', 'date'], // return jio.allDocs({
"limit": [1] // ==> equal [1, 3] in this case // "include_docs": true,
// "sort_on": [['title', 'ascending'], ['date', 'descending']],
// "select_list": ['title', 'date'],
// "limit": [1] // ==> equal [1, 3] in this case
// });
//
// })
// .then(function (answer) {
//
// deepEqual(answer, {
// "data": {
// "rows": [{
// "doc": {
// "_attachments": {
// "aa": {
// "content_type": "",
// "digest": "sha256-9834876dcfb05cb167a5c24953eba58c4" +
// "ac89b1adf57f28f2f9d09af107ee8f0",
// "length": 3
// }
// },
// "_id": "a",
// "date": o.date_a.toJSON(),
// "title": "one"
// },
// "id": "a",
// "key": "a",
// "value": {
// "date": o.date_a.toJSON(),
// "title": "one"
// }
// }, {
// "doc": {
// "_id": "d",
// "date": o.date_b.toJSON(),
// "title": "two"
// },
// "id": "d",
// "key": "d",
// "value": {
// "date": o.date_b.toJSON(),
// "title": "two"
// }
// }, {
// "doc": {
// "_id": "b",
// "date": o.date_a.toJSON(),
// "title": "two"
// },
// "id": "b",
// "key": "b",
// "value": {
// "date": o.date_a.toJSON(),
// "title": "two"
// }
// }],
// "total_rows": 3
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "AllDocs include docs + sort on + limit + select_list");
//
// })
// .then(function () {
//
// // use a query
// return jio.allDocs({'query': {
// type: 'simple',
// key: 'mydate',
// operator: '=',
// value: o.date_a.toString()
// }});
//
// })
// .then(function (answer) {
//
// deepEqual(answer, {
// "data": {
// "rows": [{
// "id": "a",
// "key": "a",
// "value": {}
// }, {
// "id": "b",
// "key": "b",
// "value": {}
// }],
// "total_rows": 2
// },
// "method": "allDocs",
// "result": "success",
// "status": 200,
// "statusText": "Ok"
// }, "AllDocs sort on + query");
//
// })
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
}); });
}).always(function (answer) {
deepEqual(answer, {
"data": {
"rows": [{
"doc": {
"_attachments": {
"aa": {
"content_type": "",
"digest": "sha256-9834876dcfb05cb167a5c24953eba58c4" +
"ac89b1adf57f28f2f9d09af107ee8f0",
"length": 3
}
},
"_id": "a",
"date": o.date_a.toJSON(),
"title": "one"
},
"id": "a",
"key": "a",
"value": {
"date": o.date_a.toJSON(),
"title": "one"
}
}, {
"doc": {
"_id": "d",
"date": o.date_b.toJSON(),
"title": "two"
},
"id": "d",
"key": "d",
"value": {
"date": o.date_b.toJSON(),
"title": "two"
}
}, {
"doc": {
"_id": "b",
"date": o.date_a.toJSON(),
"title": "two"
},
"id": "b",
"key": "b",
"value": {
"date": o.date_a.toJSON(),
"title": "two"
}
}],
"total_rows": 3
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "AllDocs include docs + sort on + limit + select_list");
}).then(function () {
// use a query
return jio.allDocs({'query': {
type: 'simple',
key: 'mydate',
operator: '=',
value: o.date_a.toString()
}});
}).always(function (answer) {
deepEqual(answer, {
"data": {
"rows": [{
"id": "a",
"key": "a",
"value": {}
}, {
"id": "b",
"key": "b",
"value": {}
}],
"total_rows": 2
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "AllDocs sort on + query");
}).always(start);
}); });
})); }(jIO, localStorage, QUnit));
...@@ -2,44 +2,40 @@ ...@@ -2,44 +2,40 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="stylesheet" href="../lib/qunit/qunit.css" />
<title>JIO Qunit/Sinon Unit Tests</title> <title>JIO Qunit/Sinon Unit Tests</title>
</head> <link rel="stylesheet" href="../node_modules/grunt-contrib-qunit/test/libs/qunit.css" type="text/css" media="screen"/>
<body> <script src="../node_modules/grunt-contrib-qunit/test/libs/qunit.js" type="text/javascript"></script>
<div id="qunit"></div> <script src="../node_modules/rsvp/dist/rsvp-2.0.4.js"></script>
<script src="../node_modules/sinon/pkg/sinon.js" type="text/javascript"></script>
<script src="../dist/jio-latest.js"></script>
<script src="html5.js"></script> <script src="html5.js"></script>
<script src="../lib/qunit/qunit.js"></script> <!--script src="jio/util.js"></script-->
<script src="../lib/sinon/sinon.js"></script> <!--script src="jio/fakestorage.js"></script>
<script src="../lib/sinon/sinon-qunit.js"></script> <script src="jio/tests.js"></script-->
<script src="../lib/rsvp/rsvp-custom.js"></script>
<script src="../src/sha256.amd.js"></script>
<script src="../jio.js"></script>
<script src="jio/util.js"></script>
<script src="jio/fakestorage.js"></script>
<script src="jio/tests.js"></script>
<script src="queries/key.tests.js"></script> <script src="queries/key.tests.js"></script>
<script src="queries/key-schema.tests.js"></script> <script src="queries/key-schema.tests.js"></script>
<script src="queries/tests.js"></script> <script src="queries/tests.js"></script>
<script src="queries/key-typechecks.tests.js"></script> <script src="queries/key-typechecks.tests.js"></script>
<script src="../lib/moment/moment-2.5.0.js"></script>
<script src="../src/jio.date/jiodate.js"></script>
<script src="queries/jiodate.tests.js"></script> <script src="queries/jiodate.tests.js"></script>
<script src="queries/key-jiodate.tests.js"></script> <script src="queries/key-jiodate.tests.js"></script>
<script src="../src/jio.storage/localstorage.js"></script>
<script src="queries/key-localstorage.tests.js"></script> <script src="queries/key-localstorage.tests.js"></script>
<script src="jio.storage/localstorage.tests.js"></script> <script src="jio.storage/localstorage.tests.js"></script>
<script src="jio.storage/davstorage.tests.js"></script>
<script src="jio.storage/indexeddbstorage.tests.js"></script>
<script src="jio.storage/unionstorage.tests.js"></script>
<script src="jio.storage/querystorage.tests.js"></script>
<script src="../lib/jquery/jquery.min.js"></script> <!--script src="../lib/jquery/jquery.min.js"></script>
<script src="../src/jio.storage/xwikistorage.js"></script> <script src="../src/jio.storage/xwikistorage.js"></script>
<script src="jio.storage/xwikistorage.tests.js"></script> <script src="jio.storage/xwikistorage.tests.js"></script-->
<script src="../src/jio.storage/davstorage.js"></script>
<script src="jio.storage/davstorage.tests.js"></script>
<script src="../src/jio.storage/indexstorage.js"></script> <!--script src="../src/jio.storage/indexstorage.js"></script>
<script src="jio.storage/indexstorage.tests.js"></script> <script src="jio.storage/indexstorage.tests.js"></script>
<script src="../src/jio.storage/gidstorage.js"></script> <script src="../src/jio.storage/gidstorage.js"></script>
...@@ -54,18 +50,16 @@ ...@@ -54,18 +50,16 @@
<script src="../src/jio.storage/splitstorage.js"></script> <script src="../src/jio.storage/splitstorage.js"></script>
<script src="../test/jio.storage/splitstorage.tests.js"></script> <script src="../test/jio.storage/splitstorage.tests.js"></script>
<script src="../src/jio.storage/querystorage.js"></script>
<script src="../test/jio.storage/querystorage.tests.js"></script>
<script src="../src/jio.storage/replicatestorage.js"></script> <script src="../src/jio.storage/replicatestorage.js"></script>
<script src="../test/jio.storage/replicatestorage.tests.js"></script> <script src="../test/jio.storage/replicatestorage.tests.js"></script-->
</head>
<!-- <body>
<script src="../src/jio.storage/indexeddbstorage.js"></script> <h1 id="qunit-header">jIO Tests</h1>
<script src="../test/jio.storage/indexeddbstorage.tests.js"></script> <h2 id="qunit-banner"></h2>
--> <div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body> </body>
</html> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>JIO Qunit/Sinon Unit Tests</title>
<script src="../lib/require/require.js"></script>
<script src="tests.require.js"></script>
<link rel="stylesheet" href="../lib/qunit/qunit.css" />
</head>
<body>
<div id="qunit"></div>
</body>
</html>
/*jslint indent: 2, maxlen: 80, nomen : true */
/*global require */
(function () {
"use strict";
require.config({
"paths": {
"rsvp": "../lib/rsvp/rsvp-custom.amd",
"sha256": "../src/sha256.amd",
"jio": "../jio",
"jio_tests": "jio/tests",
"test_util": "jio/util",
"fakestorage": "jio/fakestorage",
"jioquery_tests": "queries/tests",
"localstorage": "../src/jio.storage/localstorage",
"localstorage_tests": "jio.storage/localstorage.tests",
"davstorage": "../src/jio.storage/davstorage",
"davstorage_tests": "jio.storage/davstorage.tests",
"indexstorage": "../src/jio.storage/indexstorage",
"indexstorage_tests": "jio.storage/indexstorage.tests",
"gidstorage": "../src/jio.storage/gidstorage",
"gidstorage_tests": "jio.storage/gidstorage.tests",
"revisionstorage": "../src/jio.storage/revisionstorage",
"revisionstorage_tests": "jio.storage/revisionstorage.tests",
"replicaterevisionstorage":
"../src/jio.storage/replicaterevisionstorage",
"replicaterevisionstorage_tests":
"jio.storage/replicaterevisionstorage.tests",
"splitstorage": "../src/jio.storage/splitstorage",
"splitstorage_tests": "jio.storage/splitstorage.tests",
"replicatestorage": "../src/jio.storage/replicatestorage",
"replicatestorage_tests": "jio.storage/replicatestorage.tests",
"qunit": "../lib/qunit/qunit",
"sinon": "../lib/sinon/sinon",
"sinon_qunit": "../lib/sinon/sinon-qunit"
},
"shim": {
"sinon": ["qunit"],
"sinon_qunit": ["sinon"]
}
});
require([
"sinon_qunit",
"jio_tests",
"jioquery_tests",
"localstorage_tests",
"davstorage_tests",
"indexstorage_tests",
"gidstorage_tests",
"revisionstorage_tests",
"replicaterevisionstorage_tests",
"splitstorage_tests",
"replicatestorage_tests"
]);
}());
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment