diff options
author | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2014-02-06 23:09:13 +0200 |
---|---|---|
committer | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2014-02-14 22:35:56 +0200 |
commit | 38ac196b2f00c1e13a0928cd892384bc296c8a98 (patch) | |
tree | 473663650b5b4fd898bd9bab4c666dca6b900dd4 /web/client.js | |
parent | 7d434464a843a8ca8acc903367492186a0ae13e8 (diff) | |
download | aconf-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.js | 793 |
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) { |