summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2014-02-06 22:27:20 +0200
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2014-02-12 12:38:31 +0200
commit1dc9c3a59137b1cb9a38669938ba865154100c64 (patch)
tree28599445ef4a199ecde352afa495c4a918dd4641
parent8857a1753ba94c490211b06531995b6ad88506a6 (diff)
downloadacf2-1dc9c3a59137b1cb9a38669938ba865154100c64.tar.bz2
acf2-1dc9c3a59137b1cb9a38669938ba865154100c64.tar.xz
web client: transaction module
-rw-r--r--web/client.js424
-rw-r--r--web/transaction.js416
2 files changed, 419 insertions, 421 deletions
diff --git a/web/client.js b/web/client.js
index 81ef869..d289890 100644
--- a/web/client.js
+++ b/web/client.js
@@ -21,6 +21,7 @@ require(
[
"acf2/path",
"acf2/statusbar",
+ "acf2/transaction",
"acf2/type",
"jquery",
"underscore",
@@ -28,7 +29,7 @@ require(
"jquery-blockui",
"jquery-ui/sortable"
],
- function(pth, statusBar, type, $, _) {
+ function(pth, statusBar, txnMgr, type, $, _) {
$("#login").submit(function() {
$.ajax("/login", {
@@ -38,429 +39,10 @@ require(
})
}).done(function(data, status, xhr) {
- var txnMgr = (function(token, saveRequired) {
- var txn, changed, invalid;
-
- function reset() {
- txn = null;
- changed = {};
- invalid = {};
- }
- reset();
-
- function isValid() { return !(_.size(invalid)); }
-
- function request(url, options) {
- options = options || {};
- options.headers = {"X-ACF-Auth-Token": token};
- if (txn) options.headers["X-ACF-Transaction-ID"] = txn;
- if (options.data != undefined)
- options.data = JSON.stringify(options.data);
- return $.ajax(url, options);
- }
-
- function abort() {
- request("/transaction", {type: "DELETE"});
- reset();
- }
-
- function objRequest(path, options) {
- return request("/config" + path, options);
- }
-
- function exclusive(task) {
- $.blockUI();
-
- var def = $.Deferred();
- function resolve(txnValid) { def.resolve(txnValid); }
- function reject() { def.reject(); }
-
- var tasks = _.filter(
- _.pluck(_.values(invalid), 1), function(d) {
- return d && d.state() == "pending";
- }
- );
-
- if (tasks.length)
- tasks[0].always(function() {
- exclusive(task).done(resolve).fail(reject);
- });
- else task().always($.unblockUI).done(resolve).fail(reject);
-
- return def;
- }
-
- function query(path) {
- var def = $.Deferred();
-
- objRequest(path).done(function(data) {
- if (type.isTreeNode(data.meta) && !_.size(data.data))
- data.data = type.isList(data.meta) ? [] : {};
-
- function index(name) {
- return _.isArray(data.data) ? name - 1 : name;
- }
-
- data.get = function(name, valid) {
- var p = pth.join(path, name);
- if (!valid && p in invalid) return invalid[p][0];
-
- if (data.meta.type == "set") {
- return _.contains(
- data.data, name
- ) ? name : null;
- }
-
- return data.data[index(name)];
- };
-
- data.metaRequest = function(name) {
- return request("/meta" + pth.join(path, name));
- };
-
- data.match = function(filter) {
- if (!filter) return true;
- return _.every(_.map(filter, function(values, key) {
- return _.contains(values, data.data[key]);
- }));
- };
-
- data.status = function(name) {
- var p = name ? pth.join(path, name) : path;
- function scan(objs) {
- return _.size(_.filter(
- _.keys(objs), function(obj) {
- return pth.isSubordinate(obj, p);
- }
- ));
- }
-
- if (scan(invalid)) return "invalid";
- if (scan(changed)) return "changed";
- return null;
- };
-
- data.validate = function() {
- var valid = true;
- if (data.meta.required) {
- valid = _.size(data.data);
- if (valid) delete invalid[path];
- else invalid[path] = [path];
- }
- return valid && isValid();
- };
-
- data.set = function(name, newValue) {
- var def = $.Deferred();
- function reject(xhr) { def.reject(xhr); }
-
- var mpath = pth.join(path, name);
-
- var value = data.get(name, true);
- if (value == undefined) value = null;
-
- var tn = _.isObject(newValue);
- var npv = tn ? mpath : newValue;
-
- function ignore(path) {
- _.each(_.keys(invalid), function(p) {
- if (pth.isSubordinate(p, path))
- delete invalid[p];
- });
- }
-
- function resolve() {
- if (mpath in invalid &&
- invalid[mpath][1] == def) {
-
- var del = invalid[mpath][0] == null;
-
- delete invalid[mpath];
- if (del) ignore(mpath);
- }
-
- def.resolve(isValid());
- }
-
- function validate() {
- var del = newValue == null;
- var set = data.meta.type == "set";
-
- var options;
- if (!del)
- options = {
- type: set ? "POST" : "PUT",
- data: newValue
- };
- else if (data.get(name, true) != null)
- options = {type: "DELETE"};
-
- if (!options) {
- if (data.meta.type == "model" &&
- _.findWhere(
- data.meta.fields, {name: name}
- ).required)
- def.reject("Required value not set");
- else resolve();
- return;
- }
-
- objRequest(
- set && !del ? path : mpath, options
- ).done(function() {
- if (!(mpath in changed))
- changed[mpath] = value;
- if (!tn && newValue == changed[mpath])
- delete changed[mpath];
-
- if (npv == null)
- _.each(
- _.keys(changed),
- function(p) {
- if (pth.isSubordinate(p, mpath, true))
- delete changed[p];
- }
- );
-
- if (data.meta.type == "list" && del)
- data.data.splice(name - 1, 1);
- else if (!set) data.data[index(name)] = npv;
- else if (del)
- data.data.splice(
- data.data.indexOf(name), 1
- );
- else data.data.push(name);
-
- data.validate();
-
- if (data.meta.type == "model")
- _.each(
- data.meta.fields, function(field) {
- if (field.condition &&
- field.condition[name] &&
- !_.contains(
- field.condition[name],
- newValue
- ))
- ignore(
- pth.join(path, field.name)
- );
- }
- );
-
- if (tn && !set)
- query(mpath).done(function(data) {
-
- if (mpath in invalid) {
- if (data.meta.type == "model")
- _.each(
- data.meta.fields,
- function(field) {
- var mmpath = pth.join(
- mpath,
- field.name
- );
- if (field.required &&
- data.match(
- field.condition
- ) &&
- !(mmpath in invalid) &&
- (type.isCollection(
- field
- ) || data.get(
- field.name
- ) == null)) {
- invalid[
- mmpath
- ] = [
- null
- ];
- }
- });
-
- else if (type.isCollection(data.meta) &&
- data.meta.required)
- invalid[mpath] = [mpath];
- }
-
- resolve();
- }).fail(reject);
-
- else resolve();
-
- }).fail(reject);
- }
-
- var prevTask;
- if (mpath in invalid) prevTask = invalid[mpath][1];
-
- invalid[mpath] = [npv, def];
-
- if (prevTask) prevTask.always(validate);
- else validate();
-
- return def;
- };
-
- data.add = function(name) {
- return data.set(name, name);
- };
-
- function adjustListIndex(oldIndex, newIndex) {
- var opath = pth.join(path, oldIndex);
- var npath = pth.join(path, newIndex);
- _.each(
- [changed, invalid],
- function(map) {
- _.each(
- _.keys(map),
- function(p) {
- if (pth.isSubordinate(
- p, opath
- )) {
- map[npath +
- p.substring(
- opath.length
- )] = map[p];
- delete map[p];
- }
- });
- }
- );
- }
-
- function adjustListIndices(start, end) {
- var offset = start < end ? 1 : -1;
- for (var i = start; i != end; i += offset)
- adjustListIndex(i + offset, i);
- }
-
- function _delete(name) {
- var def = $.Deferred();
- var length = data.data.length;
-
- data.set(name, null).done(function(txnValid) {
- if (type.isTreeNode(data.meta) &&
- data.meta.type != "set") {
-
- delete changed[pth.join(path, name)];
- changed[path] = path;
-
- if (data.meta.type == "list")
- adjustListIndices(name, length);
- }
- def.resolve(txnValid);
-
- }).fail(function() { def.reject(); });
-
- return def;
- }
-
- data.delete = function(name) {
- return exclusive(function() {
- return _delete(name);
- });
- };
-
- data.move = function(oldIndex, newIndex) {
- if (oldIndex == newIndex)
- return $.Deferred().resolve(isValid());
-
- var value = data.get(oldIndex);
- var length = data.data.length;
-
- return exclusive(function() {
- var def = $.Deferred();
- function reject() { def.reject(); }
-
- if (oldIndex > newIndex) oldIndex++;
- else newIndex++;
-
- objRequest(path, {type: "POST", data: {
- index: newIndex,
- data: type.isTreeNode(data.meta.members) ?
- pth.join(path, oldIndex) : value
- }}).done(function() {
-
- data.data.splice(newIndex - 1, 0, value);
-
- adjustListIndices(length + 1, newIndex);
- adjustListIndex(oldIndex, newIndex);
-
- data.delete(oldIndex)
- .done(function(txnValid) {
- def.resolve(txnValid);
- })
- .fail(reject);
-
- }).fail(reject);
-
- return def;
- });
- };
-
- data.invoke = function(name) {
- return objRequest(pth.join(path, name), {type: "POST"});
- };
-
- def.resolve(data);
- }).fail(function() { def.reject(); });
-
- return def;
- }
-
- return {
- start: function() {
- var def = $.Deferred();
- if (txn && isValid() && !(_.size(changed))) abort();
-
- if (txn)
- def.resolve();
-
- else request("/transaction", {type: "POST"})
- .done(function(data, status, xhr) {
- txn = xhr.getResponseHeader(
- "X-ACF-Transaction-ID"
- );
- def.resolve();
- })
- .fail(function() { def.reject(); });
-
- return def;
- },
-
- commit: function() {
- var def = $.Deferred();
- function reject(xhr) { def.reject(xhr); }
- request(
- "/transaction", {type: "PUT"}
- ).done(function() {
- reset();
- if (saveRequired)
- request(
- "/save", {type: "POST"}
- ).done(function() {
- def.resolve();
- }).fail(reject);
- else def.resolve();
- }).fail(reject);
- return def;
- },
-
- abort: abort,
-
- query: query,
-
- logout: function() {
- return request("/login", {type: "DELETE"});
- }
- };
- })(
+ txnMgr = txnMgr(
xhr.getResponseHeader("X-ACF-Auth-Token"),
xhr.getResponseHeader("X-ACF-Save-Required") == "1"
);
-
-
function formatError(msg, xhr) {
msg += " " + xhr.statusCode().status;
diff --git a/web/transaction.js b/web/transaction.js
new file mode 100644
index 0000000..9a9368b
--- /dev/null
+++ b/web/transaction.js
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2012-2014 Kaarle Ritvanen
+ * See LICENSE file for license details
+ */
+
+define(
+ ["acf2/path", "acf2/type", "jquery", "underscore", "jquery-blockui"],
+ function(pth, type, $, _) {
+ return function(token, saveRequired) {
+ var txn, changed, invalid;
+
+ function reset() {
+ txn = null;
+ changed = {};
+ invalid = {};
+ }
+ reset();
+
+ function isValid() { return !(_.size(invalid)); }
+
+ function request(url, options) {
+ options = options || {};
+ options.headers = {"X-ACF-Auth-Token": token};
+ if (txn) options.headers["X-ACF-Transaction-ID"] = txn;
+ if (options.data != undefined)
+ options.data = JSON.stringify(options.data);
+ return $.ajax(url, options);
+ }
+
+ function abort() {
+ request("/transaction", {type: "DELETE"});
+ reset();
+ }
+
+ function objRequest(path, options) {
+ return request("/config" + path, options);
+ }
+
+ function exclusive(task) {
+ $.blockUI();
+
+ var def = $.Deferred();
+ function resolve(txnValid) { def.resolve(txnValid); }
+ function reject() { def.reject(); }
+
+ var tasks = _.filter(
+ _.pluck(_.values(invalid), 1), function(d) {
+ return d && d.state() == "pending";
+ }
+ );
+
+ if (tasks.length)
+ tasks[0].always(function() {
+ exclusive(task).done(resolve).fail(reject);
+ });
+ else task().always($.unblockUI).done(resolve).fail(reject);
+
+ return def;
+ }
+
+ function query(path) {
+ var def = $.Deferred();
+
+ objRequest(path).done(function(data) {
+ if (type.isTreeNode(data.meta) && !_.size(data.data))
+ data.data = type.isList(data.meta) ? [] : {};
+
+ function index(name) {
+ return _.isArray(data.data) ? name - 1 : name;
+ }
+
+ data.get = function(name, valid) {
+ var p = pth.join(path, name);
+ if (!valid && p in invalid) return invalid[p][0];
+
+ if (data.meta.type == "set")
+ return _.contains(data.data, name) ? name : null;
+
+ return data.data[index(name)];
+ };
+
+ data.metaRequest = function(name) {
+ return request("/meta" + pth.join(path, name));
+ };
+
+ data.match = function(filter) {
+ if (!filter) return true;
+ return _.every(_.map(filter, function(values, key) {
+ return _.contains(values, data.data[key]);
+ }));
+ };
+
+ data.status = function(name) {
+ var p = name ? pth.join(path, name) : path;
+ function scan(objs) {
+ return _.size(_.filter(
+ _.keys(objs), function(obj) {
+ return pth.isSubordinate(obj, p);
+ }
+ ));
+ }
+
+ if (scan(invalid)) return "invalid";
+ if (scan(changed)) return "changed";
+ return null;
+ };
+
+ data.validate = function() {
+ var valid = true;
+ if (data.meta.required) {
+ valid = _.size(data.data);
+ if (valid) delete invalid[path];
+ else invalid[path] = [path];
+ }
+ return valid && isValid();
+ };
+
+ data.set = function(name, newValue) {
+ var def = $.Deferred();
+ function reject(xhr) { def.reject(xhr); }
+
+ var mpath = pth.join(path, name);
+
+ var value = data.get(name, true);
+ if (value == undefined) value = null;
+
+ var tn = _.isObject(newValue);
+ var npv = tn ? mpath : newValue;
+
+ function ignore(path) {
+ _.each(_.keys(invalid), function(p) {
+ if (pth.isSubordinate(p, path))
+ delete invalid[p];
+ });
+ }
+
+ function resolve() {
+ if (mpath in invalid &&
+ invalid[mpath][1] == def) {
+
+ var del = invalid[mpath][0] == null;
+
+ delete invalid[mpath];
+ if (del) ignore(mpath);
+ }
+
+ def.resolve(isValid());
+ }
+
+ function validate() {
+ var del = newValue == null;
+ var set = data.meta.type == "set";
+
+ var options;
+ if (!del)
+ options = {
+ type: set ? "POST" : "PUT", data: newValue
+ };
+ else if (data.get(name, true) != null)
+ options = {type: "DELETE"};
+
+ if (!options) {
+ if (data.meta.type == "model" &&
+ _.findWhere(
+ data.meta.fields, {name: name}
+ ).required)
+ def.reject("Required value not set");
+ else resolve();
+ return;
+ }
+
+ objRequest(
+ set && !del ? path : mpath, options
+ ).done(function() {
+ if (!(mpath in changed))
+ changed[mpath] = value;
+ if (!tn && newValue == changed[mpath])
+ delete changed[mpath];
+
+ if (npv == null)
+ _.each(
+ _.keys(changed),
+ function(p) {
+ if (pth.isSubordinate(
+ p, mpath, true
+ ))
+ delete changed[p];
+ }
+ );
+
+ if (data.meta.type == "list" && del)
+ data.data.splice(name - 1, 1);
+ else if (!set) data.data[index(name)] = npv;
+ else if (del)
+ data.data.splice(
+ data.data.indexOf(name), 1
+ );
+ else data.data.push(name);
+
+ data.validate();
+
+ if (data.meta.type == "model")
+ _.each(
+ data.meta.fields, function(field) {
+ if (field.condition &&
+ field.condition[name] &&
+ !_.contains(
+ field.condition[name],
+ newValue
+ ))
+ ignore(
+ pth.join(path, field.name)
+ );
+ }
+ );
+
+ if (tn && !set)
+ query(mpath).done(function(data) {
+
+ if (mpath in invalid) {
+ if (data.meta.type == "model")
+ _.each(
+ data.meta.fields,
+ function(field) {
+ var mmpath = pth.join(
+ mpath,
+ field.name
+ );
+ if (field.required &&
+ data.match(
+ field.condition
+ ) &&
+ !(mmpath in invalid) &&
+ (type.isCollection(
+ field
+ ) || data.get(
+ field.name
+ ) == null)) {
+ invalid[mmpath] = [
+ null
+ ];
+ }
+ });
+
+ else if (
+ type.isCollection(data.meta) &&
+ data.meta.required
+ )
+ invalid[mpath] = [mpath];
+ }
+
+ resolve();
+ }).fail(reject);
+
+ else resolve();
+
+ }).fail(reject);
+ }
+
+ var prevTask;
+ if (mpath in invalid) prevTask = invalid[mpath][1];
+
+ invalid[mpath] = [npv, def];
+
+ if (prevTask) prevTask.always(validate);
+ else validate();
+
+ return def;
+ };
+
+ data.add = function(name) {
+ return data.set(name, name);
+ };
+
+ function adjustListIndex(oldIndex, newIndex) {
+ var opath = pth.join(path, oldIndex);
+ var npath = pth.join(path, newIndex);
+ _.each(
+ [changed, invalid],
+ function(map) {
+ _.each(
+ _.keys(map),
+ function(p) {
+ if (pth.isSubordinate(
+ p, opath
+ )) {
+ map[npath + p.substring(
+ opath.length
+ )] = map[p];
+ delete map[p];
+ }
+ });
+ }
+ );
+ }
+
+ function adjustListIndices(start, end) {
+ var offset = start < end ? 1 : -1;
+ for (var i = start; i != end; i += offset)
+ adjustListIndex(i + offset, i);
+ }
+
+ function _delete(name) {
+ var def = $.Deferred();
+ var length = data.data.length;
+
+ data.set(name, null).done(function(txnValid) {
+ if (type.isTreeNode(data.meta) &&
+ data.meta.type != "set") {
+
+ delete changed[pth.join(path, name)];
+ changed[path] = path;
+
+ if (data.meta.type == "list")
+ adjustListIndices(name, length);
+ }
+ def.resolve(txnValid);
+
+ }).fail(function() { def.reject(); });
+
+ return def;
+ }
+
+ data.delete = function(name) {
+ return exclusive(function() { return _delete(name); });
+ };
+
+ data.move = function(oldIndex, newIndex) {
+ if (oldIndex == newIndex)
+ return $.Deferred().resolve(isValid());
+
+ var value = data.get(oldIndex);
+ var length = data.data.length;
+
+ return exclusive(function() {
+ var def = $.Deferred();
+ function reject() { def.reject(); }
+
+ if (oldIndex > newIndex) oldIndex++;
+ else newIndex++;
+
+ objRequest(path, {type: "POST", data: {
+ index: newIndex,
+ data: type.isTreeNode(data.meta.members) ?
+ pth.join(path, oldIndex) : value
+ }}).done(function() {
+
+ data.data.splice(newIndex - 1, 0, value);
+
+ adjustListIndices(length + 1, newIndex);
+ adjustListIndex(oldIndex, newIndex);
+
+ data.delete(oldIndex)
+ .done(function(txnValid) {
+ def.resolve(txnValid);
+ })
+ .fail(reject);
+
+ }).fail(reject);
+
+ return def;
+ });
+ };
+
+ data.invoke = function(name) {
+ return objRequest(pth.join(path, name), {type: "POST"});
+ };
+
+ def.resolve(data);
+ }).fail(function() { def.reject(); });
+
+ return def;
+ }
+
+ return {
+ start: function() {
+ var def = $.Deferred();
+ if (txn && isValid() && !(_.size(changed))) abort();
+
+ if (txn) def.resolve();
+
+ else request("/transaction", {type: "POST"})
+ .done(function(data, status, xhr) {
+ txn = xhr.getResponseHeader("X-ACF-Transaction-ID");
+ def.resolve();
+ })
+ .fail(function() { def.reject(); });
+
+ return def;
+ },
+
+ commit: function() {
+ var def = $.Deferred();
+ function reject(xhr) { def.reject(xhr); }
+ request("/transaction", {type: "PUT"}).done(function() {
+ reset();
+ if (saveRequired)
+ request("/save", {type: "POST"}).done(function() {
+ def.resolve();
+ }).fail(reject);
+ else def.resolve();
+ }).fail(reject);
+ return def;
+ },
+
+ abort: abort,
+
+ query: query,
+
+ logout: function() {
+ return request("/login", {type: "DELETE"});
+ }
+ };
+ }
+ }
+);