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) {
"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.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jslint: {
npm: {
src: ['package.json'],
directives: {
maxlen: 100,
indent: 2,
maxerr: 3,
predef: [
'module'
]
}
},
grunt: {
src: ['Gruntfile.js'],
options: {
errorsOnly: true
directives: {
es5: true,
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'module',
'require'
]
}
},
jio: {
src: ['src/jio/**/*.js'],
src: ['src/jio.js', 'src/jio/**/*.js', 'src/jio/*.js'],
exclude: ['src/jio/intro.js', 'src/jio/outro.js'],
options: {
errorsOnly: true
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
nomen: true
}
},
jio_storages: {
src: ['src/jio.storage/**/*.js'],
options: {
errorsOnly: true
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'define',
'exports',
'require',
'window',
'jIO',
'complex_queries'
]
}
},
jiodate: {
src: ['src/jio.date/jiodate.js']
src: ['src/jio.date/jiodate.js'],
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'jIO'
]
},
},
tests: {
src: ['test/**/*.js'],
options: {
errorsOnly: true
}
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'RSVP',
'QUnit',
'jIO'
]
},
},
queries: {
src: ['src/queries/core/**/*.js'],
......@@ -47,19 +120,40 @@ module.exports = function (grunt) {
options: {
errorsOnly: true
}
}
},
examples: {
src: ['examples/*.js'],
directives: {
maxlen: 80,
indent: 2,
maxerr: 3,
predef: [
'window',
'RSVP',
'rJS',
'QUnit',
'jIO'
]
},
},
},
concat: {
options: {
separator: ';'
},
jio: {
// duplicate files are ignored
src: [
'src/jio/intro.js',
// core
'src/jio/core/globals.js',
'src/jio/core/util.js',
'src/jio/core/**/*.js',
'src/jio/features/**/*.js',
// 'node_modules/moment/moment.js',
'lib/moment/moment-2.5.0.js',
// 'src/*.js',
// 'src/jio/intro.js',
//
// // core
// 'src/jio/core/globals.js',
// 'src/jio/core/util.js',
// 'src/jio/core/**/*.js',
// 'src/jio/features/**/*.js',
// queries
'src/queries/core/globals.js',
......@@ -70,9 +164,19 @@ module.exports = function (grunt) {
'src/queries/core/tools.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: {
......@@ -81,37 +185,91 @@ module.exports = function (grunt) {
'<%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
jio: {
src: 'jio.js', // '<%= pkg.name %>.js'
dest: 'jio.min.js',
src: "<%= concat.jio.dest %>",
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: {
sourceMap: "jio.min.map"
livereload: LIVERELOAD_PORT
}
},
jiodate: {
src: 'src/jio.date/jiodate.js',
dest: 'jiodate.min.js',
}
},
connect: {
client: {
options: {
sourceMap: "jiodate.min.map"
port: 9000,
base: '.',
directory: '.',
middleware: livereloadMiddleware
}
}
},
qunit: {
// grunt doesn't like requirejs
files: ['test/tests.html'],
options: {
timeout: 30000 // if no test occurs for 30 seconds, then timeout
open: {
all: {
// Gets the port from the connect configuration
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('build', ['concat', 'uglify']);
grunt.registerTask('test', ['jslint', 'qunit']);
grunt.registerTask('test', ['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",
"version": "v2.0.0",
"version": "v3.0.0",
"license": "LGPLv3",
"author": "Nexedi SA",
"contributors": [
......@@ -14,7 +14,9 @@
"test": "test"
},
"scripts": {
"test": "grunt test"
"test": "./node_modules/.bin/grunt test",
"lint": "./node_modules/.bin/grunt lint",
"prepublish": "./node_modules/.bin/grunt build"
},
"repository": {
"type": "git",
......@@ -26,13 +28,25 @@
"output",
"cloud"
],
"dependencies": {
"rsvp": "git+http://git.erp5.org/repos/rsvp.js.git",
"moment": "2.8.3"
},
"devDependencies": {
"renderjs": "git+http://git.erp5.org/repos/renderjs.git",
"jscc-node": "0.3.x",
"grunt": "0.4.x",
"grunt-cli": "~0.1.11",
"grunt-contrib-concat": "0.3.x",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-uglify": "0.2.x",
"grunt-contrib-qunit": "0.2.x",
"grunt-jslint": "1.0.x"
"grunt-contrib-qunit": "~0.3.0",
"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": {
"npm": ">=1.3"
......
/*jslint indent: 2, nomen: true */
/*global module, define, exports, 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) {
/*global window, moment */
/*jslint nomen: true, maxlen: 200*/
(function (window, moment) {
"use strict";
/**
* Add a secured (write permission denied) property to an object.
*
* @param {Object} object The object to fill
* @param {String} key The object key where to store the property
* @param {Any} value The value to store
*/
function _export(key, value) {
Object.defineProperty(to_export, key, {
"configurable": false,
"enumerable": true,
"writable": false,
"value": value
});
}
// /**
// * Add a secured (write permission denied) property to an object.
// *
// * @param {Object} object The object to fill
// * @param {String} key The object key where to store the property
// * @param {Any} value The value to store
// */
// function _export(key, value) {
// Object.defineProperty(to_export, key, {
// "configurable": false,
// "enumerable": true,
// "writable": false,
// "value": value
// });
// }
var YEAR = 'year',
MONTH = 'month',
......@@ -50,7 +38,7 @@
lesserPrecision = function (p1, p2) {
return (precision_grade[p1] < precision_grade[p2]) ? p1 : p2;
},
JIODate = null;
JIODate;
JIODate = function (str) {
......@@ -161,17 +149,24 @@
};
_export('JIODate', JIODate);
_export('YEAR', YEAR);
_export('MONTH', MONTH);
_export('DAY', DAY);
_export('HOUR', HOUR);
_export('MIN', MIN);
_export('SEC', SEC);
_export('MSEC', MSEC);
return to_export;
}));
// _export('JIODate', JIODate);
//
// _export('YEAR', YEAR);
// _export('MONTH', MONTH);
// _export('DAY', DAY);
// _export('HOUR', HOUR);
// _export('MIN', MIN);
// _export('SEC', SEC);
// _export('MSEC', MSEC);
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 */
/*global jIO: true, sjcl: true, $: true, setTimeout: true */
jIO.addStorageType('crypt', function (spec, my) {
jIO.addStorage('crypt', function (spec, my) {
/*jslint todo: true*/
spec = spec || {};
var that = my.basicStorage(spec, my),
......
......@@ -4,8 +4,8 @@
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint indent: 2, maxlen: 80, nomen: true, regexp: true, unparam: true */
/*global define, window, jIO, RSVP, btoa, DOMParser, Blob */
/*jslint indent: 2, maxlen: 200, nomen: true, regexp: true, unparam: true */
/*global define, window, jIO, RSVP, btoa, DOMParser, Blob, console */
// JIO Dav Storage Description :
// {
......@@ -172,14 +172,6 @@
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
*/
......@@ -204,7 +196,7 @@
"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};
if (start !== undefined) {
if (end !== undefined) {
......@@ -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) {
return ajax[this._auth_type](
"PUT",
null,
this._url + '/' + idsToFileName(param._id, param._attachment) +
"?_=" + Date.now(),
this._url + '/' + idsToFileName(param._id, param._attachment),
param._blob,
undefined,
undefined,
......@@ -273,29 +255,36 @@
);
};
DavStorage.prototype._get = function (param) {
return ajax[this._auth_type](
"GET",
"text",
this._url + '/' + idsToFileName(param._id),
null,
undefined,
undefined,
this._login
).then(function (e) {
try {
return {"target": {
"status": e.target.status,
"statusText": e.target.statusText,
"response": JSON.parse(e.target.responseText)
}};
} catch (err) {
throw {"target": {
"status": 0,
"statusText": "Parse error"
}};
}
});
/**
* Retrieve metadata
*
* @method get
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.get = function (param) {
var context = this;
return new RSVP.Queue()
.push(function () {
return ajax[context._auth_type](
"GET",
"text",
context._url + '/' + idsToFileName(param._id),
null,
context._login
);
})
.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) {
......@@ -314,7 +303,7 @@
return ajax[this._auth_type](
"DELETE",
null,
this._url + '/' + idsToFileName(param._id) + "?_=" + Date.now(),
this._url + '/' + idsToFileName(param._id),
null,
undefined,
undefined,
......@@ -326,8 +315,7 @@
return ajax[this._auth_type](
"DELETE",
null,
this._url + '/' + idsToFileName(param._id, param._attachment) +
"?_=" + Date.now(),
this._url + '/' + idsToFileName(param._id, param._attachment),
null,
undefined,
undefined,
......@@ -411,79 +399,34 @@
// adding custom headers triggers preflight OPTIONS request!
// 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
*
* @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to put
* @param {Object} options The command options
*/
DavStorage.prototype.post = function (command, metadata) {
this.postOrPut('post', command, metadata);
DavStorage.prototype.post = function (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 @@
* Creates or updates a document
*
* @method put
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to post
* @param {Object} options The command options
*/
DavStorage.prototype.put = function (command, metadata) {
this.postOrPut('put', command, metadata);
DavStorage.prototype.put = function (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
*
* @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} options The command options
*/
DavStorage.prototype.get = function (command, param) {
var o = {
notifyGetProgress: function (e) {
command.notify({
"method": "get",
"message": "Getting metadata",
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) * 100 // 0% to 100%
});
},
success: function (e) {
command.success(e.target.status, {"data": e.target.response});
},
reject: function (e) {
command.reject(
e.target.status,
e.target.statusText,
"DavStorage, unable to get document."
);
}
};
// DavStorage.prototype.putAttachment = function (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);
// };
this._get(param).then(o.success, o.reject, o.notifyGetProgress);
};
/**
* Retriev a document attachment
*
* @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.getAttachment = function (command, param) {
var that = this, o = {
error_message: "DavStorage, unable to get attachment.",
percentage: [0, 30],
notify_message: "Getting metedata",
"404": "missing document", // Not Found
notifyProgress: function (e) {
command.notify({
"method": "getAttachment",
"message": o.notify_message,
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) +
o.percentage[0]
});
},
getAttachment: function (e) {
var attachment = e.target.response._attachments &&
e.target.response._attachments[param._attachment];
delete o["404"];
if (typeof attachment !== 'object' || attachment === null) {
throw {"target": {
"status": 404,
"statusText": "missing attachment"
}};
}
o.type = attachment.content_type || "application/octet-stream";
o.notify_message = "Retrieving attachment";
o.percentage = [30, 100];
o.digest = attachment.digest;
return that._getAttachment(param);
},
success: function (e) {
command.success(e.target.status, {
"data": new Blob([e.target.response], {"type": o.type}),
"digest": o.digest
});
},
reject: function (e) {
command.reject(
e.target.status,
o[e.target.status] || e.target.statusText,
o.error_message
);
}
};
if (param._start < 0 || param._end < 0) {
command.reject(405,
"invalide _start,_end",
"_start and _end must be positive");
return;
}
if (param._start > param._end) {
command.reject(405,
"invalide _start,_end",
"start is great then end");
return;
}
this._get(param).
then(o.getAttachment).
then(o.success, o.reject, o.notifyProgress);
};
// DavStorage.prototype.getAttachment = function (param) {
// var that = this, o = {
// error_message: "DavStorage, unable to get attachment.",
// percentage: [0, 30],
// notify_message: "Getting metedata",
// "404": "missing document", // Not Found
// notifyProgress: function (e) {
// command.notify({
// "method": "getAttachment",
// "message": o.notify_message,
// "loaded": e.loaded,
// "total": e.total,
// "percentage": (e.loaded / e.total) *
// (o.percentage[1] - o.percentage[0]) +
// o.percentage[0]
// });
// },
// getAttachment: function (e) {
// var attachment = e.target.response._attachments &&
// e.target.response._attachments[param._attachment];
// delete o["404"];
// if (typeof attachment !== 'object' || attachment === null) {
// throw {"target": {
// "status": 404,
// "statusText": "missing attachment"
// }};
// }
// o.type = attachment.content_type || "application/octet-stream";
// o.notify_message = "Retrieving attachment";
// o.percentage = [30, 100];
// o.digest = attachment.digest;
// return that._getAttachment(param);
// },
// success: function (e) {
// command.success(e.target.status, {
// "data": new Blob([e.target.response], {"type": o.type}),
// "digest": o.digest
// });
// },
// reject: function (e) {
// command.reject(
// e.target.status,
// o[e.target.status] || e.target.statusText,
// o.error_message
// );
// }
// };
// if (param._start < 0 || param._end < 0) {
// command.reject(405,
// "invalide _start,_end",
// "_start and _end must be positive");
// return;
// }
// if (param._start > param._end) {
// command.reject(405,
// "invalide _start,_end",
// "start is great then end");
// return;
// }
// this._get(param).
// then(o.getAttachment).
// then(o.success, o.reject, o.notifyProgress);
// };
/**
* Remove a document
*
* @method remove
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.remove = function (command, param) {
var that = this, o = {
error_message: "DavStorage, unable to get metadata.",
notify_message: "Getting metadata",
percentage: [0, 70],
notifyProgress: function (e) {
if (e === null) {
return;
}
command.notify({
"method": "remove",
"message": o.notify_message,
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) + o.percentage[0]
});
},
removeDocument: function (e) {
o.get_result = e;
o.percentage = [70, 80];
o.notify_message = "Removing document";
o.error_message = "DavStorage, unable to remove document";
return that._remove(param);
},
removeAllAttachments: function (e) {
var k, requests = [], attachments;
attachments = o.get_result.target.response._attachments;
o.remove_result = e;
if (typeof attachments === 'object' && attachments !== null) {
for (k in attachments) {
if (attachments.hasOwnProperty(k)) {
requests[requests.length] = promiseSucceed(
that._removeAttachment({
"_id": param._id,
"_attachment": k
})
);
}
}
}
if (requests.length === 0) {
return;
}
o.count = 0;
o.nb_requests = requests.length;
return RSVP.all(requests).then(null, null, function (e) {
if (e.value.loaded === e.value.total) {
o.count += 1;
command.notify({
"method": "remove",
"message": "Removing all associated attachments",
"loaded": o.count,
"total": o.nb_requests,
"percentage": Math.min(
o.count / o.nb_requests * 20 + 80,
100
)
});
}
return null;
});
},
success: function () {
command.success(o.remove_result.target.status);
},
reject: function (e) {
return command.reject(
e.target.status,
e.target.statusText,
o.error_message
);
}
};
this._get(param).
then(o.removeDocument).
then(o.removeAllAttachments).
then(o.success, o.reject, o.notifyProgress);
};
// DavStorage.prototype.remove = function (param) {
// var that = this, o = {
// error_message: "DavStorage, unable to get metadata.",
// notify_message: "Getting metadata",
// percentage: [0, 70],
// notifyProgress: function (e) {
// if (e === null) {
// return;
// }
// command.notify({
// "method": "remove",
// "message": o.notify_message,
// "loaded": e.loaded,
// "total": e.total,
// "percentage": (e.loaded / e.total) *
// (o.percentage[1] - o.percentage[0]) + o.percentage[0]
// });
// },
// removeDocument: function (e) {
// o.get_result = e;
// o.percentage = [70, 80];
// o.notify_message = "Removing document";
// o.error_message = "DavStorage, unable to remove document";
// return that._remove(param);
// },
// removeAllAttachments: function (e) {
// var k, requests = [], attachments;
// attachments = o.get_result.target.response._attachments;
// o.remove_result = e;
// if (typeof attachments === 'object' && attachments !== null) {
// for (k in attachments) {
// if (attachments.hasOwnProperty(k)) {
// requests[requests.length] = promiseSucceed(
// that._removeAttachment({
// "_id": param._id,
// "_attachment": k
// })
// );
// }
// }
// }
// if (requests.length === 0) {
// return;
// }
// o.count = 0;
// o.nb_requests = requests.length;
// return RSVP.all(requests).then(null, null, function (e) {
// if (e.value.loaded === e.value.total) {
// o.count += 1;
// command.notify({
// "method": "remove",
// "message": "Removing all associated attachments",
// "loaded": o.count,
// "total": o.nb_requests,
// "percentage": Math.min(
// o.count / o.nb_requests * 20 + 80,
// 100
// )
// });
// }
// return null;
// });
// },
// success: function () {
// command.success(o.remove_result.target.status);
// },
// reject: function (e) {
// return command.reject(
// e.target.status,
// e.target.statusText,
// o.error_message
// );
// }
// };
//
// this._get(param).
// then(o.removeDocument).
// then(o.removeAllAttachments).
// then(o.success, o.reject, o.notifyProgress);
// };
/**
* Remove an attachment
*
* @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.removeAttachment = function (command, param) {
var that = this, o = {
error_message: "DavStorage, an error occured while getting metadata.",
percentage: [0, 40],
notify_message: "Getting metadata",
notifyProgress: function (e) {
command.notify({
"method": "remove",
"message": o.notify_message,
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) *
(o.percentage[1] - o.percentage[0]) +
o.percentage[0]
});
},
updateMetadata: function (e) {
var k, doc = e.target.response, attachment;
attachment = doc._attachments && doc._attachments[param._attachment];
o.error_message = "DavStorage, document attachment not found.";
if (typeof attachment !== 'object' || attachment === null) {
throw {"target": {
"status": 404,
"statusText": "missing attachment"
}};
}
delete doc._attachments[param._attachment];
for (k in doc._attachments) {
if (doc._attachments.hasOwnProperty(k)) {
break;
}
}
if (k === undefined) {
delete doc._attachments;
}
o.percentage = [40, 80];
o.notify_message = "Updating metadata";
o.error_message = "DavStorage, an error occured " +
"while updating metadata.";
return that._put(doc);
},
removeAttachment: function () {
o.percentage = [80, 100];
o.notify_message = "Removing attachment";
o.error_message = "DavStorage, an error occured " +
"while removing attachment.";
return that._removeAttachment(param);
},
success: function (e) {
command.success(e.status);
},
reject: function (e) {
return command.reject(
e.target.status,
e.target.statusText,
o.error_message
);
}
};
this._get(param).
then(o.updateMetadata).
then(o.removeAttachment).
then(o.success, o.reject, o.notifyProgress);
};
// DavStorage.prototype.removeAttachment = function (param) {
// var that = this, o = {
// error_message: "DavStorage, an error occured while getting metadata.",
// percentage: [0, 40],
// notify_message: "Getting metadata",
// notifyProgress: function (e) {
// command.notify({
// "method": "remove",
// "message": o.notify_message,
// "loaded": e.loaded,
// "total": e.total,
// "percentage": (e.loaded / e.total) *
// (o.percentage[1] - o.percentage[0]) +
// o.percentage[0]
// });
// },
// updateMetadata: function (e) {
// var k, doc = e.target.response, attachment;
// attachment = doc._attachments && doc._attachments[param._attachment];
// o.error_message = "DavStorage, document attachment not found.";
// if (typeof attachment !== 'object' || attachment === null) {
// throw {"target": {
// "status": 404,
// "statusText": "missing attachment"
// }};
// }
// delete doc._attachments[param._attachment];
// for (k in doc._attachments) {
// if (doc._attachments.hasOwnProperty(k)) {
// break;
// }
// }
// if (k === undefined) {
// delete doc._attachments;
// }
// o.percentage = [40, 80];
// o.notify_message = "Updating metadata";
// o.error_message = "DavStorage, an error occured " +
// "while updating metadata.";
// return that._put(doc);
// },
// removeAttachment: function () {
// o.percentage = [80, 100];
// o.notify_message = "Removing attachment";
// o.error_message = "DavStorage, an error occured " +
// "while removing attachment.";
// return that._removeAttachment(param);
// },
// success: function (e) {
// command.success(e.status);
// },
// reject: function (e) {
// return command.reject(
// e.target.status,
// e.target.statusText,
// o.error_message
// );
// }
// };
//
// this._get(param).
// then(o.updateMetadata).
// then(o.removeAttachment).
// then(o.success, o.reject, o.notifyProgress);
// };
/**
* Retrieve a list of present document
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
* @param {Boolean} [options.include_docs=false]
* Also retrieve the actual document content.
*/
DavStorage.prototype.allDocs = function (command, param, options) {
var that = this, o = {
error_message: "DavStorage, an error occured while " +
"retrieving document list",
max_percentage: options.include_docs === true ? 20 : 100,
notifyAllDocsProgress: function (e) {
command.notify({
"method": "remove",
"message": "Retrieving document list",
"loaded": e.loaded,
"total": e.total,
"percentage": (e.loaded / e.total) * o.max_percentage
});
},
getAllMetadataIfNecessary: function (e) {
var requests = [];
o.alldocs_result = e;
if (options.include_docs !== true ||
e.target.response.rows.length === 0) {
return;
}
e.target.response.rows.forEach(function (row) {
if (row.id !== "") {
requests[requests.length] = that._get({"_id": row.id}).
then(function (e) {
row.doc = e.target.response;
});
}
});
o.count = 0;
o.nb_requests = requests.length;
o.error_message = "DavStorage, an error occured while " +
"getting document metadata";
return RSVP.all(requests).then(null, null, function (e) {
if (e.value.loaded === e.value.total) {
o.count += 1;
command.notify({
"method": "allDocs",
"message": "Getting all documents metadata",
"loaded": o.count,
"total": o.nb_requests,
"percentage": Math.min(
o.count / o.nb_requests * 80 + 20,
100
)
});
}
throw null;
});
},
success: function () {
command.success(o.alldocs_result.target.status, {
"data": o.alldocs_result.target.response
});
},
reject: function (e) {
return command.reject(
e.target.status,
e.target.statusText,
o.error_message
);
}
};
this._allDocs(param, options).
then(o.getAllMetadataIfNecessary).
then(o.success, o.reject, o.notifyProgress);
};
// DavStorage.prototype.allDocs = function (param, options) {
// var that = this, o = {
// error_message: "DavStorage, an error occured while " +
// "retrieving document list",
// max_percentage: options.include_docs === true ? 20 : 100,
// notifyAllDocsProgress: function (e) {
// command.notify({
// "method": "remove",
// "message": "Retrieving document list",
// "loaded": e.loaded,
// "total": e.total,
// "percentage": (e.loaded / e.total) * o.max_percentage
// });
// },
// getAllMetadataIfNecessary: function (e) {
// var requests = [];
// o.alldocs_result = e;
// if (options.include_docs !== true ||
// e.target.response.rows.length === 0) {
// return;
// }
//
// e.target.response.rows.forEach(function (row) {
// if (row.id !== "") {
// requests[requests.length] = that._get({"_id": row.id}).
// then(function (e) {
// row.doc = e.target.response;
// });
// }
// });
//
// o.count = 0;
// o.nb_requests = requests.length;
// o.error_message = "DavStorage, an error occured while " +
// "getting document metadata";
// return RSVP.all(requests).then(null, null, function (e) {
// if (e.value.loaded === e.value.total) {
// o.count += 1;
// command.notify({
// "method": "allDocs",
// "message": "Getting all documents metadata",
// "loaded": o.count,
// "total": o.nb_requests,
// "percentage": Math.min(
// o.count / o.nb_requests * 80 + 20,
// 100
// )
// });
// }
// throw null;
// });
// },
// success: function () {
// command.success(o.alldocs_result.target.status, {
// "data": o.alldocs_result.target.response
// });
// },
// reject: function (e) {
// return command.reject(
// e.target.status,
// e.target.statusText,
// o.error_message
// );
// }
// };
//
// this._allDocs(param, options).
// then(o.getAllMetadataIfNecessary).
// then(o.success, o.reject, o.notifyProgress);
// };
/**
* Check the storage or a specific document
*
* @method check
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.check = function (command, param) {
this.genericRepair(command, param, false);
};
// DavStorage.prototype.check = function (param) {
// this.genericRepair(param, false);
// };
/**
* Repair the storage or a specific document
*
* @method repair
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
DavStorage.prototype.repair = function (command, param) {
this.genericRepair(command, param, true);
};
// DavStorage.prototype.repair = function (param) {
// this.genericRepair(param, true);
// };
/**
* A generic method that manage check or repair command
*
* @method genericRepair
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Boolean} repair If true then repair else just check
*/
DavStorage.prototype.genericRepair = function (command, param, repair) {
var that = this, repair_promise;
// returns a jio object
function getAllFile() {
return ajax[that._auth_type](
"PROPFIND",
"text",
that._url + '/',
null,
that._login
).then(function (e) { // on success
var i, length, rows = new DOMParser().parseFromString(
e.target.responseText,
"text/xml"
).querySelectorAll(
"D\\:response, response"
);
if (rows.length === 1) {
return {"status": 200, "data": []};
}
// exclude parent folder and browse
rows = [].slice.call(rows);
rows.shift();
length = rows.length;
for (i = 0; i < length; i += 1) {
rows[i] = rows[i].querySelector("D\\:href, href").
textContent.split('/').slice(-1)[0];
}
return {"data": rows, "status": 200};
// rows -> [
// 'file_path_1',
// ...
// ]
}, function (e) { // on error
// convert into jio error object
// then propagate
throw {"status": e.target.status,
"reason": e.target.statusText};
});
}
// returns jio object
function repairOne(shared, repair) {
var modified = false, document_id = shared._id;
return that._get({"_id": document_id}).then(function (event) {
var attachment_id, metadata = event.target.response;
// metadata should be an object
if (typeof metadata !== 'object' || metadata === null ||
Array.isArray(metadata)) {
if (!repair) {
throw {
"status": "conflict",
"reason": "corrupted",
"message": "Bad metadata found in document \"" +
document_id + "\""
};
}
return {};
}
// check metadata content
if (!repair) {
if (!(new jIO.Metadata(metadata).check())) {
return {
"status": "conflict",
"reason": "corrupted",
"message": "Some metadata might be lost"
};
}
} else {
modified = (
jIO.util.uniqueJSONStringify(metadata) !==
jIO.util.uniqueJSONStringify(
new jIO.Metadata(metadata).format()._dict
)
);
}
// check metadata id
if (metadata._id !== document_id) {
// metadata id is different than file
// this is not a critical thing
modified = true;
metadata._id = document_id;
}
// check attachment metadata container
if (metadata._attachments &&
(typeof metadata._attachments !== 'object' ||
Array.isArray(metadata._attachments))) {
// is not undefined nor object
if (!repair) {
throw {
"status": "conflict",
"reason": "corrupted",
"message": "Bad attachment metadata found in document \"" +
document_id + "\""
};
}
delete metadata._attachments;
modified = true;
}
// check every attachment metadata
if (metadata._attachments) {
for (attachment_id in metadata._attachments) {
if (metadata._attachments.hasOwnProperty(attachment_id)) {
// check attachment metadata type
if (typeof metadata._attachments[attachment_id] !== 'object' ||
metadata._attachments[attachment_id] === null ||
Array.isArray(metadata._attachments[attachment_id])) {
// is not object
if (!repair) {
throw {
"status": "conflict",
"reason": "corrupted",
"message": "Bad attachment metadata found in document \"" +
document_id + "\", attachment \"" +
attachment_id + "\""
};
}
metadata._attachments[attachment_id] = {};
modified = true;
}
// check attachment existency if all attachment are listed
if (shared.referenced_dict) {
if (shared.unreferenced_dict[metadata._id] &&
shared.unreferenced_dict[metadata._id][attachment_id]) {
// attachment seams to exist but is not referenced
shared.referenced_dict[metadata._id] =
shared.referenced_dict[metadata._id] || {};
shared.referenced_dict[metadata._id][attachment_id] = true;
delete shared.unreferenced_dict[metadata._id][attachment_id];
} else if (
!(shared.referenced_dict[metadata._id] &&
shared.referenced_dict[metadata._id][attachment_id])
) {
// attachment doesn't exist, remove attachment id
if (!repair) {
throw {
"status": "conflict",
"reason": "attachment missing",
"message": "Attachment \"" +
attachment_id + "\" from document \"" +
document_id + "\" is missing"
};
}
delete metadata._attachments[attachment_id];
modified = true;
}
}
}
}
}
return {
"modified": modified,
"metadata": metadata
};
}, function (event) { // on error
// convert into jio error object
// then propagate
throw {"status": event.target.status,
"reason": event.target.statustext};
}).then(function (dict) {
if (dict.modified) {
return this._put(dict.metadata);
}
return null;
}).then(function () {
return "no_content";
});
}
// returns jio object
function repairAll(shared, repair) {
return getAllFile().then(function (answer) {
var index, data = answer.data, length = data.length, id_list,
document_list = [];
for (index = 0; index < length; index += 1) {
// parsing all files
id_list = fileNameToIds(data[index]);
if (id_list.length === 1) {
// this is a document
document_list[document_list.length] = id_list[0];
} else if (id_list.length === 2) {
// this is an attachment
// reference it
shared.unreferenced_dict[id_list[0]] =
shared.unreferenced_dict[id_list[0]] || {};
shared.unreferenced_dict[id_list[0]][id_list[1]] = true;
} else {
shared.unknown_file_list.push(data[index]);
}
}
length = document_list.length;
for (index = 0; index < length; index += 1) {
shared._id = document_list[index];
document_list[index] = repairOne(shared, repair);
}
function removeFile(name) {
return ajax[that._auth_type](
"DELETE",
null,
that._url + '/' + name + "?_=" + Date.now(),
null,
that._login
);
}
function errorEventConverter(event) {
throw {"status": event.target.status,
"reason": event.target.statusText};
}
length = shared.unknown_file_list.length;
for (index = 0; index < length; index += 1) {
document_list.push(
removeFile(shared.unknown_file_list[index]).
then(null, errorEventConverter)
);
}
return RSVP.all(document_list);
}).then(function () {
return "no_content";
});
}
if (typeof param._id === 'string') {
repair_promise = repairOne(param, repair);
} else {
param.referenced_attachment_path_dict = {};
param.unreferenced_attachment_path_dict = {};
param.unknown_file_list = [];
repair_promise = repairAll(param, repair);
}
repair_promise.then(command.success, command.error, command.notify);
};
// DavStorage.prototype.genericRepair = function (param, repair) {
//
// var that = this, repair_promise;
//
// // returns a jio object
// function getAllFile() {
// return ajax[that._auth_type](
// "PROPFIND",
// "text",
// that._url + '/',
// null,
// that._login
// ).then(function (e) { // on success
// var i, length, rows = new DOMParser().parseFromString(
// e.target.responseText,
// "text/xml"
// ).querySelectorAll(
// "D\\:response, response"
// );
// if (rows.length === 1) {
// return {"status": 200, "data": []};
// }
// // exclude parent folder and browse
// rows = [].slice.call(rows);
// rows.shift();
// length = rows.length;
// for (i = 0; i < length; i += 1) {
// rows[i] = rows[i].querySelector("D\\:href, href").
// textContent.split('/').slice(-1)[0];
// }
// return {"data": rows, "status": 200};
// // rows -> [
// // 'file_path_1',
// // ...
// // ]
// }, function (e) { // on error
// // convert into jio error object
// // then propagate
// throw {"status": e.target.status,
// "reason": e.target.statusText};
// });
// }
//
// // returns jio object
// function repairOne(shared, repair) {
// var modified = false, document_id = shared._id;
// return that._get({"_id": document_id}).then(function (event) {
// var attachment_id, metadata = event.target.response;
//
// // metadata should be an object
// if (typeof metadata !== 'object' || metadata === null ||
// Array.isArray(metadata)) {
// if (!repair) {
// throw {
// "status": "conflict",
// "reason": "corrupted",
// "message": "Bad metadata found in document \"" +
// document_id + "\""
// };
// }
// return {};
// }
//
// // check metadata content
// if (!repair) {
// if (!(new jIO.Metadata(metadata).check())) {
// return {
// "status": "conflict",
// "reason": "corrupted",
// "message": "Some metadata might be lost"
// };
// }
// } else {
// modified = (
// jIO.util.uniqueJSONStringify(metadata) !==
// jIO.util.uniqueJSONStringify(
// new jIO.Metadata(metadata).format()._dict
// )
// );
// }
//
// // check metadata id
// if (metadata._id !== document_id) {
// // metadata id is different than file
// // this is not a critical thing
// modified = true;
// metadata._id = document_id;
// }
//
// // check attachment metadata container
// if (metadata._attachments &&
// (typeof metadata._attachments !== 'object' ||
// Array.isArray(metadata._attachments))) {
// // is not undefined nor object
// if (!repair) {
// throw {
// "status": "conflict",
// "reason": "corrupted",
// "message": "Bad attachment metadata found in document \"" +
// document_id + "\""
// };
// }
// delete metadata._attachments;
// modified = true;
// }
//
// // check every attachment metadata
// if (metadata._attachments) {
// for (attachment_id in metadata._attachments) {
// if (metadata._attachments.hasOwnProperty(attachment_id)) {
// // check attachment metadata type
// if (typeof metadata._attachments[attachment_id] !== 'object' ||
// metadata._attachments[attachment_id] === null ||
// Array.isArray(metadata._attachments[attachment_id])) {
// // is not object
// if (!repair) {
// throw {
// "status": "conflict",
// "reason": "corrupted",
// "message": "Bad attachment metadata found in document \"" +
// document_id + "\", attachment \"" +
// attachment_id + "\""
// };
// }
// metadata._attachments[attachment_id] = {};
// modified = true;
// }
// // check attachment existency if all attachment are listed
// if (shared.referenced_dict) {
// if (shared.unreferenced_dict[metadata._id] &&
// shared.unreferenced_dict[metadata._id][attachment_id]) {
// // attachment seams to exist but is not referenced
// shared.referenced_dict[metadata._id] =
// shared.referenced_dict[metadata._id] || {};
// shared.referenced_dict[metadata._id][attachment_id] = true;
// delete shared.unreferenced_dict[metadata._id][attachment_id];
// } else if (
// !(shared.referenced_dict[metadata._id] &&
// shared.referenced_dict[metadata._id][attachment_id])
// ) {
// // attachment doesn't exist, remove attachment id
// if (!repair) {
// throw {
// "status": "conflict",
// "reason": "attachment missing",
// "message": "Attachment \"" +
// attachment_id + "\" from document \"" +
// document_id + "\" is missing"
// };
// }
// delete metadata._attachments[attachment_id];
// modified = true;
// }
// }
// }
// }
// }
// return {
// "modified": modified,
// "metadata": metadata
// };
// }, function (event) { // on error
// // convert into jio error object
// // then propagate
// throw {"status": event.target.status,
// "reason": event.target.statustext};
// }).then(function (dict) {
// if (dict.modified) {
// return this._put(dict.metadata);
// }
// return null;
// }).then(function () {
// return "no_content";
// });
// }
//
// // returns jio object
// function repairAll(shared, repair) {
// return getAllFile().then(function (answer) {
// var index, data = answer.data, length = data.length, id_list,
// document_list = [];
// for (index = 0; index < length; index += 1) {
// // parsing all files
// id_list = fileNameToIds(data[index]);
// if (id_list.length === 1) {
// // this is a document
// document_list[document_list.length] = id_list[0];
// } else if (id_list.length === 2) {
// // this is an attachment
// // reference it
// shared.unreferenced_dict[id_list[0]] =
// shared.unreferenced_dict[id_list[0]] || {};
// shared.unreferenced_dict[id_list[0]][id_list[1]] = true;
// } else {
// shared.unknown_file_list.push(data[index]);
// }
// }
// length = document_list.length;
// for (index = 0; index < length; index += 1) {
// shared._id = document_list[index];
// document_list[index] = repairOne(shared, repair);
// }
//
// function removeFile(name) {
// return ajax[that._auth_type](
// "DELETE",
// null,
// that._url + '/' + name,
// null,
// that._login
// );
// }
//
// function errorEventConverter(event) {
// throw {"status": event.target.status,
// "reason": event.target.statusText};
// }
//
// length = shared.unknown_file_list.length;
// for (index = 0; index < length; index += 1) {
// document_list.push(
// removeFile(shared.unknown_file_list[index]).
// then(null, errorEventConverter)
// );
// }
//
// return RSVP.all(document_list);
// }).then(function () {
// return "no_content";
// });
// }
//
// if (typeof param._id === 'string') {
// repair_promise = repairOne(param, repair);
// } else {
// param.referenced_attachment_path_dict = {};
// param.unreferenced_attachment_path_dict = {};
// param.unknown_file_list = [];
// repair_promise = repairAll(param, repair);
// }
//
// repair_promise.then(command.success, command.error, command.notify);
//
// };
jIO.addStorage('dav', DavStorage);
......
......@@ -28,25 +28,28 @@
* - https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB
*/
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global define, module, require, indexedDB, jIO, RSVP, Blob, Math*/
(function (dependencies, factory) {
"use strict";
if (typeof define === "function" && define.amd) {
return define(dependencies, factory);
}
if (typeof module === "object" && module !== null &&
typeof module.exports === "object" && module.exports !== null &&
typeof require === "function") {
module.exports = factory.apply(null, dependencies.map(require));
return;
}
factory(jIO, RSVP);
}(["jio", "rsvp"], function (jIO, RSVP) {
/*jslint indent: 2, maxlen: 120, nomen: true */
/*global define, module, require, indexedDB, jIO, RSVP, Blob, Math, alert*/
// (function (dependencies, factory) {
// "use strict";
// if (typeof define === "function" && define.amd) {
// return define(dependencies, factory);
// }
// if (typeof module === "object" && module !== null &&
// typeof module.exports === "object" && module.exports !== null &&
// typeof require === "function") {
// module.exports = factory.apply(null, dependencies.map(require));
// return;
// }
// factory(jIO, RSVP);
// }(["jio", "rsvp"], function (jIO, RSVP) {
// "use strict";
(function (jIO) {
"use strict";
var Promise = RSVP.Promise, generateUuid = jIO.util.generateUuid;
var generateUuid = jIO.util.generateUuid;
function metadataObjectToString(value) {
var i, l;
......@@ -319,7 +322,7 @@
*@param {Object} command The JIO command
*@param {Object} param The command parameters
*/
IndexedDBStorage.prototype.get = function (command, param) {
IndexedDBStorage.prototype.get = function (param) {
var jio_storage = this,
transaction,
global_db,
......@@ -341,8 +344,7 @@
var store = transaction.objectStore("attachment");
return getIndexedDB(store, param._id);
}
throw ({"status": 404, "reason": "Not Found",
"message": "IndexeddbStorage, unable to get document."});
throw new jIO.util.jIOError("Cannot find document", 404);
})
.push(function (result) {
//get the reste data from attachment
......@@ -359,8 +361,7 @@
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
});
};
......@@ -505,390 +506,390 @@
/**
* Retrieve a list of present document
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
* @param {Boolean} [options.include_docs=false]
* Also retrieve the actual document content.
*/
IndexedDBStorage.prototype.getListMetadata = function (option) {
var rows = [], onCancel, open_req = indexedDB.open(this._database_name);
return new Promise(function (resolve, reject, notify) {
open_req.onerror = function () {
if (open_req.result) { open_req.result.close(); }
reject(open_req.error);
};
open_req.onsuccess = function () {
var tx, date, j = 0, index_req, db = open_req.result;
try {
tx = db.transaction(["metadata", "attachment"], "readonly");
onCancel = function () {
tx.abort();
db.close();
};
index_req = tx.objectStore("metadata").index("_id").openCursor();
date = Date.now();
index_req.onsuccess = function (event) {
var cursor = event.target.result, now, value, i, key;
if (cursor) {
// Called for each matching record
// notification management
now = Date.now();
if (date <= now - 1000) {
notify({"loaded": rows.length});
date = now;
}
// option.limit management
if (Array.isArray(option.limit)) {
if (option.limit.length > 1) {
if (option.limit[0] > 0) {
option.limit[0] -= 1;
cursor["continue"]();
return;
}
if (option.limit[1] <= 0) {
// end
index_req.onsuccess({"target": {}});
return;
}
option.limit[1] -= 1;
} else {
if (option.limit[0] <= 0) {
// end
index_req.onsuccess({"target": {}});
return;
}
option.limit[0] -= 1;
}
}
value = {};
// option.select_list management
if (option.select_list) {
for (i = 0; i < option.select_list.length; i += 1) {
key = option.select_list[i];
value[key] = cursor.value[key];
}
}
// option.include_docs management
if (option.include_docs) {
rows.push({
"id": cursor.value._id,
"doc": cursor.value,
"value": value
});
} else {
rows.push({
"id": cursor.value._id,
"value": value
});
}
// continue to next iteration
cursor["continue"]();
} else {
index_req = tx.objectStore("attachment").
index("_id").openCursor();
index_req.onsuccess = function (event) {
//second table
cursor = event.target.result;
if (cursor) {
value = {};
if (cursor.value._attachment) {
if (option.select_list) {
for (i = 0; i < option.select_list.length; i += 1) {
key = option.select_list[i];
value[key] = cursor.value._attachment[key];
}
}
//add info of attachment into metadata
rows[j].value._attachment = value;
if (option.include_docs) {
rows[j].doc._attachment = cursor.value._attachment;
}
}
j += 1;
cursor["continue"]();
} else {
notify({"loaded": rows.length});
resolve({"data": {"rows": rows, "total_rows": rows.length}});
db.close();
}
};
}
};
} catch (e) {
reject(e);
db.close();
}
};
}, function () {
if (typeof onCancel === "function") {
onCancel();
}
});
};
/**
* Add an attachment to a document
*
* @param {Object} command The JIO command
* @param {Object} metadata The data
*
*/
IndexedDBStorage.prototype.putAttachment = function (command, metadata) {
var jio_storage = this,
transaction,
global_db,
BlobInfo,
readResult;
function putAllPart(store, metadata, readResult, count, part) {
var blob,
readPart,
end;
if (count >= metadata._blob.size) {
return;
}
end = count + jio_storage._unite;
blob = metadata._blob.slice(count, end);
readPart = readResult.slice(count, end);
return putIndexedDB(store, {"_id": metadata._id,
"_attachment" : metadata._attachment,
"_part" : part,
"blob": blob}, readPart)
.then(function () {
return putAllPart(store, metadata, readResult, end, part + 1);
});
}
return jIO.util.readBlobAsArrayBuffer(metadata._blob)
.then(function (event) {
readResult = event.target.result;
BlobInfo = {
"content_type": metadata._blob.type,
"length": metadata._blob.size
};
return new RSVP.Queue()
.push(function () {
return openIndexedDB(jio_storage._database_name);
})
.push(function (db) {
global_db = db;
transaction = db.transaction(["attachment",
"blob"], "readwrite");
return promiseResearch(transaction,
metadata._id, "attachment", "_id");
})
.push(function (researchResult) {
if (researchResult.result === undefined) {
throw ({"status": 404, "reason": "Not Found",
"message": "indexeddbStorage unable to put attachment"});
}
//update attachment
researchResult.result._attachment = researchResult.
result._attachment || {};
researchResult.result._attachment[metadata._attachment] =
(BlobInfo === undefined) ? "BlobInfo" : BlobInfo;
return putIndexedDB(researchResult.store, researchResult.result);
})
.push(function () {
//put in blob
var store = transaction.objectStore("blob");
return putAllPart(store, metadata, readResult, 0, 0);
})
.push(function () {
return transactionEnd(transaction);
})
.push(function () {
return {"status": 204};
})
.push(undefined, function (error) {
if (global_db !== undefined) {
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
});
};
/**
* Retriev a document attachment
*
* @param {Object} command The JIO command
* @param {Object} param The command parameter
*/
IndexedDBStorage.prototype.getAttachment = function (command, param) {
var jio_storage = this,
transaction,
global_db,
blob,
totalLength;
function getDesirePart(store, start, end) {
if (start > end) {
return;
}
return getIndexedDB(store, [param._id, param._attachment, start])
.then(function (result) {
var blobPart = result.blob;
if (result.blob.byteLength !== undefined) {
blobPart = new Blob([result.blob]);
}
if (blob) {
blob = new Blob([blob, blobPart]);
} else {
blob = blobPart;
}
return getDesirePart(store, start + 1, end);
});
}
return new RSVP.Queue()
.push(function () {
return openIndexedDB(jio_storage._database_name);
})
.push(function (db) {
global_db = db;
transaction = db.transaction(["attachment", "blob"], "readwrite");
//check if the attachment exists
return promiseResearch(transaction,
param._id, "attachment", "_id");
})
.push(function (researchResult) {
var result = researchResult.result,
start,
end;
if (result === undefined ||
result._attachment[param._attachment] === undefined) {
throw ({"status": 404, "reason": "missing attachment",
"message": "IndexeddbStorage, unable to get attachment."});
}
totalLength = result._attachment[param._attachment].length;
param._start = param._start === undefined ? 0 : param._start;
param._end = param._end === undefined ? totalLength
: param._end;
if (param._end > totalLength) {
param._end = totalLength;
}
if (param._start < 0 || param._end < 0) {
throw ({"status": 404, "reason": "invalide _start, _end",
"message": "_start and _end must be positive"});
}
if (param._start > param._end) {
throw ({"status": 404, "reason": "invalide offset",
"message": "start is great then end"});
}
start = Math.floor(param._start / jio_storage._unite);
end = Math.floor(param._end / jio_storage._unite);
if (param._end % jio_storage._unite === 0) {
end -= 1;
}
return getDesirePart(transaction.objectStore("blob"),
start,
end);
})
.push(function () {
var start = param._start % jio_storage._unite,
end = start + param._end - param._start;
blob = blob.slice(start, end);
return ({ "data": new Blob([blob], {type: "text/plain"})});
})
.push(undefined, function (error) {
// Check if transaction is ongoing, if so, abort it
if (transaction !== undefined) {
transaction.abort();
}
if (global_db !== undefined) {
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
};
/**
* Remove an attachment
*
* @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters
*/
IndexedDBStorage.prototype.removeAttachment = function (command, param) {
var jio_storage = this,
transaction,
global_db,
totalLength;
function removePart(store, part) {
if (part * jio_storage._unite >= totalLength) {
return;
}
return removeIndexedDB(store, [param._id, param._attachment, part])
.then(function () {
return removePart(store, part + 1);
});
}
return new RSVP.Queue()
.push(function () {
return openIndexedDB(jio_storage._database_name);
})
.push(function (db) {
global_db = db;
transaction = db.transaction(["attachment", "blob"], "readwrite");
//check if the attachment exists
return promiseResearch(transaction, param._id,
"attachment", "_id");
})
.push(function (researchResult) {
var result = researchResult.result;
if (result === undefined ||
result._attachment[param._attachment] === undefined) {
throw ({"status": 404, "reason": "missing attachment",
"message":
"IndexeddbStorage, document attachment not found."});
}
totalLength = result._attachment[param._attachment].length;
//updata attachment
delete result._attachment[param._attachment];
return putIndexedDB(researchResult.store, result);
})
.push(function () {
var store = transaction.objectStore("blob");
return removePart(store, 0);
})
.push(function () {
return transactionEnd(transaction);
})
.push(function () {
return ({ "status": 204 });
})
.push(undefined, function (error) {
if (global_db !== undefined) {
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
};
IndexedDBStorage.prototype.allDocs = function (command, param, option) {
/*jslint unparam: true */
this.createDBIfNecessary().
then(this.getListMetadata.bind(this, option)).
then(command.success, command.error, command.notify);
};
IndexedDBStorage.prototype.check = function (command) {
command.success();
};
IndexedDBStorage.prototype.repair = function (command) {
command.success();
};
// /**
// * Retrieve a list of present document
// *
// * @method allDocs
// * @param {Object} command The JIO command
// * @param {Object} param The command parameters
// * @param {Object} options The command options
// * @param {Boolean} [options.include_docs=false]
// * Also retrieve the actual document content.
// */
// IndexedDBStorage.prototype.getListMetadata = function (option) {
// var rows = [], onCancel, open_req = indexedDB.open(this._database_name);
// return new Promise(function (resolve, reject, notify) {
// open_req.onerror = function () {
// if (open_req.result) { open_req.result.close(); }
// reject(open_req.error);
// };
// open_req.onsuccess = function () {
// var tx, date, j = 0, index_req, db = open_req.result;
// try {
// tx = db.transaction(["metadata", "attachment"], "readonly");
// onCancel = function () {
// tx.abort();
// db.close();
// };
// index_req = tx.objectStore("metadata").index("_id").openCursor();
// date = Date.now();
// index_req.onsuccess = function (event) {
// var cursor = event.target.result, now, value, i, key;
// if (cursor) {
// // Called for each matching record
// // notification management
// now = Date.now();
// if (date <= now - 1000) {
// notify({"loaded": rows.length});
// date = now;
// }
// // option.limit management
// if (Array.isArray(option.limit)) {
// if (option.limit.length > 1) {
// if (option.limit[0] > 0) {
// option.limit[0] -= 1;
// cursor["continue"]();
// return;
// }
// if (option.limit[1] <= 0) {
// // end
// index_req.onsuccess({"target": {}});
// return;
// }
// option.limit[1] -= 1;
// } else {
// if (option.limit[0] <= 0) {
// // end
// index_req.onsuccess({"target": {}});
// return;
// }
// option.limit[0] -= 1;
// }
// }
// value = {};
// // option.select_list management
// if (option.select_list) {
// for (i = 0; i < option.select_list.length; i += 1) {
// key = option.select_list[i];
// value[key] = cursor.value[key];
// }
// }
// // option.include_docs management
// if (option.include_docs) {
// rows.push({
// "id": cursor.value._id,
// "doc": cursor.value,
// "value": value
// });
// } else {
// rows.push({
// "id": cursor.value._id,
// "value": value
// });
// }
// // continue to next iteration
// cursor["continue"]();
// } else {
// index_req = tx.objectStore("attachment").
// index("_id").openCursor();
// index_req.onsuccess = function (event) {
// //second table
// cursor = event.target.result;
// if (cursor) {
// value = {};
// if (cursor.value._attachment) {
// if (option.select_list) {
// for (i = 0; i < option.select_list.length; i += 1) {
// key = option.select_list[i];
// value[key] = cursor.value._attachment[key];
// }
// }
// //add info of attachment into metadata
// rows[j].value._attachment = value;
// if (option.include_docs) {
// rows[j].doc._attachment = cursor.value._attachment;
// }
// }
// j += 1;
// cursor["continue"]();
// } else {
// notify({"loaded": rows.length});
// resolve({"data": {"rows": rows, "total_rows": rows.length}});
// db.close();
// }
// };
// }
// };
// } catch (e) {
// reject(e);
// db.close();
// }
// };
// }, function () {
// if (typeof onCancel === "function") {
// onCancel();
// }
// });
// };
// /**
// * Add an attachment to a document
// *
// * @param {Object} command The JIO command
// * @param {Object} metadata The data
// *
// */
// IndexedDBStorage.prototype.putAttachment = function (command, metadata) {
// var jio_storage = this,
// transaction,
// global_db,
// BlobInfo,
// readResult;
// function putAllPart(store, metadata, readResult, count, part) {
// var blob,
// readPart,
// end;
// if (count >= metadata._blob.size) {
// return;
// }
// end = count + jio_storage._unite;
// blob = metadata._blob.slice(count, end);
// readPart = readResult.slice(count, end);
// return putIndexedDB(store, {"_id": metadata._id,
// "_attachment" : metadata._attachment,
// "_part" : part,
// "blob": blob}, readPart)
// .then(function () {
// return putAllPart(store, metadata, readResult, end, part + 1);
// });
// }
// return jIO.util.readBlobAsArrayBuffer(metadata._blob)
// .then(function (event) {
// readResult = event.target.result;
// BlobInfo = {
// "content_type": metadata._blob.type,
// "length": metadata._blob.size
// };
// return new RSVP.Queue()
// .push(function () {
// return openIndexedDB(jio_storage._database_name);
// })
// .push(function (db) {
// global_db = db;
// transaction = db.transaction(["attachment",
// "blob"], "readwrite");
// return promiseResearch(transaction,
// metadata._id, "attachment", "_id");
// })
// .push(function (researchResult) {
// if (researchResult.result === undefined) {
// throw ({"status": 404, "reason": "Not Found",
// "message": "indexeddbStorage unable to put attachment"});
// }
// //update attachment
// researchResult.result._attachment = researchResult.
// result._attachment || {};
// researchResult.result._attachment[metadata._attachment] =
// (BlobInfo === undefined) ? "BlobInfo" : BlobInfo;
// return putIndexedDB(researchResult.store, researchResult.result);
// })
// .push(function () {
// //put in blob
// var store = transaction.objectStore("blob");
// return putAllPart(store, metadata, readResult, 0, 0);
// })
// .push(function () {
// return transactionEnd(transaction);
// })
// .push(function () {
// return {"status": 204};
// })
// .push(undefined, function (error) {
// if (global_db !== undefined) {
// global_db.close();
// }
// throw error;
// })
// .push(command.success, command.error, command.notify);
// });
// };
// /**
// * Retriev a document attachment
// *
// * @param {Object} command The JIO command
// * @param {Object} param The command parameter
// */
// IndexedDBStorage.prototype.getAttachment = function (command, param) {
// var jio_storage = this,
// transaction,
// global_db,
// blob,
// totalLength;
// function getDesirePart(store, start, end) {
// if (start > end) {
// return;
// }
// return getIndexedDB(store, [param._id, param._attachment, start])
// .then(function (result) {
// var blobPart = result.blob;
// if (result.blob.byteLength !== undefined) {
// blobPart = new Blob([result.blob]);
// }
// if (blob) {
// blob = new Blob([blob, blobPart]);
// } else {
// blob = blobPart;
// }
// return getDesirePart(store, start + 1, end);
// });
// }
// return new RSVP.Queue()
// .push(function () {
// return openIndexedDB(jio_storage._database_name);
// })
// .push(function (db) {
// global_db = db;
// transaction = db.transaction(["attachment", "blob"], "readwrite");
// //check if the attachment exists
// return promiseResearch(transaction,
// param._id, "attachment", "_id");
// })
// .push(function (researchResult) {
// var result = researchResult.result,
// start,
// end;
// if (result === undefined ||
// result._attachment[param._attachment] === undefined) {
// throw ({"status": 404, "reason": "missing attachment",
// "message": "IndexeddbStorage, unable to get attachment."});
// }
// totalLength = result._attachment[param._attachment].length;
// param._start = param._start === undefined ? 0 : param._start;
// param._end = param._end === undefined ? totalLength
// : param._end;
// if (param._end > totalLength) {
// param._end = totalLength;
// }
// if (param._start < 0 || param._end < 0) {
// throw ({"status": 404, "reason": "invalide _start, _end",
// "message": "_start and _end must be positive"});
// }
// if (param._start > param._end) {
// throw ({"status": 404, "reason": "invalide offset",
// "message": "start is great then end"});
// }
// start = Math.floor(param._start / jio_storage._unite);
// end = Math.floor(param._end / jio_storage._unite);
// if (param._end % jio_storage._unite === 0) {
// end -= 1;
// }
// return getDesirePart(transaction.objectStore("blob"),
// start,
// end);
// })
// .push(function () {
// var start = param._start % jio_storage._unite,
// end = start + param._end - param._start;
// blob = blob.slice(start, end);
// return ({ "data": new Blob([blob], {type: "text/plain"})});
// })
// .push(undefined, function (error) {
// // Check if transaction is ongoing, if so, abort it
// if (transaction !== undefined) {
// transaction.abort();
// }
// if (global_db !== undefined) {
// global_db.close();
// }
// throw error;
// })
// .push(command.success, command.error, command.notify);
// };
// /**
// * Remove an attachment
// *
// * @method removeAttachment
// * @param {Object} command The JIO command
// * @param {Object} param The command parameters
// */
// IndexedDBStorage.prototype.removeAttachment = function (command, param) {
// var jio_storage = this,
// transaction,
// global_db,
// totalLength;
// function removePart(store, part) {
// if (part * jio_storage._unite >= totalLength) {
// return;
// }
// return removeIndexedDB(store, [param._id, param._attachment, part])
// .then(function () {
// return removePart(store, part + 1);
// });
// }
// return new RSVP.Queue()
// .push(function () {
// return openIndexedDB(jio_storage._database_name);
// })
// .push(function (db) {
// global_db = db;
// transaction = db.transaction(["attachment", "blob"], "readwrite");
// //check if the attachment exists
// return promiseResearch(transaction, param._id,
// "attachment", "_id");
// })
// .push(function (researchResult) {
// var result = researchResult.result;
// if (result === undefined ||
// result._attachment[param._attachment] === undefined) {
// throw ({"status": 404, "reason": "missing attachment",
// "message":
// "IndexeddbStorage, document attachment not found."});
// }
// totalLength = result._attachment[param._attachment].length;
// //updata attachment
// delete result._attachment[param._attachment];
// return putIndexedDB(researchResult.store, result);
// })
// .push(function () {
// var store = transaction.objectStore("blob");
// return removePart(store, 0);
// })
// .push(function () {
// return transactionEnd(transaction);
// })
// .push(function () {
// return ({ "status": 204 });
// })
// .push(undefined, function (error) {
// if (global_db !== undefined) {
// global_db.close();
// }
// throw error;
// })
// .push(command.success, command.error, command.notify);
// };
// IndexedDBStorage.prototype.allDocs = function (command, param, option) {
// /*jslint unparam: true */
// this.createDBIfNecessary().
// then(this.getListMetadata.bind(this, option)).
// then(command.success, command.error, command.notify);
// };
//
// IndexedDBStorage.prototype.check = function (command) {
// command.success();
// };
//
// IndexedDBStorage.prototype.repair = function (command) {
// command.success();
// };
jIO.addStorage("indexeddb", IndexedDBStorage);
}));
}(jIO));
......@@ -6,7 +6,7 @@
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global jIO, localStorage, setTimeout, window, define, Blob, Uint8Array,
exports, require */
exports, require, console, RSVP */
/**
* JIO Local Storage. Type = 'local'.
......@@ -47,21 +47,7 @@
* @class LocalStorage
*/
// define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports, require('jio'));
}
window.local_storage = {};
module(window.local_storage, jIO);
}([
'exports',
'jio'
], function (exports, jIO) {
(function (exports, jIO) {
"use strict";
/**
......@@ -123,13 +109,6 @@
* @constructor
*/
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) {
case "memory":
this._database = ram;
......@@ -150,31 +129,25 @@
* Create a document in local storage.
*
* @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
LocalStorage.prototype.post = function (command, metadata) {
LocalStorage.prototype.post = function (metadata) {
var doc, doc_id = metadata._id;
if (!doc_id) {
if (doc_id === undefined) {
doc_id = jIO.util.generateUuid();
}
doc = this._storage.getItem(this._localpath + "/" + doc_id);
if (doc === null) {
if (this._storage.getItem(doc_id) === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
doc._id = doc_id;
// XXX
delete doc._attachments;
this._storage.setItem(this._localpath + "/" + doc_id, doc);
command.success({"id": doc_id});
} else {
// the document already exists
command.error(
"conflict",
"document exists",
"Cannot create a new document"
);
this._storage.setItem(doc_id, doc);
return doc_id;
}
// the document already exists
throw new jIO.util.jIOError("Cannot create a new document", 409);
};
/**
......@@ -185,130 +158,94 @@
* @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
LocalStorage.prototype.put = function (command, metadata) {
var doc, tmp, status;
doc = this._storage.getItem(this._localpath + "/" + metadata._id);
LocalStorage.prototype.put = function (metadata) {
var doc, tmp;
doc = this._storage.getItem(metadata._id);
if (doc === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
delete doc._attachments;
status = "created";
} else {
// the document already exists
tmp = jIO.util.deepClone(metadata);
tmp._attachments = doc._attachments;
doc = tmp;
status = "no_content";
}
// write
this._storage.setItem(this._localpath + "/" + metadata._id, doc);
command.success(status);
this._storage.setItem(metadata._id, doc);
return metadata._id;
};
/**
* Add an attachment to a document
*
* @method putAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.putAttachment = function (command, param) {
var that = this, doc, status = "created";
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
// the document does not exist
return command.error(
"not_found",
"missing",
"Impossible to add attachment"
);
}
LocalStorage.prototype.putAttachment = function (param) {
var that = this, doc;
doc = this.get({"_id": param._id});
// the document already exists
// download data
jIO.util.readBlobAsBinaryString(param._blob).then(function (e) {
doc._attachments = doc._attachments || {};
if (doc._attachments[param._attachment]) {
status = "no_content";
}
doc._attachments[param._attachment] = {
"content_type": param._blob.type,
"digest": jIO.util.makeBinaryStringDigest(e.target.result),
"length": param._blob.size
};
that._storage.setItem(that._localpath + "/" + param._id + "/" +
param._attachment, e.target.result);
that._storage.setItem(that._localpath + "/" + param._id, doc);
command.success(status,
{"digest": doc._attachments[param._attachment].digest});
}, function (e) {
command.error(
"request_timeout",
"blob error",
"Error " + e.status + ", unable to get blob content"
);
}, function (e) {
command.notify((e.loaded / e.total) * 100);
});
return new RSVP.Queue()
.push(function () {
return jIO.util.readBlobAsBinaryString(param._blob);
})
.push(function (e) {
doc._attachments = doc._attachments || {};
// if (doc._attachments[param._attachment]) {
// status = "no_content";
// }
doc._attachments[param._attachment] = {
"content_type": param._blob.type,
"digest": jIO.util.makeBinaryStringDigest(e.target.result),
"length": param._blob.size
};
that._storage.setItem(param._id + "/" +
param._attachment, e.target.result);
that._storage.setItem(param._id, doc);
return {"digest": doc._attachments[param._attachment].digest};
});
};
/**
* Get a document
*
* @method get
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.get = function (command, param) {
var doc = this._storage.getItem(
this._localpath + "/" + param._id
);
if (doc !== null) {
command.success({"data": doc});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
LocalStorage.prototype.get = function (param) {
var doc = this._storage.getItem(param._id);
if (doc === null) {
throw new jIO.util.jIOError("Cannot find document", 404);
}
return doc;
};
/**
* Get an attachment
*
* @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.getAttachment = function (command, param) {
LocalStorage.prototype.getAttachment = function (param) {
var doc, i, uint8array, binarystring;
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
return command.error(
"not_found",
"missing document",
"Cannot find document"
);
}
doc = this.get({"_id": param._id});
if (typeof doc._attachments !== 'object' ||
typeof doc._attachments[param._attachment] !== 'object') {
return command.error(
"not_found",
"missing attachment",
"Cannot find attachment"
);
throw new jIO.util.jIOError("Cannot find attachment", 404);
}
// Storing data twice in binarystring and in uint8array (in memory)
// is not a problem here because localStorage <= 5MB
binarystring = this._storage.getItem(
this._localpath + "/" + param._id + "/" + param._attachment
param._id + "/" + param._attachment
) || "";
uint8array = new Uint8Array(binarystring.length);
for (i = 0; i < binarystring.length; i += 1) {
......@@ -318,10 +255,10 @@
"type": doc._attachments[param._attachment].content_type || ""
});
command.success({
return {
"data": uint8array,
"digest": doc._attachments[param._attachment].digest
});
};
};
/**
......@@ -334,7 +271,7 @@
*/
LocalStorage.prototype.remove = function (command, param) {
var doc, i, attachment_list;
doc = this._storage.getItem(this._localpath + "/" + param._id);
doc = this._storage.getItem(param._id);
attachment_list = [];
if (doc !== null && typeof doc === "object") {
if (typeof doc._attachments === "object") {
......@@ -397,87 +334,88 @@
command.success();
};
/**
* Get all filenames belonging to a user from the document index
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.allDocs = function (command, param, options) {
var i, row, path_re, rows, document_list, document_object, delete_id;
param.unused = true;
rows = [];
document_list = [];
path_re = new RegExp(
"^" + jIO.Query.stringEscapeRegexpCharacters(this._localpath) +
"/[^/]+$"
);
if (options.query === undefined && options.sort_on === undefined &&
options.select_list === undefined &&
options.include_docs === undefined) {
rows = [];
for (i in this._database) {
if (this._database.hasOwnProperty(i)) {
// filter non-documents
if (path_re.test(i)) {
row = { value: {} };
row.id = i.split('/').slice(-1)[0];
row.key = row.id;
if (options.include_docs) {
row.doc = JSON.parse(this._storage.getItem(i));
}
rows.push(row);
}
}
}
command.success({"data": {"rows": rows, "total_rows": rows.length}});
} else {
// create jio query object from returned results
for (i in this._database) {
if (this._database.hasOwnProperty(i)) {
if (path_re.test(i)) {
document_list.push(this._storage.getItem(i));
}
}
}
options.select_list = options.select_list || [];
if (options.select_list.indexOf("_id") === -1) {
options.select_list.push("_id");
delete_id = true;
}
if (options.include_docs === true) {
document_object = {};
document_list.forEach(function (meta) {
document_object[meta._id] = meta;
});
}
jIO.QueryFactory.create(options.query || "",
this._key_schema).
exec(document_list, options).then(function () {
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
"key": value._id
};
if (options.include_docs === true) {
o.doc = document_object[value._id];
delete document_object[value._id];
}
if (delete_id) {
delete value._id;
}
o.value = value;
return o;
});
command.success({"data": {
"total_rows": document_list.length,
"rows": document_list
}});
});
}
};
// /**
// * Get all filenames belonging to a user from the document index
// *
// * @method allDocs
// * @param {Object} command The JIO command
// * @param {Object} param The given parameters
// * @param {Object} options The command options
// */
// LocalStorage.prototype.allDocs = function (command, param, options) {
// console.log("allDocs begin");
// var i, row, path_re, rows, document_list, document_object, delete_id;
// param.unused = true;
// rows = [];
// document_list = [];
// path_re = new RegExp(
// "^" + jIO.Query.stringEscapeRegexpCharacters(this._localpath) +
// "/[^/]+$"
// );
// if (options.query === undefined && options.sort_on === undefined &&
// options.select_list === undefined &&
// options.include_docs === undefined) {
// rows = [];
// for (i in this._database) {
// if (this._database.hasOwnProperty(i)) {
// // filter non-documents
// if (path_re.test(i)) {
// row = { value: {} };
// row.id = i.split('/').slice(-1)[0];
// row.key = row.id;
// if (options.include_docs) {
// row.doc = JSON.parse(this._storage.getItem(i));
// }
// rows.push(row);
// }
// }
// }
// command.success({"data": {"rows": rows, "total_rows": rows.length}});
// } else {
// // create jio query object from returned results
// for (i in this._database) {
// if (this._database.hasOwnProperty(i)) {
// if (path_re.test(i)) {
// document_list.push(this._storage.getItem(i));
// }
// }
// }
// options.select_list = options.select_list || [];
// if (options.select_list.indexOf("_id") === -1) {
// options.select_list.push("_id");
// delete_id = true;
// }
// if (options.include_docs === true) {
// document_object = {};
// document_list.forEach(function (meta) {
// document_object[meta._id] = meta;
// });
// }
// jIO.QueryFactory.create(options.query || "",
// this._key_schema).
// exec(document_list, options).then(function () {
// document_list = document_list.map(function (value) {
// var o = {
// "id": value._id,
// "key": value._id
// };
// if (options.include_docs === true) {
// o.doc = document_object[value._id];
// delete document_object[value._id];
// }
// if (delete_id) {
// delete value._id;
// }
// o.value = value;
// return o;
// });
// command.success({"data": {
// "total_rows": document_list.length,
// "rows": document_list
// }});
// });
// }
// };
/**
* Check the storage or a specific document
......@@ -679,51 +617,6 @@
jIO.addStorage('local', LocalStorage);
//////////////////////////////////////////////////////////////////////
// Tools
function createLocalDescription(username, application_name) {
if (typeof username !== 'string') {
throw new TypeError("LocalStorage username must be a string");
}
var description = {
"type": "local",
"username": username
};
if (typeof application_name === 'string') {
description.application_name = application_name;
}
return description;
}
function createMemoryDescription(username, application_name) {
var description = createLocalDescription(username, application_name);
description.mode = "memory";
return description;
}
/**
* Tool to help users to create local storage description for JIO
*
* @param {String} username The username
* @param {String} [application_name] The application_name
* @param {String} [mode="localStorage"] Use localStorage or memory
* @return {Object} The storage description
*/
function createDescription(username, application_name, mode) {
if (mode === undefined || mode.toString() === 'localStorage') {
return createLocalDescription(username, application_name);
}
if (mode.toString() === 'memory') {
return createMemoryDescription(username, application_name);
}
throw new TypeError("Unknown LocalStorage '" + mode.toString() + "' mode");
}
exports.createDescription = createDescription;
exports.createLocalDescription = createLocalDescription;
exports.createMemoryDescription = createMemoryDescription;
function clearLocalStorage() {
var k;
for (k in localStorage) {
......@@ -743,4 +636,4 @@
exports.clearLocalStorage = clearLocalStorage;
exports.clearMemoryStorage = clearMemoryStorage;
}));
}(window, jIO));
/*jslint indent: 2, maxlen: 80, nomen: true, regexp: true, unparam: true */
/*global define, RSVP, jIO */
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(RSVP, jIO);
}(['rsvp', 'jio'], function (RSVP, jIO) {
/*jslint nomen: true*/
/*global console*/
(function (jIO) {
"use strict";
/**
* The jIO QueryStorage extension
*
......@@ -18,116 +10,23 @@
* @constructor
*/
function QueryStorage(spec) {
this._sub_storage = spec.sub_storage;
this._sub_storage = jIO.createJIO(spec.sub_storage);
this._key_schema = spec.key_schema;
}
/**
* 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.get = function () {
return this._sub_storage.get.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.post = function () {
return this._sub_storage.post.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.put = function () {
return this._sub_storage.put.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);
QueryStorage.prototype.remove = function () {
return this._sub_storage.remove.apply(this._sub_storage, arguments);
};
/**
* Retrieve documents.
* This method performs an .allDocs() call on the substorage,
......@@ -137,103 +36,74 @@
* @param {Object} command The given parameters
* @param {Object} options The command options
*/
QueryStorage.prototype.allDocs = function (command, param, options) {
var that = this,
substorage = command.storage(this._sub_storage),
// we need the full documents in order to perform the query, will
// remove them later if they were not required.
include_docs = (options.include_docs || options.query) ? true : false;
substorage.allDocs({
"include_docs": include_docs
}).then(function (response) {
var data_rows = response.data.rows, docs = {}, row, i, l;
if (!include_docs) {
return response;
}
if (options.include_docs) {
for (i = 0, l = data_rows.length; i < l; i += 1) {
row = data_rows[i];
docs[row.id] = JSON.parse(JSON.stringify(row.doc));
row.doc._id = row.id;
data_rows[i] = row.doc;
}
} else {
for (i = 0, l = data_rows.length; i < l; i += 1) {
row = data_rows[i];
row.doc._id = row.id;
data_rows[i] = row.doc;
}
}
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) {
// reconstruct filtered rows, preserving the order from docs
if (options.include_docs) {
for (i = 0, l = filtered_docs.length; i < l; i += 1) {
filtered_docs[i] = {
"id": filtered_docs[i]._id,
"doc": docs[filtered_docs[i]._id],
"value": options.select_list ? filtered_docs[i] : {}
};
delete filtered_docs[i].value._id;
}
} else {
for (i = 0, l = filtered_docs.length; i < l; i += 1) {
filtered_docs[i] = {
"id": filtered_docs[i]._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;
});
}).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);
QueryStorage.prototype.allDocs = function (options) {
console.log(options);
// var context = this,
var substorage = this._sub_storage;
// // we need the full documents in order to perform the query, will
// // remove them later if they were not required.
// include_docs = (options.include_docs || options.query) ? true : false;
return substorage.allDocs.apply(substorage, arguments);
// substorage.allDocs({
// "include_docs": include_docs
// }).then(function (response) {
//
// var data_rows = response.data.rows, docs = {}, row, i, l;
//
// if (!include_docs) {
// return response;
// }
//
// if (options.include_docs) {
// for (i = 0, l = data_rows.length; i < l; i += 1) {
// row = data_rows[i];
// docs[row.id] = JSON.parse(JSON.stringify(row.doc));
// row.doc._id = row.id;
// data_rows[i] = row.doc;
// }
// } else {
// for (i = 0, l = data_rows.length; i < l; i += 1) {
// row = data_rows[i];
// row.doc._id = row.id;
// data_rows[i] = row.doc;
// }
// }
//
// 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) {
// // reconstruct filtered rows, preserving the order from docs
// if (options.include_docs) {
// for (i = 0, l = filtered_docs.length; i < l; i += 1) {
// filtered_docs[i] = {
// "id": filtered_docs[i]._id,
// "doc": docs[filtered_docs[i]._id],
// "value": options.select_list ? filtered_docs[i] : {}
// };
// delete filtered_docs[i].value._id;
// }
// } else {
// for (i = 0, l = filtered_docs.length; i < l; i += 1) {
// filtered_docs[i] = {
// "id": filtered_docs[i]._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;
// });
//
// }).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));
/*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 */
/*global exports, Blob, FileReader, RSVP, hex_sha256, XMLHttpRequest,
constants */
constants, console */
/**
* Do not exports these tools unless they are not writable, not configurable.
......@@ -44,66 +44,6 @@ function jsonDeepClone(object) {
}
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.
......@@ -178,24 +118,6 @@ function arrayValuesToTypeDict(array) {
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.
......@@ -349,58 +271,6 @@ function readBlobAsText(blob) {
}
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
......
/*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 */
/*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
......@@ -208,4 +208,4 @@ ComplexQuery.prototype.NOT = function (item) {
query_class_dict.complex = ComplexQuery;
exports.ComplexQuery = ComplexQuery;
window.ComplexQuery = ComplexQuery;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
true, select: true, exports, stringEscapeRegexpCharacters: true,
true, select: true, window, stringEscapeRegexpCharacters: true,
deepClone, RSVP, sequence */
/**
......@@ -194,4 +194,4 @@ Query.prototype.serialized = function () {
return undefined;
};
exports.Query = Query;
window.Query = Query;
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global exports, ComplexQuery, SimpleQuery, Query, parseStringToObject,
/*global window, ComplexQuery, SimpleQuery, Query, parseStringToObject,
query_class_dict */
/**
......@@ -36,4 +36,4 @@ QueryFactory.create = function (object, key_schema) {
"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 */
/*global Query, inherits, query_class_dict, exports,
/*global Query, inherits, query_class_dict, window,
searchTextToRegExp, RSVP */
var checkKeySchema = function (key_schema) {
......@@ -394,4 +394,4 @@ SimpleQuery.prototype[">="] = function (object_value, comparison_value) {
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 */
/*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) {
/*jslint maxlen: 120, nomen: true */
/*global indexedDB, test_util, console, Blob, sinon*/
(function (jIO, QUnit) {
"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");
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;
module("indexeddbStorage", {
setup: function () {
// localStorage.clear();
this.jio = jIO.createJIO({
"type": "indexeddb",
"database": "qunit"
});
}
});
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");
/////////////////////////////////////////////////////////////////
// indexeddbStorage.get
/////////////////////////////////////////////////////////////////
test("get inexistent document", function () {
stop();
expect(3);
this.jio.get({"_id": "inexistent"})
.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();
});
}
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");
test("get document", function () {
var id = "post1",
// myAPI,
indexedDB,
mock;
// localStorage[id] = JSON.stringify({
// title: "myPost1"
// });
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
// 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();
stop();
expect(1);
this.jio.get({"_id": id})
.then(function (result) {
deepEqual(result, {
"title": "myPost1"
}, "Check document");
mock.verify();
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
mock.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 */
/*global window, define, module, test_util, RSVP, jIO, local_storage, test, ok,
deepEqual, sinon, expect, stop, start, Blob */
// 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) {
/*jslint maxlen: 120, nomen: true */
/*global localStorage, test_util, console, Blob*/
(function (jIO, localStorage, QUnit) {
"use strict";
module("LocalStorage");
local_storage.clear();
/**
* all(promises): Promise
*
* 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
* given promises.
*
* @param {Array} promises The promises to use
* @return {Promise} A new promise
*/
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();
}
}
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;
module("localStorage", {
setup: function () {
localStorage.clear();
this.jio = jIO.createJIO({
"type": "local"
});
}
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();
expect(3);
all([
// get inexistent document
jio.get({"_id": "inexistent"}).always(function (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");
}),
// 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");
this.jio.get({"_id": "inexistent"})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document");
equal(error.status_code, 404);
})
]).always(start);
.fail(function (error) {
console.error(error);
ok(false, error);
})
.always(function () {
start();
});
});
test("Put & Get", function () {
expect(4);
var jio = jIO.createJIO({
"type": "local",
"username": "uput",
"application_name": "aput"
}, {
"workspace": {}
test("get document", function () {
var id = "post1";
localStorage[id] = JSON.stringify({
title: "myPost1"
});
stop();
expect(1);
// put non empty document
jio.put({"_id": "put1", "title": "myPut1"}).always(function (answer) {
deepEqual(answer, {
"id": "put1",
"method": "put",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Creates a document");
}).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");
this.jio.get({"_id": id})
.then(function (result) {
deepEqual(result, {
"title": "myPost1"
}, "Check document");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}).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, {
"data": {
"_id": "put1",
"title": "myPut2"
},
"id": "put1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get, Check document");
this.jio.post({})
.then(function (uuid) {
// ok(util.isUuid(uuid), "Uuid should look like " +
// "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
equal(localStorage[uuid], JSON.stringify({
"_id": uuid
}));
})
.fail(function (error) {
ok(false, error);
})
.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 () {
expect(9);
var jio = jIO.createJIO({
"type": "local",
"username": "uputattmt",
"application_name": "aputattmt"
}, {
"workspace": {}
test("post but document already exists", function () {
var id = "post1";
localStorage[id] = JSON.stringify({
"_id": id,
title: "myPost1"
});
expect(4);
stop();
all([
// get an attachment from an inexistent document
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");
this.jio.post({"_id": "post1", "title": "myPost2"})
.then(function (result) {
ok(false, result);
})
.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 () {
expect(4);
var jio = jIO.createJIO({
"type": "local",
"username": "uremove",
"application_name": "aremove"
}, {
"workspace": {}
test("put when document already exists", function () {
var id = "put1";
localStorage[id] = JSON.stringify({
"_id": id,
title: "myPut1"
});
expect(2);
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 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");
this.jio.put({"_id": id, "title": "myPut2"})
.then(function (uuid) {
equal(uuid, "put1");
equal(localStorage.put1, JSON.stringify({
"_id": "put1",
"title": "myPut2"
}));
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}).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);
var o = {}, jio = jIO.createJIO({
"type": "local",
"username": "ualldocs",
"application_name": "aalldocs"
}, {
"workspace": {}
localStorage[id] = JSON.stringify({
"_id": id
});
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();
expect(4);
o.date_a = new Date(0);
o.date_b = new Date();
// put some document before list them
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;
});
localStorage[id] = JSON.stringify({
"_id": id,
"_attachments": {
"putattmt2": {
content_type: "",
digest: "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
length: 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
});
localStorage[id + '/' + attachment] = JSON.stringify("");
this.jio.getAttachment({
"_id": id,
"_attachment": attachment
})
.then(function (result) {
ok(result.data instanceof Blob, "Data is Blob");
deepEqual(result.data.type, "", "Check mimetype");
deepEqual(result.data.size, 0, "Check size");
delete result.data;
deepEqual(result, {
"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855"
}, "Get Attachment, Check Response");
})
.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": "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);
/////////////////////////////////////////////////////////////////
// localStorage.putAttachment
/////////////////////////////////////////////////////////////////
test("put an attachment to an inexistent document", function () {
stop();
expect(3);
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 () {
expect(18);
var o = {}, jio = jIO.createJIO({
"type": "local",
"username": "urepair",
"application_name": "arepair"
}, {
"workspace": {}
test("put an attachment to a document", function () {
var id = "putattmt1";
localStorage[id] = JSON.stringify({
"_id": id,
"title": "myPutAttmt1"
});
stop();
expect(3);
o.putCorruptedDocuments = function () {
// put a document with a wrong attachment reference
util.json_local_storage.setItem(
"jio/localstorage/urepair/arepair/war",
{"_id": "war", "title": "b", "_attachments": {"aa": {}}}
);
// put a document with a wrong metadata
util.json_local_storage.setItem(
"jio/localstorage/urepair/arepair/meta",
{"_id": "meta", "title": ["b", ["c", {}], {"blue": "blue"}]}
);
// put a corrupted document
util.json_local_storage.setItem(
"jio/localstorage/urepair/arepair/cor",
"blue"
);
};
// put an unreferenced attachment
util.json_local_storage.setItem(
"jio/localstorage/urepair/arepair/unref/aa",
"attachment content"
);
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);
this.jio.putAttachment({
"_id": id,
"_attachment": "putattmt2",
"_data": ""
})
.then(function (result) {
deepEqual(result, {"digest": "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855"
});
equal(localStorage[id], JSON.stringify({
"_id": id,
"title": "myPutAttmt1",
"_attachments": {
"putattmt2": {
content_type: "",
digest: "sha256-e3b0c44298fc1c149afbf4c8996fb9242" +
"7ae41e4649b934ca495991b7852b855",
length: 0
}
}
}));
equal(localStorage[id + '/putattmt2'], JSON.stringify(""));
})
.fail(function (error) {
ok(false, error);
})
.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 */
/*global window, define, module, test_util, RSVP, jIO, local_storage, test, ok,
deepEqual, sinon, expect, stop, start, Blob, query_storage */
// 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) {
/*jslint nomen: true, maxlen: 200 */
/*global Blob, test_util, console*/
(function (jIO, QUnit) {
"use strict";
module("QueryStorage");
function createQueryStorage(name, key_schema) {
var local_description = local_storage.createDescription(name,
name,
'memory');
return jIO.createJIO({
type: 'query',
sub_storage: local_description,
key_schema: key_schema
}, {
workspace: {}
});
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 Storage200() {
return this;
}
/*
* 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');
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;
};
Storage200.prototype.allDocs = function (options) {
deepEqual(options, {"_id": "bar", "title": "foo"}, "post 200 called");
return options._id;
};
jIO.addStorage('querystorage200', Storage200);
/////////////////////////////////////////////////////////////////
// queryStorage.get
/////////////////////////////////////////////////////////////////
module("queryStorage.get");
test("get called substorage get", function () {
stop();
expect(2);
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"}));
}
var jio = jIO.createJIO({
type: "query",
sub_storage: {
type: "querystorage200"
}
});
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"
jio.get({"_id": "bar"})
.then(function (result) {
deepEqual(result, {
"title": "foo"
}, "Check document");
})
.fail(function (error) {
ok(false, error);
})
.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);
});
test("put & get", 4, function () {
var jio = createQueryStorage('put-get');
/////////////////////////////////////////////////////////////////
// queryStorage.post
/////////////////////////////////////////////////////////////////
module("queryStorage.post");
test("post called substorage post", function () {
stop();
expect(2);
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"});
}
var jio = jIO.createJIO({
type: "query",
sub_storage: {
type: "querystorage200"
}
});
function getNonEmptyDocument2Test(answer) {
deepEqual(answer, {
"data": {
"_id": "put1",
"title": "myPut2"
},
"id": "put1",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
jio.post({"_id": "bar", "title": "foo"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
}
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');
/////////////////////////////////////////////////////////////////
// queryStorage.put
/////////////////////////////////////////////////////////////////
module("queryStorage.put");
test("put called substorage put", function () {
stop();
expect(2);
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"})
]);
}
var jio = jIO.createJIO({
type: "query",
sub_storage: {
type: "querystorage200"
}
});
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"
jio.put({"_id": "bar", "title": "foo"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
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');
/////////////////////////////////////////////////////////////////
// queryStorage.remove
/////////////////////////////////////////////////////////////////
module("queryStorage.remove");
test("remove called substorage remove", function () {
stop();
expect(2);
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"
});
}
var jio = jIO.createJIO({
type: "query",
sub_storage: {
type: "querystorage200"
}
});
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"
jio.remove({"_id": "bar"})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
}
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());
}
}
}
};
/////////////////////////////////////////////////////////////////
// queryStorage.allDocs
/////////////////////////////////////////////////////////////////
module("queryStorage.allDocs");
test("allDocs called substorage allDocs", function () {
stop();
expect(2);
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;
});
var jio = jIO.createJIO({
type: "query",
sub_storage: {
type: "querystorage200"
}
});
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"
jio.allDocs({
include_docs: true,
query: 'title: "two"'
})
.then(function (result) {
equal(result, "bar");
})
.fail(function (error) {
console.error(error);
console.error(error.stack);
ok(false, error);
})
.always(function () {
start();
});
}
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 () {
// })
}));
// function createQueryStorage(name, key_schema) {
// // var local_description = local_storage.createDescription(name,
// // name,
// // '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 */
/*global define, exports, require, test, ok, strictEqual, equal, throws, jiodate, moment, module */
// 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) {
/*jslint nomen: true, newcap: true */
/*global jiodate, moment, console*/
(function (QUnit, jiodate, moment) {
"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');
var JIODate = jiodate.JIODate;
test("A JIODate can be instantiated without parameters (=now)", function () {
ok((new JIODate()) instanceof JIODate);
......@@ -47,17 +41,18 @@
});
test("By default, maximum precision is kept, but it can be changed later", function () {
var d = new JIODate();
test("By default, maximum precision is kept, " +
"but it can be changed later", function () {
var d = new JIODate();
equal(d.getPrecision(), jiodate.MSEC);
d.setPrecision(jiodate.SEC);
equal(d.getPrecision(), jiodate.SEC);
d.setPrecision(jiodate.DAY);
equal(d.getPrecision(), jiodate.DAY);
d.setPrecision(jiodate.MONTH);
equal(d.getPrecision(), jiodate.MONTH);
});
equal(d.getPrecision(), jiodate.MSEC);
d.setPrecision(jiodate.SEC);
equal(d.getPrecision(), jiodate.SEC);
d.setPrecision(jiodate.DAY);
equal(d.getPrecision(), jiodate.DAY);
d.setPrecision(jiodate.MONTH);
equal(d.getPrecision(), jiodate.MONTH);
});
test("Passing a JIODate object to the constructor clones it", function () {
......@@ -222,69 +217,68 @@
});
test("Comparison between heterogeneous values is done with the lesser precision", function () {
var dmsec = JIODate('2012-05-02 06:07:08.989'),
dsec = JIODate('2012-05-02 06:07:08'),
dmin = JIODate('2012-05-02 06:07'),
dhour = JIODate('2012-05-02 06'),
dday = JIODate('2012-05-02'),
dmonth = JIODate('2012-05'),
dyear = JIODate('2012');
strictEqual(dmsec.cmp(dsec), 0);
strictEqual(dmsec.cmp(dmin), 0);
strictEqual(dmsec.cmp(dhour), 0);
strictEqual(dmsec.cmp(dday), 0);
strictEqual(dmsec.cmp(dmonth), 0);
strictEqual(dmsec.cmp(dyear), 0);
strictEqual(dsec.cmp(dmsec), 0);
strictEqual(dsec.cmp(dmin), 0);
strictEqual(dsec.cmp(dhour), 0);
strictEqual(dsec.cmp(dday), 0);
strictEqual(dsec.cmp(dmonth), 0);
strictEqual(dsec.cmp(dyear), 0);
strictEqual(dmin.cmp(dmsec), 0);
strictEqual(dmin.cmp(dsec), 0);
strictEqual(dmin.cmp(dhour), 0);
strictEqual(dmin.cmp(dday), 0);
strictEqual(dmin.cmp(dmonth), 0);
strictEqual(dmin.cmp(dyear), 0);
strictEqual(dhour.cmp(dmsec), 0);
strictEqual(dhour.cmp(dsec), 0);
strictEqual(dhour.cmp(dmin), 0);
strictEqual(dhour.cmp(dday), 0);
strictEqual(dhour.cmp(dmonth), 0);
strictEqual(dhour.cmp(dyear), 0);
strictEqual(dday.cmp(dmsec), 0);
strictEqual(dday.cmp(dsec), 0);
strictEqual(dday.cmp(dmin), 0);
strictEqual(dday.cmp(dhour), 0);
strictEqual(dday.cmp(dmonth), 0);
strictEqual(dday.cmp(dyear), 0);
strictEqual(dmonth.cmp(dmsec), 0);
strictEqual(dmonth.cmp(dsec), 0);
strictEqual(dmonth.cmp(dmin), 0);
strictEqual(dmonth.cmp(dhour), 0);
strictEqual(dmonth.cmp(dday), 0);
strictEqual(dmonth.cmp(dyear), 0);
strictEqual(dyear.cmp(dmsec), 0);
strictEqual(dyear.cmp(dsec), 0);
strictEqual(dyear.cmp(dmin), 0);
strictEqual(dyear.cmp(dhour), 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:09')), -1);
});
}));
test("Comparison between heterogeneous values is done with " +
"the lesser precision", function () {
var dmsec = JIODate('2012-05-02 06:07:08.989'),
dsec = JIODate('2012-05-02 06:07:08'),
dmin = JIODate('2012-05-02 06:07'),
dhour = JIODate('2012-05-02 06'),
dday = JIODate('2012-05-02'),
dmonth = JIODate('2012-05'),
dyear = JIODate('2012');
strictEqual(dmsec.cmp(dsec), 0);
strictEqual(dmsec.cmp(dmin), 0);
strictEqual(dmsec.cmp(dhour), 0);
strictEqual(dmsec.cmp(dday), 0);
strictEqual(dmsec.cmp(dmonth), 0);
strictEqual(dmsec.cmp(dyear), 0);
strictEqual(dsec.cmp(dmsec), 0);
strictEqual(dsec.cmp(dmin), 0);
strictEqual(dsec.cmp(dhour), 0);
strictEqual(dsec.cmp(dday), 0);
strictEqual(dsec.cmp(dmonth), 0);
strictEqual(dsec.cmp(dyear), 0);
strictEqual(dmin.cmp(dmsec), 0);
strictEqual(dmin.cmp(dsec), 0);
strictEqual(dmin.cmp(dhour), 0);
strictEqual(dmin.cmp(dday), 0);
strictEqual(dmin.cmp(dmonth), 0);
strictEqual(dmin.cmp(dyear), 0);
strictEqual(dhour.cmp(dmsec), 0);
strictEqual(dhour.cmp(dsec), 0);
strictEqual(dhour.cmp(dmin), 0);
strictEqual(dhour.cmp(dday), 0);
strictEqual(dhour.cmp(dmonth), 0);
strictEqual(dhour.cmp(dyear), 0);
strictEqual(dday.cmp(dmsec), 0);
strictEqual(dday.cmp(dsec), 0);
strictEqual(dday.cmp(dmin), 0);
strictEqual(dday.cmp(dhour), 0);
strictEqual(dday.cmp(dmonth), 0);
strictEqual(dday.cmp(dyear), 0);
strictEqual(dmonth.cmp(dmsec), 0);
strictEqual(dmonth.cmp(dsec), 0);
strictEqual(dmonth.cmp(dmin), 0);
strictEqual(dmonth.cmp(dhour), 0);
strictEqual(dmonth.cmp(dday), 0);
strictEqual(dmonth.cmp(dyear), 0);
strictEqual(dyear.cmp(dmsec), 0);
strictEqual(dyear.cmp(dsec), 0);
strictEqual(dyear.cmp(dmin), 0);
strictEqual(dyear.cmp(dhour), 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:09')), -1);
});
}(QUnit, jiodate, moment));
/*jslint indent: 2, maxlen: 100, nomen: true */
/*global window, define, module, test_util, RSVP, jIO, local_storage, test, ok,
deepEqual, sinon, expect, stop, start, Blob, console */
// 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) {
/*jslint maxlen: 120, nomen: true */
/*global localStorage, test_util, console*/
(function (jIO, localStorage, QUnit) {
"use strict";
module("LocalStorage");
local_storage.clear();
var key_schema = {
var test = QUnit.test,
stop = QUnit.stop,
start = QUnit.start,
ok = QUnit.ok,
expect = QUnit.expect,
// deepEqual = QUnit.deepEqual,
module = QUnit.module,
key_schema;
key_schema = {
cast_lookup: {
dateType: function (obj) {
if (Object.prototype.toString.call(obj) === '[object Date]') {
......@@ -39,6 +29,17 @@
}
};
module("localStorage", {
setup: function () {
localStorage.clear();
this.jio = jIO.createJIO({
"type": "local"
}, {
"workspace": {}
});
}
});
test("AllDocs", function () {
expect(3);
......@@ -72,147 +73,159 @@
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
])
// .then(function () {
//
// // get a list of documents
// return jio.allDocs();
//
// })
// .then(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
// });
//
// })
// .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 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="../lib/qunit/qunit.css" />
<title>JIO Qunit/Sinon Unit Tests</title>
</head>
<body>
<div id="qunit"></div>
<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/sinon/pkg/sinon.js" type="text/javascript"></script>
<script src="../dist/jio-latest.js"></script>
<script src="html5.js"></script>
<script src="../lib/qunit/qunit.js"></script>
<script src="../lib/sinon/sinon.js"></script>
<script src="../lib/sinon/sinon-qunit.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="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-schema.tests.js"></script>
<script src="queries/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/key-jiodate.tests.js"></script>
<script src="../src/jio.storage/localstorage.js"></script>
<script src="queries/key-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="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="../src/jio.storage/gidstorage.js"></script>
......@@ -54,18 +50,16 @@
<script src="../src/jio.storage/splitstorage.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="../test/jio.storage/replicatestorage.tests.js"></script>
<!--
<script src="../src/jio.storage/indexeddbstorage.js"></script>
<script src="../test/jio.storage/indexeddbstorage.tests.js"></script>
-->
<script src="../test/jio.storage/replicatestorage.tests.js"></script-->
</head>
<body>
<h1 id="qunit-header">jIO Tests</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>
<!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