summaryrefslogtreecommitdiffstats
path: root/web/client.js
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2014-02-06 23:09:13 +0200
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2014-02-14 22:35:56 +0200
commit38ac196b2f00c1e13a0928cd892384bc296c8a98 (patch)
tree473663650b5b4fd898bd9bab4c666dca6b900dd4 /web/client.js
parent7d434464a843a8ca8acc903367492186a0ae13e8 (diff)
downloadaconf-38ac196b2f00c1e13a0928cd892384bc296c8a98.tar.bz2
aconf-38ac196b2f00c1e13a0928cd892384bc296c8a98.tar.xz
web client: extract each widget to a dedicated file
Diffstat (limited to 'web/client.js')
-rw-r--r--web/client.js793
1 files changed, 8 insertions, 785 deletions
diff --git a/web/client.js b/web/client.js
index 24d335f..4a9d396 100644
--- a/web/client.js
+++ b/web/client.js
@@ -25,13 +25,15 @@ require(
"acf2/statusbar",
"acf2/transaction",
"acf2/type",
+ "acf2/widget/inline",
"jquery",
"underscore",
+ "acf2/layout/stacked",
+ "acf2/layout/tabular",
"jquery-bbq",
- "jquery-blockui",
- "jquery-ui/sortable"
+ "jquery-blockui"
],
- function(dom, formatError, pth, statusBar, txnMgr, type, $, _) {
+ function(dom, formatError, pth, statusBar, txnMgr, type, Inline, $, _) {
$("#login").submit(function() {
$.ajax("/login", {
@@ -47,787 +49,6 @@ require(
);
- var Widget = {
- extend: function(spec) {
- var res = Object.create(this);
- for (key in spec) res[key] = spec[key];
- res.base = this;
- return res;
- },
-
- super: function() {
- var args = _.toArray(arguments);
- var cls = args.shift();
- var key = args.shift();
- return cls.base[key].apply(this, args);
- },
-
- new: function(data, name, meta, level, editable, removable) {
- return Object.create(this).init(
- data, name, meta, level, editable, removable
- );
- },
-
- init: function(data, name, meta, level, editable, removable) {
- this.data = data;
- this.name = name;
- this.meta = meta;
- this.level = level;
-
- var value = data.get(name);
- var status = data.status(name);
-
- if (!editable || !meta.editable) {
- var el = this.staticRender(value, meta);
- if (el) {
- dom.setStatus(el, status);
- return el;
- }
- }
-
- this.makeEl();
- this.dynamic = meta.dynamic;
-
- var self = this;
-
- var request;
- function handleRequest(req) {
- request = req;
- req.done(function(value, meta) {
- if (req != request) return;
- self.render(value, meta);
- self.setStatus(status);
- });
- }
-
- this.wrapped = this.wrap();
- this.visible = true;
-
- if (removable) {
- var link = dom.href().click(function() {
- data.delete(name).done(function(txnValid) {
- $("#content").trigger("reload", [txnValid]);
- })
- }).text("Delete");
- this.wrapped = dom.makeRow(this.wrapped);
- if (this.wrapped.is("tr")) link = $("<td>").html(link);
- this.wrapped.append(link);
- }
-
- handleRequest(this.requestData(value, meta));
-
- function validate() {
- request.done(function(value) {
- self.validate(value);
- });
- }
-
- this.wrapped.on("start", function(event) {
- if (data.status(name) == "invalid") validate();
- else self.setVisible();
- event.stopPropagation();
- });
-
- this.wrapped.on("updated", function(event, field) {
- if (self.dynamic) handleRequest(self.refreshData());
- if (!field ||
- self.dynamic ||
- (meta.condition && field in meta.condition))
- validate();
- event.stopPropagation();
- });
-
- return this.wrapped;
- },
-
- makeEl: function() { this.el = this.createEl(); },
-
- requestData: function(value, meta) {
- return $.Deferred().resolve(value, meta);
- },
-
- refreshData: function() {
- var def = $.Deferred();
- var self = this;
- this.data.metaRequest(this.name).done(function(data) {
- def.resolve(self.get(), data);
- });
- return def;
- },
-
- wrap: function() { return this.el; },
-
- showStatus: true,
-
- setStatus: function(status) {
- if (this.el && this.showStatus)
- dom.setStatus(this.statusEl(), status);
- },
-
- statusEl: function() { return this.el; },
-
- setVisible: function() {
- this.visible = this.data.match(this.meta.condition);
- if (this.wrapped)
- this.wrapped.trigger("setVisible", [this.visible]);
- },
-
- validate: function(value) { this.setVisible(); }
- };
-
-
- var Field = Widget.extend({
- init: function(
- data, name, meta, level, editable, removable
- ) {
- this.editable = editable && meta.editable;
-
- var el = this.super(
- Field,
- "init",
- data,
- name,
- meta,
- level,
- editable,
- removable
- );
-
- if (this.editable)
- this.field.change(_.bind(this.validateChange, this));
-
- return el;
- },
-
- staticRender: function(value, meta) {
- return $("<td>").text(value);
- },
-
- makeEl: function() {
- this.super(Field, "makeEl");
- if (!this.field) this.field = this.el;
- },
-
- createEl: function() { return $("<input>"); },
-
- render: function(value, meta) {
- this.field.attr({type: "text", value: value});
- },
-
- wrap: function() {
- var td = $("<td>");
- this.msg = $("<div>");
- td.append(this.msg);
- td.append(this.el);
- return td;
- },
-
- validate: function(value) {
- this.super(Field, "validate", value);
- if (!this.visible || !this.editable) return;
- this.validateChange();
- },
-
- validateChange: function() {
- this.msg.text("[checking]");
- statusBar.setError("Validating changes", "validate");
-
- var self = this;
-
- this.data.set(this.name, this.get())
- .done(function(txnValid) {
- self.msg.empty()
- self.setStatus(self.data.status(self.name));
- statusBar.validationReady(txnValid);
- self.el.trigger("validated");
-
- })
- .fail(function(xhr) {
- if (_.isString(xhr)) self.msg.text(xhr);
-
- else if (xhr.statusCode().status == 422)
- self.msg.html(
- _.reduce(
- _.map(
- $.parseJSON(xhr.responseText),
- _.escape
- ),
- function(a, b) {
- return a + "<br/>" + b;
- }
- )
- );
-
- else self.msg.text(formatError("Error", xhr));
-
- self.setStatus("invalid");
- statusBar.validationReady(false);
- });
- },
-
- get: function() {
- return this.editable ? (
- this.field.val() || null
- ) : this.data.get(this.name);
- }
- });
-
-
- var ComboBox = Field.extend({
- createEl: function() { return $("<select>"); },
-
- staticRender: function(value, meta) {
- return this.super(
- ComboBox,
- "staticRender",
- _.findWhere(meta.choice, {value: value})["ui-value"],
- meta
- );
- },
-
- render: function(value, meta) {
- var el = this.field.empty();
-
- function opt(value, ui_value, selected) {
- el.append($("<option>").attr(
- {value: value, selected: selected}
- ).text(ui_value));
- }
-
- if (!meta.required) opt("", "(none)", value == null);
-
- _.each(
- meta.choice,
- function(choice) {
- var selected = value == choice.value;
- if (choice.enabled || selected)
- opt(choice.value, choice["ui-value"], selected);
- }
- );
- }
- });
-
- var CheckBox = Field.extend({
- staticRender: function(value, meta) {
- return $("<td>").text(value ? "Yes" : "No");
- },
-
- statusEl: function() { return this.el.parent(); },
-
- render: function(value, meta) {
- this.field.attr({type: "checkbox", checked: value});
- },
-
- get: function() { return this.field.is(":checked"); }
- });
-
-
- var Link = Widget.extend({
- staticRender: function(value, meta) {
- return $("<td>").html(dom.objectRef(value));
- },
-
- createEl: dom.href,
-
- render: function(value, meta) { dom.objectRef(value, this.el) },
-
- wrap: function() { return $("<td>").html(this.el); },
-
- get: function() { return {}; }
- });
-
-
- var Inline = Link.extend({
- init: function(
- data, name, meta, level, editable, removable
- ) {
- return this.super(
- Inline,
- "init",
- data,
- name,
- meta,
- Math.min(6, level + 1),
- editable,
- removable
- );
- },
-
- staticRender: function(value, meta) { return null; },
-
- createEl: function() { return $("<div>"); },
-
- showStatus: false,
-
- requestData: function(value, meta) {
- this.path = value;
- return this.refreshData();
- },
-
- refreshData: function() {
- var def = $.Deferred();
- txnMgr.query(this.path).done(function(data) {
- def.resolve(data, data.meta);
- });
- return def;
- },
-
- showHeading: true,
-
- render: function(data, meta) {
- if (this.showHeading)
- this.el.html(
- $("<h" + this.level + ">").text(meta["ui-name"])
- );
- },
-
- wrap: function() { return this.el; },
-
- validate: function(data) {
- this.super(Inline, "validate", data);
-
- if (this.data.match(this.meta.condition)) {
- var valid = data.validate();
- this.setStatus(data.status());
- statusBar.validationReady(valid);
- }
-
- if (this.fields)
- _.each(this.fields, function(field) {
- field.trigger("updated");
- });
- }
- });
-
-
- var InlineFields = Inline.extend({
-
- render: function(data, meta) {
- this.super(InlineFields, "render", data, meta);
-
- this.appendAboveFields(data, meta);
-
- this.reqData = data;
- var self = this;
-
- if (meta.type == "model") {
- this.fields = {};
- _.each(meta.fields, function(field) {
- if (field.visible)
- self.fields[field.name] = self.renderField(
- field.name,
- field,
- field["ui-name"],
- true,
- false
- );
- });
-
- _.each(this.fields, function(f1, name) {
- _.each(self.fields, function(f2) {
- if (f1 != f2)
- f1.on("validated", function(event) {
- f2.trigger("updated", [name]);
- event.stopPropagation();
- });
- });
- });
-
- _.each(this.fields, function(field) {
- field.trigger("start");
- });
- }
-
- else _.each(data.data, function(value, name) {
- if (meta.type == "set") name = data.data[name];
- else if (_.isArray(data.data)) name++;
- self.renderCollectionMember(name, meta);
- });
-
- this.appendBelowFields(data, meta);
- },
-
- appendAboveFields: function(data, meta) {},
- appendBelowFields: function(data, meta) {},
-
- renderField: function(
- name, meta, label, editable, removable
- ) {
- var widget = this.widget(meta).new(
- this.reqData,
- name,
- meta,
- this.level,
- editable,
- removable
- );
- var container = this.appendWidget(widget, label);
- widget.on("setVisible", function(event, visible) {
- if (visible) container.show();
- else container.hide();
- event.stopPropagation();
- });
- return widget;
- },
-
- renderCollectionMember: function(name, meta) {
- var set = meta.type == "set";
- this.renderField(
- name,
- meta.members,
- meta["ui-member"] + " " + name,
- !set,
- !set && _.contains(meta.removable, name)
- ).trigger("start");
- },
-
- widget: function(meta) { return widgets[meta.widget]; }
- });
-
-
- var Horizontal = InlineFields.extend({
- createEl: function() {
- this.previous = null;
- return $("<tr>").html($("<td>").prop(
- "class", "placeholder"
- ));
- },
-
- showHeading: false,
-
- appendWidget: function(el, label) {
- if (!el.is("td")) return null;
- if (this.previous) this.previous.after(el);
- else {
- var ph = this.el.find(".placeholder");
- ph.after(el);
- ph.remove();
- }
- this.previous = el;
- return el;
- }
- });
-
-
- var HeaderHorizontal = Horizontal.extend({
- init: function(
- data, name, meta, level, editable, removable
- ) {
- this.header = $("<tr>");
- var table = $("<table>");
- table.append($("<thead>").html(this.header));
- table.append($("<tbody>").html(
- this.super(
- HeaderHorizontal,
- "init",
- data,
- name,
- meta,
- level,
- editable,
- removable
- )
- ));
- return table;
- },
-
- appendWidget: function(el, label) {
- el = this.super(
- HeaderHorizontal, "appendWidget", el, label
- );
- if (el) this.header.append($("<th>").text(label));
- return el;
- }
- });
-
-
- var Vertical = InlineFields.extend({
- wrap: function() { return $("<div>").html(this.el); },
-
- appendAboveFields: function(data, meta) {
- this.div = $("<div>");
- this.el.append(this.div);
-
- var self = this;
-
- if (meta.type == "model")
- _.each(meta.actions, function(action) {
- self.div.append($("<input>").attr(
- {type: "submit", value: action["ui-name"]}
- ).click(function() {
- data.invoke(action.name).done(function() {
- alert("Done");
- }).fail(function() {
- alert("Failed");
- });
- }));
- });
- },
-
- appendBelowFields: function(data, meta) {
- if (meta.editable &&
- _.contains(["collection", "list"], meta.type)) {
- if (!this.table) this.makeSortable(this.div);
-
- var self = this;
- var keys = _.clone(_.keys(data.data));
-
- var button = $("<input>").attr(
- {type: "submit", value: "Insert"}
- ).click(function() {
-
- var getter;
-
- function insert() {
- var name = getter();
-
- if (_.contains(keys, name)) {
- button.prop("class", null);
- return;
- }
- keys.push(name);
-
- data.set(
- name,
- type.isTreeNode(meta.members) ? {} : null
- ).done(function(txnValid) {
- if (_.isObject(meta.removable))
- meta.removable = [];
- meta.removable.push(name);
-
- self.renderCollectionMember(name, meta);
- button.prop("class", null);
- statusBar.validationReady(txnValid);
- });
- }
-
- button.prop("class", "hidden");
-
- if (meta.type == "collection") {
- var field = $("<input>").attr({type: "text"});
- var row = $("<tr>").html($("<td>").html(field));
- getter = function() {
- var res = field.val();
- row.remove();
- return res;
- }
- field.change(insert);
- self.appendRow(row);
- }
- else {
- getter = function() {
- return data.data.length + 1;
- };
- insert();
- }
- });
- this.el.append($("<p>").html(button));
- }
- },
-
- appendWidget: function(el, label) {
- var self = this;
- el = dom.makeRow(el);
-
- if (el.is("tr")) {
- el.prepend($("<td>").text(label));
- this.appendRow(el);
- }
- else {
- if (el.is("table")) {
- this.makeSortable(el.find("tbody"));
- var td;
- el.find("tr").each(function(index, row) {
- td = $("<td>");
- $(row).prepend(td);
- });
- td.text(label);
- this.table = el;
- }
- else this.table = null;
- this.div.append(el);
- }
-
- return el;
- },
-
- appendRow: function(row) {
- if (!this.table) {
- this.table = this.makeSortable($("<tbody>"));
- this.div.append($("<table>").html(this.table));
- }
- this.table.append(row);
- },
-
- makeSortable: function(el) {
- var data = this.reqData;
-
- if (data.meta.type == "list")
- el.sortable({
- start: function(event, ui) {
- $(":focus").change();
- ui.item.data("index", ui.item.index());
- },
- stop: function(event, ui) {
- var oldIndex = ui.item.data("index") + 1;
- var newIndex = ui.item.index() + 1;
- if (newIndex != oldIndex)
- data.move(oldIndex, newIndex)
- .done(function(txnValid) {
- $("#content").trigger(
- "reload", [txnValid]
- );
- });
- }
- });
- return el;
- }
- });
-
-
- var CheckBoxes = Inline.extend({
- showStatus: true,
-
- setStatus: function(status) {
- this.super(
- CheckBoxes,
- "setStatus",
- status == "invalid" ? "invalid" : null
- );
- },
-
- render: function(data, meta) {
- this.dynamic = meta.members.dynamic;
-
- this.super(CheckBoxes, "render", data, meta);
-
- var table = $("<tbody>");
- this.el.append($("<table>").html(table));
-
- var self = this;
-
- _.each(meta.members.choice, function(choice) {
- var selected = _.contains(data.data, choice.value);
- if (!(choice.enabled || selected)) return;
-
- var cbox = $("<input>").attr({
- type: "checkbox", checked: selected
- });
-
- var row = $("<tr>");
- row.append($("<td>").html(cbox));
-
- var item = $("<td>");
- if (choice.ref)
- item.html(dom.objectRef(choice.ref)
- .text(choice["ui-value"]));
- else item.text(choice["ui-value"]);
- row.append(item);
-
- function setRowStatus() {
- dom.setStatus(row, data.status(choice.value));
- }
- setRowStatus();
-
- cbox.change(function() {
- (
- cbox.is(":checked") ?
- data.add(choice.value) :
- data.delete(choice.value)
- ).done(function(txnValid) {
- self.setStatus(data.status());
- setRowStatus();
- statusBar.validationReady(txnValid);
- });
- });
-
- table.append(row);
- });
- }
- });
-
-
- var Reference = ComboBox.extend({
- init: function(
- data, name, meta, level, editable, removable
- ) {
- this.field = ComboBox.createEl();
- return this.super(
- Reference,
- "init",
- data,
- name,
- meta,
- level,
- editable,
- removable
- );
- },
-
- staticRender: function(value, meta) {
- return $("<td>").html(dom.objectRef(value).text(value));
- },
-
- statusEl: function() { return this.el.find("select"); },
-
- createEl: function() { return $("<div>"); },
-
- render: function(value, meta) {
- this.super(Reference, "render", value, meta);
-
- this.el.html(this.field);
- this.el.append(" ");
-
- var link = $("<div>");
- var update = _.bind(function() {
- link.html(dom.objectRef(this.get()));
- }, this);
- this.el.append(link);
-
- this.field.change(update);
- update();
- }
- });
-
-
- var widgets = {
- boolean: CheckBox,
- checkboxes: CheckBoxes,
- combobox: ComboBox,
- field: Field,
- inline: Vertical,
- link: Link,
- reference: Reference
- };
-
-
- var Tabular = Vertical.extend({
- render: function(data, meta) {
- this.header = true;
- this.super(Tabular, "render", data, meta);
- },
-
- widget: function(meta) {
- if (!type.isTreeNode(meta))
- return this.super(Tabular, "widget", meta);
- if (!this.header) return Horizontal;
- this.header = false;
- return HeaderHorizontal;
- }
- });
-
-
- var Stacked = Vertical.extend({
- widget: function(meta) {
- return type.isTreeNode(meta) ?
- Vertical : this.super(Stacked, "widget", meta);
- }
- });
-
-
- var layouts = {stacked: Stacked, tabular: Tabular};
-
-
function redirect(path) { $.bbq.pushState("#" + path); }
function renderObject(path, data) {
@@ -838,7 +59,9 @@ require(
).done(function(data) {
var layout = data.meta.widget;
var name = pth.split(path).pop();
- (layout ? layouts[layout] : Vertical).extend({
+ (layout ?
+ require("acf2/layout/" + layout) :
+ Inline).extend({
createEl: function() { return $("#content").empty(); },
wrap: function() { return this.el; },
requestData: function(value, meta) {