From 39585e03087158d37af2166aa47d06307c668d68 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Wed, 11 Sep 2013 15:12:49 +0300 Subject: web client: login/logout --- web/client.css | 2 +- web/client.html | 17 +- web/client.js | 1273 ++++++++++++++++++++++++++++--------------------------- 3 files changed, 676 insertions(+), 616 deletions(-) diff --git a/web/client.css b/web/client.css index 4e104e4..f0edb71 100644 --- a/web/client.css +++ b/web/client.css @@ -24,7 +24,7 @@ body { left: 15px; } -#status div { +#logout, #status div { position: absolute; top: 0.5em; right: 15px; diff --git a/web/client.html b/web/client.html index c2103c2..f8830e0 100644 --- a/web/client.html +++ b/web/client.html @@ -17,6 +17,7 @@

+ -
+
+
+ + + + + + + + + +
Username
Password
+ +
+
diff --git a/web/client.js b/web/client.js index 4305c4b..d0114ca 100644 --- a/web/client.js +++ b/web/client.js @@ -4,741 +4,786 @@ */ $(function() { - $.ajax("/login", { - type: "POST", - data: JSON.stringify({username: "admin", password: "admin"}) - }).done(function(data, status, xhr) { - - function split(path) { - var res = []; - while (path && path != "/") { - var comp = path.match(/^\/([^\\\/]|\\.)+/)[0]; - res.push(comp.substring(1)); - path = path.substring(comp.length); + $("#login").submit(function() { + + var statusBar = (function() { + function set(status, msg, mode) { + $("#status").prop("class", status); + $("#status p").text(msg); + $("#logout").prop("class", mode ? "hidden" : null); + $("#status div").prop("class", mode == "txn" ? null : "hidden"); + $("#commit").prop("disabled", status == "invalid"); } - return res; - } - function join(path, name) { - if (_.isString(name)) { - name = name.replace(/([\\\/])/g, "\\$1"); - if (!isNaN(Number(name))) name = "\\" + name; + return { + enableCommit: function() { + set("changed", "You have uncommitted changes", "txn"); + }, + setError: function(msg, mode) { set("invalid", msg, mode); }, + reset: function() { set(null, ""); } } - return (path == "/" ? "" : path) + "/" + name; - } - - function isRealSubordinate(p1, p2) { return !p1.indexOf(p2 + "/"); } - - function isSubordinate(p1, p2) { - return p1 == p2 || isRealSubordinate(p1, p2); - } + })(); - function isTreeNode(meta) { - return _.contains( - ["collection", "list", "model", "set"], meta.type - ); - } - - - var txnMgr = (function(token) { - var txn, changed, invalid; - - function reset() { - txn = null; - changed = {}; - invalid = {}; + $.ajax("/login", { + type: "POST", + data: JSON.stringify({ + username: $("#username").val(), password: $("#password").val() + }) + }).done(function(data, status, xhr) { + + function split(path) { + var res = []; + while (path && path != "/") { + var comp = path.match(/^\/([^\\\/]|\\.)+/)[0]; + res.push(comp.substring(1)); + path = path.substring(comp.length); + } + return res; } - reset(); - function isValid() { return !(_.size(invalid)); } + function join(path, name) { + if (_.isString(name)) { + name = name.replace(/([\\\/])/g, "\\$1"); + if (!isNaN(Number(name))) name = "\\" + name; + } + return (path == "/" ? "" : path) + "/" + name; + } - function request(url, options) { - options = options || {}; - options.headers = {"X-ACF-Auth-Token": token}; - if (txn) options.headers["X-ACF-Transaction-ID"] = txn; - return $.ajax(url, options); + function isRealSubordinate(p1, p2) { + return !p1.indexOf(p2 + "/"); } - function abort() { - var def = request("/", {type: "DELETE"}); - reset(); - return def; + function isSubordinate(p1, p2) { + return p1 == p2 || isRealSubordinate(p1, p2); } - - function objRequest(path, options) { - return request("/config" + path, options); + + function isTreeNode(meta) { + return _.contains( + ["collection", "list", "model", "set"], meta.type + ); } + - function query(path) { - var def = $.Deferred(); + var txnMgr = (function(token) { + var txn, changed, invalid; + + function reset() { + txn = null; + changed = {}; + invalid = {}; + } + reset(); - objRequest(path).done(function(data) { - function index(name) { - return _.isArray(data.data) ? name - 1 : name; - } + function isValid() { return !(_.size(invalid)); } - data.get = function(name, valid) { - var p = join(path, name); - return (!valid && p in invalid) ? - invalid[p][0] : data.data[index(name)]; - }; - - data.status = function(name) { - var p = join(path, name); - function scan(objs) { - return _.size(_.filter( - _.keys(objs), function(obj) { - return isSubordinate(obj, p); - } - )); - } + function request(url, options) { + options = options || {}; + options.headers = {"X-ACF-Auth-Token": token}; + if (txn) options.headers["X-ACF-Transaction-ID"] = txn; + return $.ajax(url, options); + } - if (scan(invalid)) return "invalid"; - if (scan(changed)) return "changed"; - return null; - } + function abort() { + var def = request("/", {type: "DELETE"}); + reset(); + return def; + } + + function objRequest(path, options) { + return request("/config" + path, options); + } + + function query(path) { + var def = $.Deferred(); - data.set = function(name, newValue) { - var def = $.Deferred(); + objRequest(path).done(function(data) { + function index(name) { + return _.isArray(data.data) ? name - 1 : name; + } - var mpath = join(path, name); - var value = data.get(name); - - var tn = _.isObject(newValue); - var npv = tn ? mpath : newValue; - - function validate() { - var options; - if (newValue != null) - options = { - type: "PUT", - data: JSON.stringify(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 def.resolve(); - return; + data.get = function(name, valid) { + var p = join(path, name); + return (!valid && p in invalid) ? + invalid[p][0] : data.data[index(name)]; + }; + + data.status = function(name) { + var p = join(path, name); + function scan(objs) { + return _.size(_.filter( + _.keys(objs), function(obj) { + return isSubordinate(obj, p); + } + )); } - - objRequest(mpath, options).done(function() { - if (!(mpath in changed)) - changed[mpath] = value; - if (!tn && newValue == changed[mpath]) - delete changed[mpath]; - else { - data.data[index(name)] = npv; - if (npv == null) - _.each(_.keys(changed), function(p) { - if (isRealSubordinate(p, mpath)) - delete changed[p]; - }); - } - - function resolve() { - if (mpath in invalid && - invalid[mpath][1] == def) { - var del = invalid[mpath][0] == null; - delete invalid[mpath]; + if (scan(invalid)) return "invalid"; + if (scan(changed)) return "changed"; + return null; + } - if (del) + data.set = function(name, newValue) { + var def = $.Deferred(); + + var mpath = join(path, name); + var value = data.get(name); + + var tn = _.isObject(newValue); + var npv = tn ? mpath : newValue; + + function validate() { + var options; + if (newValue != null) + options = { + type: "PUT", + data: JSON.stringify(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 def.resolve(); + return; + } + + objRequest(mpath, options).done(function() { + if (!(mpath in changed)) + changed[mpath] = value; + if (!tn && newValue == changed[mpath]) + delete changed[mpath]; + else { + data.data[index(name)] = npv; + if (npv == null) _.each( - _.keys(invalid), + _.keys(changed), function(p) { if (isRealSubordinate( p, mpath - )) delete invalid[p]; + )) delete changed[p]; } ); } - def.resolve(isValid()); - } - - if (tn) query(mpath).done(function(data) { - if (mpath in invalid && - data.meta.type == "model") - _.each( - data.meta.fields, - function(field) { - var mmpath = join( - mpath, field.name + function resolve() { + if (mpath in invalid && + invalid[mpath][1] == def) { + + var del = invalid[mpath][0] == null; + delete invalid[mpath]; + + if (del) + _.each( + _.keys(invalid), + function(p) { + if (isRealSubordinate( + p, mpath + )) delete invalid[p]; + } ); - if (field.required && - !(mmpath in invalid) && - data.get( - field.name - ) == null) - invalid[mmpath] = [null]; - }); - resolve(); - }).fail(function(xhr) { def.reject(xhr); }); - - else resolve(); - - }).fail(function(xhr) { def.reject(xhr); }); - } + } - var prevTask; - if (mpath in invalid) prevTask = invalid[mpath][1]; + def.resolve(isValid()); + } - invalid[mpath] = [npv, def]; + if (tn) query(mpath).done(function(data) { + if (mpath in invalid && + data.meta.type == "model") + _.each( + data.meta.fields, + function(field) { + var mmpath = join( + mpath, field.name + ); + if (field.required && + !(mmpath in invalid) && + data.get( + field.name + ) == null) + invalid[mmpath] = [ + null + ]; + }); + resolve(); + }).fail(function(xhr) { + def.reject(xhr); + }); + + else resolve(); + + }).fail(function(xhr) { def.reject(xhr); }); + } - if (prevTask) prevTask.always(validate); - else validate(); + var prevTask; + if (mpath in invalid) prevTask = invalid[mpath][1]; - return def; - }; + invalid[mpath] = [npv, def]; - data.delete = function(name) { - var def = $.Deferred(); + if (prevTask) prevTask.always(validate); + else validate(); - var tasks = _.filter( - _.pluck(_.values(invalid), 1), - function(d) { return d.state() == "pending"; } - ); + return def; + }; - if (tasks.length) - tasks[0].always(function() { - data.delete(name).done(function(txnValid) { - def.resolve(txnValid); - }).fail(function() { def.reject(); }); - }); + data.delete = function(name) { + var def = $.Deferred(); - else { - var length = data.data.length; - - data.set(name, null).done(function(txnValid) { - if (isTreeNode(data.meta)) { - delete changed[join(path, name)]; - changed[path] = path; - - if (data.meta.type == "list") - for (var i = name; i < length; i++) { - var opath = join(path, i + 1); - var npath = join(path, i); - - _.each( - [changed, invalid], - function(map) { - _.each( - _.keys(map), - function(p) { - if (isSubordinate( - p, opath - )) { - map[npath + - p.substring( - opath.length - )] = map[p]; - delete map[p]; - } - }); - } - ); - } - } - def.resolve(txnValid); + var tasks = _.filter( + _.pluck(_.values(invalid), 1), + function(d) { return d.state() == "pending"; } + ); - }).fail(function() { def.reject(); }); - } + if (tasks.length) + tasks[0].always(function() { + data.delete(name).done(function(txnValid) { + def.resolve(txnValid); + }).fail(function() { def.reject(); }); + }); + + else { + var length = data.data.length; + + data.set(name, null).done(function(txnValid) { + if (isTreeNode(data.meta)) { + delete changed[join(path, name)]; + changed[path] = path; + + if (data.meta.type == "list") + for (var i = name; i < length; i++) { + var opath = join(path, i + 1); + var npath = join(path, i); + + _.each( + [changed, invalid], + function(map) { + _.each( + _.keys(map), + function(p) { + if (isSubordinate( + p, opath + )) { + map[npath + + p.substring( + opath.length + )] = map[p]; + delete map[p]; + } + }); + } + ); + } + } + def.resolve(txnValid); - return def; - }; + }).fail(function() { def.reject(); }); + } - def.resolve(data); - }).fail(function() { def.reject(); }); - - return def; - } + return def; + }; - return { - start: function() { - var def = $.Deferred(); - if (txn && isValid() && !(_.size(changed))) abort(); + def.resolve(data); + }).fail(function() { def.reject(); }); + + return def; + } - if (txn) - def.resolve(); + return { + start: function() { + var def = $.Deferred(); + if (txn && isValid() && !(_.size(changed))) abort(); - else request("/", {type: "POST"}) - .done(function(data, status, xhr) { - txn = xhr.getResponseHeader("X-ACF-Transaction-ID"); + if (txn) def.resolve(); - }) - .fail(function() { def.reject(); }); - return def; - }, + else request("/", {type: "POST"}) + .done(function(data, status, xhr) { + txn = xhr.getResponseHeader( + "X-ACF-Transaction-ID" + ); + def.resolve(); + }) + .fail(function() { def.reject(); }); - commit: function() { - var def = $.Deferred(); - request("/", {type: "PUT"}).done(function() { - reset(); - def.resolve(); - }).fail(function(xhr) { def.reject(xhr); }); - return def; - }, + return def; + }, - abort: abort, + commit: function() { + var def = $.Deferred(); + request("/", {type: "PUT"}).done(function() { + reset(); + def.resolve(); + }).fail(function(xhr) { def.reject(xhr); }); + return def; + }, - query: query - }; - })(xhr.getResponseHeader("X-ACF-Auth-Token")); - + abort: abort, + query: query, - function href() { - return $("").attr({href: "javascript:void(0);"}); - } + logout: function() { + return request("/login", {type: "DELETE"}); + } + }; + })(xhr.getResponseHeader("X-ACF-Auth-Token")); + - var Field = { - format: function(value, status, label, level) { - var el = this.staticRender(value, level); - this.setElStatus(el, status); - if (label) return this.wrap(el, label).row; - return el; - }, - staticRender: function(value, level) { - return $("
").text(value); - }, + function href() { + return $("").attr({href: "javascript:void(0);"}); + } - setElStatus: function(el, status) { el.prop("class", status); }, + var Field = { + format: function(value, status, label, level) { + var el = this.staticRender(value, level); + this.setElStatus(el, status); + if (label) return this.wrap(el, label).row; + return el; + }, - wrap: function(el, label, remove) { - var row = $(""); - row.append($("").text(label)); + staticRender: function(value, level) { + return $("
").text(value); + }, - var td = $(""); - var msg = $("
"); - td.append(msg); - td.append(el); - row.append(td); + setElStatus: function(el, status) { + el.prop("class", status); + }, - if (remove) - row.append( - $("").html(href().click(remove).text("Delete")) - ); + wrap: function(el, label, remove) { + var row = $(""); + row.append($("").text(label)); - return {row: row, msg: msg}; - }, + var td = $(""); + var msg = $("
"); + td.append(msg); + td.append(el); + row.append(td); - init: function(value, meta, update, remove, label, level) { - this.el = this.render(value, meta, level); - this.el.change(update); - if (!label) return this.el; - this.els = this.wrap(this.el, label, remove); - return this.els.row; - }, + if (remove) + row.append( + $("").html(href().click(remove).text("Delete")) + ); - render: function(value, meta, level) { - return $("").attr({type: "text", value: value}); - }, + return {row: row, msg: msg}; + }, - setMessage: function(msg, html) { - if (html) this.els.msg.html(msg); - else this.els.msg.text(msg); - }, + init: function(value, meta, update, remove, label, level) { + this.el = this.render(value, meta, level); + this.el.change(update); + if (!label) return this.el; + this.els = this.wrap(this.el, label, remove); + return this.els.row; + }, - setStatus: function(status) { this.setElStatus(this.el, status); }, + render: function(value, meta, level) { + return $("").attr({type: "text", value: value}); + }, - get: function() { return this.el.val() || null; } - } + setMessage: function(msg, html) { + if (html) this.els.msg.html(msg); + else this.els.msg.text(msg); + }, - var ComboBox = Object.create(Field); - ComboBox.render = function(value, meta, level) { - var el = $(""); - _.each( - _.zip(meta.choice, meta["ui-choice"]), - function(choice) { - opt(choice[0], choice[1], value == choice[0]); + function opt(value, ui_value, selected) { + el.append($("