Commit 36070dd8 authored by Cédric Le Ninivin's avatar Cédric Le Ninivin

erp5_core: Update jio.js to version 3.13.0

parent c61cde5b
...@@ -7458,11 +7458,17 @@ return new Parser; ...@@ -7458,11 +7458,17 @@ return new Parser;
}; };
}(window, moment)); }(window, moment));
;/*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query, atob, ;/*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array */ FileReader, ArrayBuffer, Uint8Array, navigator */
(function (window, RSVP, Blob, QueryFactory, Query, atob, (function (window, RSVP, Blob, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array) { FileReader, ArrayBuffer, Uint8Array, navigator) {
"use strict"; "use strict";
if (window.openDatabase === undefined) {
window.openDatabase = function () {
throw new Error('WebSQL is not supported by ' + navigator.userAgent);
};
}
var util = {}, var util = {},
jIO; jIO;
...@@ -7569,8 +7575,46 @@ return new Parser; ...@@ -7569,8 +7575,46 @@ return new Parser;
} }
util.readBlobAsDataURL = readBlobAsDataURL; util.readBlobAsDataURL = readBlobAsDataURL;
function stringify(obj) {
// Implement a stable JSON.stringify
// Object's keys are alphabetically ordered
var key,
key_list,
i,
value,
result_list;
if (obj === undefined) {
return undefined;
}
if (obj.constructor === Object) {
key_list = Object.keys(obj).sort();
result_list = [];
for (i = 0; i < key_list.length; i += 1) {
key = key_list[i];
value = stringify(obj[key]);
if (value !== undefined) {
result_list.push(stringify(key) + ':' + value);
}
}
return '{' + result_list.join(',') + '}';
}
if (obj.constructor === Array) {
result_list = [];
for (i = 0; i < obj.length; i += 1) {
result_list.push(stringify(obj[i]));
}
return '[' + result_list.join(',') + ']';
}
return JSON.stringify(obj);
}
util.stringify = stringify;
// https://gist.github.com/davoclavo/4424731 // https://gist.github.com/davoclavo/4424731
function dataURItoBlob(dataURI) { function dataURItoBlob(dataURI) {
if (dataURI === 'data:') {
return new Blob();
}
// convert base64 to raw binary data held in a string // convert base64 to raw binary data held in a string
var byteString = atob(dataURI.split(',')[1]), var byteString = atob(dataURI.split(',')[1]),
// separate out the mime component // separate out the mime component
...@@ -7931,7 +7975,7 @@ return new Parser; ...@@ -7931,7 +7975,7 @@ return new Parser;
window.jIO = jIO; window.jIO = jIO;
}(window, RSVP, Blob, QueryFactory, Query, atob, }(window, RSVP, Blob, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array)); FileReader, ArrayBuffer, Uint8Array, navigator));
;/* ;/*
* Rusha, a JavaScript implementation of the Secure Hash Algorithm, SHA-1, * Rusha, a JavaScript implementation of the Secure Hash Algorithm, SHA-1,
* as defined in FIPS PUB 180-1, tuned for high performance with large inputs. * as defined in FIPS PUB 180-1, tuned for high performance with large inputs.
...@@ -8158,7 +8202,7 @@ return new Parser; ...@@ -8158,7 +8202,7 @@ return new Parser;
var ceilHeapSize = function (v) { var ceilHeapSize = function (v) {
// The asm.js spec says: // The asm.js spec says:
// The heap object's byteLength must be either // The heap object's byteLength must be either
// 2^n for n in [12, 24) or 2^24 * n for n ≥ 1. // 2^n for n in [12, 24) or 2^24 * n for n 1.
// Also, byteLengths smaller than 2^16 are deprecated. // Also, byteLengths smaller than 2^16 are deprecated.
var p; var p;
// If v is smaller than 2^16, the smallest possible solution // If v is smaller than 2^16, the smallest possible solution
...@@ -8370,7 +8414,7 @@ return new Parser; ...@@ -8370,7 +8414,7 @@ return new Parser;
/*jslint nomen: true*/ /*jslint nomen: true*/
/*global jIO, RSVP, Rusha*/ /*global jIO, RSVP, Rusha*/
(function (jIO, RSVP, Rusha) { (function (jIO, RSVP, Rusha, stringify) {
"use strict"; "use strict";
var rusha = new Rusha(), var rusha = new Rusha(),
...@@ -8397,14 +8441,14 @@ return new Parser; ...@@ -8397,14 +8441,14 @@ return new Parser;
this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage); this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage);
this._signature_hash = "_replicate_" + generateHash( this._signature_hash = "_replicate_" + generateHash(
JSON.stringify(spec.local_sub_storage) + stringify(spec.local_sub_storage) +
JSON.stringify(spec.remote_sub_storage) + stringify(spec.remote_sub_storage) +
JSON.stringify(this._query_options) stringify(this._query_options)
); );
this._signature_sub_storage = jIO.createJIO({ this._signature_sub_storage = jIO.createJIO({
type: "document", type: "document",
document_id: this._signature_hash, document_id: this._signature_hash,
sub_storage: spec.local_sub_storage sub_storage: spec.signature_storage || spec.local_sub_storage
}); });
this._use_remote_post = spec.use_remote_post || false; this._use_remote_post = spec.use_remote_post || false;
...@@ -8540,40 +8584,45 @@ return new Parser; ...@@ -8540,40 +8584,45 @@ return new Parser;
}); });
} }
function checkLocalCreation(queue, source, destination, id, options, function propagateDeletion(destination, id) {
getMethod) { return destination.remove(id)
var remote_doc;
queue
.push(function () { .push(function () {
return destination.get(id); return context._signature_sub_storage.remove(id);
}) })
.push(function (doc) { .push(function () {
remote_doc = doc; skip_document_dict[id] = null;
});
}
function checkAndPropagate(status_hash, local_hash, doc,
source, destination, id,
conflict_force, conflict_revert,
conflict_ignore,
options) {
return destination.get(id)
.push(function (remote_doc) {
return [remote_doc, generateHash(stringify(remote_doc))];
}, function (error) { }, function (error) {
if ((error instanceof jIO.util.jIOError) && if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) { (error.status_code === 404)) {
// This document was never synced. return [null, null];
// Push it to the remote storage and store sync information
return;
} }
throw error; throw error;
}) })
.push(function () { .push(function (remote_list) {
// This document was never synced. var remote_doc = remote_list[0],
// Push it to the remote storage and store sync information remote_hash = remote_list[1];
return getMethod(id);
})
.push(function (doc) {
var local_hash = generateHash(JSON.stringify(doc)),
remote_hash;
if (remote_doc === undefined) {
return propagateModification(source, destination, doc, local_hash,
id, options);
}
remote_hash = generateHash(JSON.stringify(remote_doc));
if (local_hash === remote_hash) { if (local_hash === remote_hash) {
// Same document // Same modifications on both side
if (local_hash === null) {
// Deleted on both side, drop signature
return context._signature_sub_storage.remove(id)
.push(function () {
skip_document_dict[id] = null;
});
}
return context._signature_sub_storage.put(id, { return context._signature_sub_storage.put(id, {
"hash": local_hash "hash": local_hash
}) })
...@@ -8581,50 +8630,58 @@ return new Parser; ...@@ -8581,50 +8630,58 @@ return new Parser;
skip_document_dict[id] = null; skip_document_dict[id] = null;
}); });
} }
if (options.conflict_ignore === true) {
return; if ((remote_hash === status_hash) || (conflict_force === true)) {
} // Modified only locally. No conflict or force
if (options.conflict_force === true) { if (local_hash === null) {
return propagateModification(source, destination, doc, local_hash, // Deleted locally
id, options); return propagateDeletion(destination, id);
}
return propagateModification(source, destination, doc,
local_hash, id,
{use_post: ((options.use_post) &&
(remote_hash === null))});
} }
// Already exists on destination
throw new jIO.util.jIOError("Conflict on '" + id + "': " +
JSON.stringify(doc) + " !== " +
JSON.stringify(remote_doc),
409);
});
}
function checkBulkLocalCreation(queue, source, destination, id_list, // Conflict cases
options) { if (conflict_ignore === true) {
queue return;
.push(function () { }
return source.bulk(id_list);
})
.push(function (result_list) {
var i,
sub_queue = new RSVP.Queue();
function getResult(j) { if ((conflict_revert === true) || (local_hash === null)) {
return function (id) { // Automatically resolve conflict or force revert
if (id !== id_list[j].parameter_list[0]) { if (remote_hash === null) {
throw new Error("Does not access expected ID " + id); // Deleted remotely
} return propagateDeletion(source, id);
return result_list[j]; }
}; return propagateModification(
destination,
source,
remote_doc,
remote_hash,
id,
{use_post: ((options.use_revert_post) &&
(local_hash === null))}
);
} }
for (i = 0; i < result_list.length; i += 1) { // Minimize conflict if it can be resolved
checkLocalCreation(sub_queue, source, destination, if (remote_hash === null) {
id_list[i].parameter_list[0], // Copy remote modification remotely
options, getResult(i)); return propagateModification(source, destination, doc,
local_hash, id,
{use_post: options.use_post});
} }
return sub_queue; throw new jIO.util.jIOError("Conflict on '" + id + "': " +
stringify(doc || '') + " !== " +
stringify(remote_doc || ''),
409);
}); });
} }
function checkLocalDeletion(queue, destination, id, source) { function checkLocalDeletion(queue, destination, id, source,
conflict_force, conflict_revert,
conflict_ignore, options) {
var status_hash; var status_hash;
queue queue
.push(function () { .push(function () {
...@@ -8632,93 +8689,57 @@ return new Parser; ...@@ -8632,93 +8689,57 @@ return new Parser;
}) })
.push(function (result) { .push(function (result) {
status_hash = result.hash; status_hash = result.hash;
return destination.get(id) return checkAndPropagate(status_hash, null, null,
.push(function (doc) { source, destination, id,
var remote_hash = generateHash(JSON.stringify(doc)); conflict_force, conflict_revert,
if (remote_hash === status_hash) { conflict_ignore,
return destination.remove(id) options);
.push(function () {
return context._signature_sub_storage.remove(id);
})
.push(function () {
skip_document_dict[id] = null;
});
}
// Modifications on remote side
// Push them locally
return propagateModification(destination, source, doc,
remote_hash, id);
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return context._signature_sub_storage.remove(id)
.push(function () {
skip_document_dict[id] = null;
});
}
throw error;
});
}); });
} }
function checkSignatureDifference(queue, source, destination, id, function checkSignatureDifference(queue, source, destination, id,
conflict_force, conflict_ignore, conflict_force, conflict_revert,
getMethod) { conflict_ignore,
is_creation, is_modification,
getMethod, options) {
queue queue
.push(function () { .push(function () {
return RSVP.all([ // Optimisation to save a get call to signature storage
getMethod(id), if (is_creation === true) {
context._signature_sub_storage.get(id) return RSVP.all([
]); getMethod(id),
{hash: null}
]);
}
if (is_modification === true) {
return RSVP.all([
getMethod(id),
context._signature_sub_storage.get(id)
]);
}
throw new jIO.util.jIOError("Unexpected call of"
+ " checkSignatureDifference",
409);
}) })
.push(function (result_list) { .push(function (result_list) {
var doc = result_list[0], var doc = result_list[0],
local_hash = generateHash(JSON.stringify(doc)), local_hash = generateHash(stringify(doc)),
status_hash = result_list[1].hash; status_hash = result_list[1].hash;
if (local_hash !== status_hash) { if (local_hash !== status_hash) {
// Local modifications return checkAndPropagate(status_hash, local_hash, doc,
return destination.get(id) source, destination, id,
.push(function (remote_doc) { conflict_force, conflict_revert,
var remote_hash = generateHash(JSON.stringify(remote_doc)); conflict_ignore,
if (remote_hash !== status_hash) { options);
// Modifications on both sides
if (local_hash === remote_hash) {
// Same modifications on both side \o/
return context._signature_sub_storage.put(id, {
"hash": local_hash
})
.push(function () {
skip_document_dict[id] = null;
});
}
if (conflict_ignore === true) {
return;
}
if (conflict_force !== true) {
throw new jIO.util.jIOError("Conflict on '" + id + "': " +
JSON.stringify(doc) + " !== " +
JSON.stringify(remote_doc),
409);
}
}
return propagateModification(source, destination, doc,
local_hash, id);
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
// Document has been deleted remotely
return propagateModification(source, destination, doc,
local_hash, id);
}
throw error;
});
} }
}); });
} }
function checkBulkSignatureDifference(queue, source, destination, id_list, function checkBulkSignatureDifference(queue, source, destination, id_list,
conflict_force, conflict_ignore) { document_status_list, options,
conflict_force, conflict_revert,
conflict_ignore) {
queue queue
.push(function () { .push(function () {
return source.bulk(id_list); return source.bulk(id_list);
...@@ -8739,8 +8760,11 @@ return new Parser; ...@@ -8739,8 +8760,11 @@ return new Parser;
for (i = 0; i < result_list.length; i += 1) { for (i = 0; i < result_list.length; i += 1) {
checkSignatureDifference(sub_queue, source, destination, checkSignatureDifference(sub_queue, source, destination,
id_list[i].parameter_list[0], id_list[i].parameter_list[0],
conflict_force, conflict_ignore, conflict_force, conflict_revert,
getResult(i)); conflict_ignore,
document_status_list[i].is_creation,
document_status_list[i].is_modification,
getResult(i), options);
} }
return sub_queue; return sub_queue;
}); });
...@@ -8751,6 +8775,9 @@ return new Parser; ...@@ -8751,6 +8775,9 @@ return new Parser;
if (!options.hasOwnProperty("use_post")) { if (!options.hasOwnProperty("use_post")) {
options.use_post = false; options.use_post = false;
} }
if (!options.hasOwnProperty("use_revert_post")) {
options.use_revert_post = false;
}
return queue return queue
.push(function () { .push(function () {
return RSVP.all([ return RSVP.all([
...@@ -8761,9 +8788,11 @@ return new Parser; ...@@ -8761,9 +8788,11 @@ return new Parser;
.push(function (result_list) { .push(function (result_list) {
var i, var i,
local_dict = {}, local_dict = {},
new_list = [], document_list = [],
change_list = [], document_status_list = [],
signature_dict = {}, signature_dict = {},
is_modification,
is_creation,
key; key;
for (i = 0; i < result_list[0].data.total_rows; i += 1) { for (i = 0; i < result_list[0].data.total_rows; i += 1) {
if (!skip_document_dict.hasOwnProperty( if (!skip_document_dict.hasOwnProperty(
...@@ -8779,55 +8808,53 @@ return new Parser; ...@@ -8779,55 +8808,53 @@ return new Parser;
signature_dict[result_list[1].data.rows[i].id] = i; signature_dict[result_list[1].data.rows[i].id] = i;
} }
} }
for (key in local_dict) {
if (options.check_creation === true) { if (local_dict.hasOwnProperty(key)) {
for (key in local_dict) { is_modification = signature_dict.hasOwnProperty(key)
if (local_dict.hasOwnProperty(key)) { && options.check_modification;
if (!signature_dict.hasOwnProperty(key)) { is_creation = !signature_dict.hasOwnProperty(key)
if (options.use_bulk_get === true) { && options.check_creation;
new_list.push({ if (is_modification === true || is_creation === true) {
method: "get", if (options.use_bulk_get === true) {
parameter_list: [key] document_list.push({
}); method: "get",
} else { parameter_list: [key]
checkLocalCreation(queue, source, destination, key, });
options, source.get.bind(source)); document_status_list.push({
} is_creation: is_creation,
is_modification: is_modification
});
} else {
checkSignatureDifference(queue, source, destination, key,
options.conflict_force,
options.conflict_revert,
options.conflict_ignore,
is_creation, is_modification,
source.get.bind(source),
options);
} }
} }
} }
if ((options.use_bulk_get === true) && (new_list.length !== 0)) {
checkBulkLocalCreation(queue, source, destination, new_list,
options);
}
} }
for (key in signature_dict) { if (options.check_deletion === true) {
if (signature_dict.hasOwnProperty(key)) { for (key in signature_dict) {
if (local_dict.hasOwnProperty(key)) { if (signature_dict.hasOwnProperty(key)) {
if (options.check_modification === true) { if (!local_dict.hasOwnProperty(key)) {
if (options.use_bulk_get === true) { checkLocalDeletion(queue, destination, key, source,
change_list.push({ options.conflict_force,
method: "get", options.conflict_revert,
parameter_list: [key] options.conflict_ignore,
}); options);
} else {
checkSignatureDifference(queue, source, destination, key,
options.conflict_force,
options.conflict_ignore,
source.get.bind(source));
}
}
} else {
if (options.check_deletion === true) {
checkLocalDeletion(queue, destination, key, source);
} }
} }
} }
} }
if ((options.use_bulk_get === true) && (change_list.length !== 0)) { if ((options.use_bulk_get === true) && (document_list.length !== 0)) {
checkBulkSignatureDifference(queue, source, destination, checkBulkSignatureDifference(queue, source, destination,
change_list, document_list, document_status_list,
options,
options.conflict_force, options.conflict_force,
options.conflict_revert,
options.conflict_ignore); options.conflict_ignore);
} }
}); });
...@@ -8879,10 +8906,10 @@ return new Parser; ...@@ -8879,10 +8906,10 @@ return new Parser;
use_post: context._use_remote_post, use_post: context._use_remote_post,
conflict_force: (context._conflict_handling === conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL), CONFLICT_KEEP_LOCAL),
conflict_ignore: ((context._conflict_handling === conflict_revert: (context._conflict_handling ===
CONFLICT_CONTINUE) || CONFLICT_KEEP_REMOTE),
(context._conflict_handling === conflict_ignore: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE)), CONFLICT_CONTINUE),
check_modification: context._check_local_modification, check_modification: context._check_local_modification,
check_creation: context._check_local_creation, check_creation: context._check_local_creation,
check_deletion: context._check_local_deletion check_deletion: context._check_local_deletion
...@@ -8907,8 +8934,11 @@ return new Parser; ...@@ -8907,8 +8934,11 @@ return new Parser;
return pushStorage(context._remote_sub_storage, return pushStorage(context._remote_sub_storage,
context._local_sub_storage, { context._local_sub_storage, {
use_bulk_get: use_bulk_get, use_bulk_get: use_bulk_get,
use_revert_post: context._use_remote_post,
conflict_force: (context._conflict_handling === conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE), CONFLICT_KEEP_REMOTE),
conflict_revert: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL),
conflict_ignore: (context._conflict_handling === conflict_ignore: (context._conflict_handling ===
CONFLICT_CONTINUE), CONFLICT_CONTINUE),
check_modification: context._check_remote_modification, check_modification: context._check_remote_modification,
...@@ -8921,7 +8951,7 @@ return new Parser; ...@@ -8921,7 +8951,7 @@ return new Parser;
jIO.addStorage('replicate', ReplicateStorage); jIO.addStorage('replicate', ReplicateStorage);
}(jIO, RSVP, Rusha)); }(jIO, RSVP, Rusha, jIO.util.stringify));
;/* ;/*
* Copyright 2015, Nexedi SA * Copyright 2015, Nexedi SA
* Released under the LGPL license. * Released under the LGPL license.
...@@ -10898,7 +10928,7 @@ return new Parser; ...@@ -10898,7 +10928,7 @@ return new Parser;
sub_query, sub_query,
result_list, result_list,
local_roles, local_roles,
tmp_list = []; sort_list = [];
if (options.query) { if (options.query) {
parsed_query = jIO.QueryFactory.create(options.query); parsed_query = jIO.QueryFactory.create(options.query);
...@@ -10942,9 +10972,8 @@ return new Parser; ...@@ -10942,9 +10972,8 @@ return new Parser;
if (options.sort_on) { if (options.sort_on) {
for (i = 0; i < options.sort_on.length; i += 1) { for (i = 0; i < options.sort_on.length; i += 1) {
tmp_list.push(JSON.stringify(options.sort_on[i])); sort_list.push(JSON.stringify(options.sort_on[i]));
} }
options.sort_on = tmp_list;
} }
return jIO.util.ajax({ return jIO.util.ajax({
...@@ -10955,7 +10984,7 @@ return new Parser; ...@@ -10955,7 +10984,7 @@ return new Parser;
// XXX Force erp5 to return embedded document // XXX Force erp5 to return embedded document
select_list: options.select_list || ["title", "reference"], select_list: options.select_list || ["title", "reference"],
limit: options.limit, limit: options.limit,
sort_on: options.sort_on, sort_on: sort_list,
local_roles: local_roles local_roles: local_roles
}), }),
"xhrFields": { "xhrFields": {
...@@ -11827,9 +11856,11 @@ return new Parser; ...@@ -11827,9 +11856,11 @@ return new Parser;
*/ */
/*jslint nomen: true */ /*jslint nomen: true */
/*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange*/ /*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError, Event*/
(function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange) { (function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError) {
"use strict"; "use strict";
// Read only as changing it can lead to data corruption // Read only as changing it can lead to data corruption
...@@ -11888,7 +11919,14 @@ return new Parser; ...@@ -11888,7 +11919,14 @@ return new Parser;
if (request.result) { if (request.result) {
request.result.close(); request.result.close();
} }
reject(error); if ((error !== undefined) &&
(error.target instanceof IDBOpenDBRequest) &&
(error.target.error instanceof DOMError)) {
reject("Connection to: " + db_name + " failed: " +
error.target.error.message);
} else {
reject(error);
}
}; };
request.onabort = function () { request.onabort = function () {
...@@ -12249,7 +12287,7 @@ return new Parser; ...@@ -12249,7 +12287,7 @@ return new Parser;
}; };
jIO.addStorage("indexeddb", IndexedDBStorage); jIO.addStorage("indexeddb", IndexedDBStorage);
}(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange)); }(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError));
;/* ;/*
* Copyright 2015, Nexedi SA * Copyright 2015, Nexedi SA
* Released under the LGPL license. * Released under the LGPL license.
......
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