Rough work but sort of capturing the structure of the vmsp tree editor
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Fri, 8 Apr 2011 21:48:57 +0000 (17:48 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Fri, 8 Apr 2011 21:48:57 +0000 (17:48 -0400)
Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/vandelay/treetest.js [new file with mode: 0644]
Open-ILS/web/templates/default/vandelay/treetest.tt2 [new file with mode: 0644]

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
new file mode 100644 (file)
index 0000000..9043884
--- /dev/null
@@ -0,0 +1,16 @@
+dojo.provide("openils.vandelay.TreeDndSource");
+dojo.require("dijit._tree.dndSource");
+
+dojo.declare(
+    "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
+        "checkItemAcceptance": function(target, source, position) {
+            /* Here we'll return false if source has no fm object in the right place */
+            console.log(
+                "target: " + dijit.getEnclosingWidget(target).item.name +
+                " source: " + source +
+                " position: " + position
+            );
+            return true;
+        }
+    }
+);
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
new file mode 100644 (file)
index 0000000..72261eb
--- /dev/null
@@ -0,0 +1,37 @@
+dojo.provide("openils.vandelay.TreeStoreModel");
+dojo.require("dijit.tree.TreeStoreModel");
+
+function _simple_item(model, item) {
+    /* Instead of model.getLabel(), could do
+     * model.store.getValue(item, "blah") or something like that ... */
+    return {
+        "label": model.getLabel(item),
+        "children": {}
+    };
+}
+
+dojo.declare(
+    "openils.vandelay.TreeStoreModel", dijit.tree.TreeStoreModel, {
+        "getSimpleTree": function(item, oncomplete, result) {
+            var self = this;
+            if (!result) result = {};
+
+            var mykey = this.getIdentity(item);
+            result[mykey] = _simple_item(this, item);
+            var child_collector = result[mykey].children;
+
+            if (this.mayHaveChildren(item)) {
+                this.getChildren(
+                    item, function(children) {
+                        for (var i = 0; i < children.length; i++) {
+                            self.getSimpleTree(
+                                children[i], null, child_collector
+                            );
+                        }
+                        if (oncomplete) oncomplete(result);
+                    }
+                );
+            }
+        }
+    }
+);
diff --git a/Open-ILS/web/js/ui/default/vandelay/treetest.js b/Open-ILS/web/js/ui/default/vandelay/treetest.js
new file mode 100644 (file)
index 0000000..4d4b7b2
--- /dev/null
@@ -0,0 +1,281 @@
+dojo.require("dojo.data.ItemFileWriteStore");
+dojo.require("dijit.Tree");
+dojo.require("dijit.form.Button");
+dojo.require("dojo.dnd.Source");
+dojo.require("dijit._tree.dndSource");
+//dojo.require("openils.vandelay.TreeDndSource");
+dojo.require("openils.vandelay.TreeStoreModel");
+dojo.require("openils.User");
+dojo.require("openils.Util");
+dojo.require("openils.PermaCrud");
+dojo.require("openils.widget.ProgressDialog");
+
+var node_editor;
+var _crads;
+
+function _find_crad_by_name(name) {
+    for (var i = 0; i < _crads.length; i++) {
+        if (_crads[i].name() == name)
+            return _crads[i];
+    }
+    return null;
+}
+
+function NodeEditor() {
+    var self = this;
+
+    var _svf_select_template = null;
+    var _factories_by_type = {
+        "svf": function() {
+            if (!_svf_select_template) {
+                _svf_select_template = dojo.create(
+                    "select", {"fmfield": "svf"}
+                );
+                for (var i=0; i<_crads.length; i++) {
+                    dojo.create(
+                        "option", {
+                            "value": _crads[i].name(),
+                            "innerHTML": _crads[i].label()
+                        }, _svf_select_template
+                    );
+                }
+            }
+
+            var select = dojo.clone(_svf_select_template);
+            dojo.attr(select, "id", "svf-select");
+            var label = dojo.create(
+                "label", {
+                    "for": "svf-select", "innerHTML": "Single-Value-Field:"
+                }
+            );
+
+            var tr = dojo.create("tr");
+            dojo.place(label, dojo.create("td", null, tr));
+            dojo.place(select, dojo.create("td", null, tr));
+
+            return [tr];
+        },
+        "tag": function() {
+            var rows = [dojo.create("tr"), dojo.create("tr")];
+            dojo.create(
+                "label", {
+                    "for": "tag-input", "innerHTML": "Tag:"
+                }, dojo.create("td", null, rows[0])
+            );
+            dojo.create(
+                "input", {
+                    "id": "tag-input",
+                    "type": "text",
+                    "size": 4,
+                    "maxlength": 3,
+                    "fmfield": "tag"
+                }, dojo.create("td", null, rows[0])
+            );
+            dojo.create(
+                "label", {
+                    "for": "subfield-input", "innerHTML": "Subfield: \u2021"
+                }, dojo.create("td", null, rows[1])
+            );
+            dojo.create(
+                "input", {
+                    "id": "subfield-input",
+                    "type": "text",
+                    "size": 2,
+                    "maxlength": 1,
+                    "fmfield": "subfield"
+                }, dojo.create("td", null, rows[1])
+            );
+            return rows;
+        },
+        "bool_op": function() {
+            var tr = dojo.create("tr");
+            dojo.create(
+                "label",
+                {"for": "operator-select", "innerHTML": "Operator:"},
+                dojo.create("td", null, tr)
+            );
+            var select = dojo.create(
+                "select", {"fmfield": "bool_op", "id": "operator-select"},
+                dojo.create("td", null, tr)
+            );
+            dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
+            dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
+
+            return [tr];
+        }
+    };
+
+    function _simple_value_getter(control) {
+        if (typeof control.selectedIndex != "undefined")
+            return control.options[control.selectedIndex].value;
+        else if (dojo.attr(control, "type") == "checkbox")
+            return control.checked;
+        else
+            return control.value;
+    };
+
+    this._init = function(dnd_source, node_editor_container) {
+        this.dnd_source = dnd_source;
+        this.node_editor_container = dojo.byId(node_editor_container);
+    };
+
+    this.clear = function() {
+        this.dnd_source.selectAll().deleteSelectedNodes();
+        dojo.empty(this.node_editor_container);
+    };
+
+    this.update_draggable = function(draggable) {
+        var s = "";
+//        draggable.data = {"match_point": new vmsp()};
+        var had_op = false;
+        dojo.query("[fmfield]", this.node_editor_container).forEach(
+            function(control) {
+                var used_svf = null;
+                var field = dojo.attr(control, "fmfield");
+                var value = _simple_value_getter(control);
+//                draggable.data.match_point[field](value);
+
+                if (field == "subfield")
+                    s += " \u2021";
+                if (field == "svf")
+                    used_svf = value;
+                if (field == "quality")
+                    return;
+                if (field == "bool_op")
+                    had_op = true;
+                if (field == "negate") {
+                    if (value) {
+                        if (had_op)
+                            s = "<strong>N</strong>" + s;
+                        else
+                            s = "<strong>NOT</strong> " + s;
+                    }
+                } else {
+                    s += value;
+                }
+
+                if (used_svf !== null) {
+                    var our_crad = _find_crad_by_name(used_svf);
+                    /* XXX i18n, use fmtted strings */
+                    s += " / " + our_crad.label() + "<br /><em>" +
+                        (our_crad.description() || "") + "</em><br />";
+                }
+            }
+        );
+        dojo.attr(draggable, "innerHTML", s);
+    };
+
+    this._add_consistent_controls = function(tgt) {
+        if (!this._consistent_controls) {
+            var trs = dojo.query("[consistent-controls]");
+            this._consistent_controls = [];
+            for (var i = 0; i < trs.length; i++)
+                this._consistent_controls[i] = dojo.clone(trs[i]);
+            dojo.empty(trs[0].parentNode);
+        }
+
+        this._consistent_controls.forEach(
+            function(node) { dojo.place(dojo.clone(node), tgt); }
+        );
+    };
+
+    this.add = function(type) {
+        this.clear();
+
+        /* a representation, not the editing widgets, but will also carry
+         * the fieldmapper object when dragged to the tree */
+        var draggable = dojo.create(
+            "li", {
+                "innerHTML": "Define your match point and drag me<br/>" +
+                    "to the tree on the right"
+            }
+        );
+
+        /* these are the editing widgets */
+        var table = dojo.create("table", {"className": "node-editor"});
+
+        var nodes = _factories_by_type[type]();
+        for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
+
+        this._add_consistent_controls(table);
+
+        dojo.create(
+            "input", {
+                "type": "submit", "value": "Ok",
+                "onclick": function() { self.update_draggable(draggable); }
+            }, dojo.create(
+                "td", {"colspan": 2, "align": "center"},
+                dojo.create("tr", null, table)
+            )
+        );
+
+        dojo.place(table, this.node_editor_container, "only");
+        /* XXX around here attach other data structures to the node */
+        this.dnd_source.insertNodes(false, [draggable]);
+    };
+
+    this._init.apply(this, arguments);
+}
+
+function init_test() {
+    progress_dialog.show(true);
+
+    /* XXX No-one should have hundreds of these or anything, but theoretically
+     * this could be problematic with a big enough list of crad objects. */
+    _crads = new openils.PermaCrud().retrieveAll(
+        "crad", {"order_by": {"crad": "label"}}
+    );
+
+    var store = new dojo.data.ItemFileWriteStore({
+        "data": {
+            "identifier": "id", "label": "name", "items": [
+                {
+                    "id": "root", "name": "AND",
+                    "children": [
+                        {"_reference": "leaf0"}, {"_reference": "leaf1"}
+                    ]
+                },
+                {"id": "leaf0", "name": "nonsense test"},
+                {"id": "leaf1", "name": "more nonsense"}
+            ]
+        }
+    });
+
+    var treeModel = new openils.vandelay.TreeStoreModel({
+        store: store, "query": {"id": "root"}
+    });
+
+    var tree = new dijit.Tree(
+        {
+            "model": treeModel,
+            "dndController": dijit._tree.dndSource,
+            "dragThreshold": 8,
+            "betweenThreshold": 5,
+            "persist": false
+        }, "treeOne"
+    );
+
+    node_editor = new NodeEditor(mysrc, "node-editor-container");
+
+    dojo.connect(
+        mysrc, "onDndDrop", null,
+        function(source, nodes, copy, target) {
+            if (source == this) {
+                var model = target.tree.model;
+                model.getRoot(
+                    function(root) {
+                        model.getSimpleTree(
+                            root, function(results) { alert(js2JSON(results)); }
+                        );
+                    }
+                );
+                node_editor.clear();  /* because otherwise this acts like a copy! */
+            } else {
+                alert("XXX [mysrc] nodes length is " + nodes.length); /* XXX DEBUG */
+            }
+        }
+    );
+    progress_dialog.hide();
+}
+
+openils.Util.addOnLoad(init_test);
diff --git a/Open-ILS/web/templates/default/vandelay/treetest.tt2 b/Open-ILS/web/templates/default/vandelay/treetest.tt2
new file mode 100644 (file)
index 0000000..32e72c9
--- /dev/null
@@ -0,0 +1,59 @@
+[% WRAPPER 'default/base.tt2' %]
+[% ctx.page_title = 'Vandelay Match Set Points' %]
+<style type="text/css">
+    .outer { clear: both; }
+    #vmsp-buttons button { padding: 0 1.5em; }
+    .node-editor { margin-bottom: 2em; }
+    .node-editor td { padding: 0.5ex; }
+    label[title] { border-bottom: 1px dashed #666; }
+    li { background-color: #ddd; }
+</style>
+<h1>[% ctx.page_title %]</h1>
+<table class="hidden">
+    <tr consistent-controls="1">
+        <td>
+            <label for="quality-input"
+                title="A relative number representing the impact of this expression on the quality of the overall record match">
+                Quality:
+            </label>
+        </td>
+        <td>
+            <input id="quality-input" type="text" value="1"
+                size="4" maxlength="3" fmfield="quality" />
+        </td>
+    </tr>
+    <tr consistent-controls="1">
+        <td>
+            <label for="negate-input">Negate?</label>
+        </td>
+        <td>
+            <input id="negate-input" type="checkbox" fmfield="negate" />
+        </td>
+    </tr>
+</table>
+<div class="outer">
+    <div><!-- XXX TODO: consider a read-only display here of the query as built
+        so far from the treet --></div>
+    <div id="vmsp-buttons">
+        <button onclick="node_editor.add('svf');">New Single-Value-Field</button>
+        <button onclick="node_editor.add('tag');">New MARC Tag and Subfield</button>
+        <button onclick="node_editor.add('bool_op');">New Boolean Operator</button>
+    </div>
+</div>
+<div class="outer" style="margin-top: 2ex;">
+    <div style="float: left; width: 49%">
+        <div>
+            <form id="node-editor-container" onsubmit="return false;"></form>
+        </div>
+        <ul dojoType="dojo.dnd.Source" jsId="mysrc"></ul>
+    </div>
+
+    <div style="float: right; width: 50%">
+        <div><big>Your Expression</big></div>
+        <div id="treeOne"></div>
+    </div>
+</div>
+<div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>
+<script type="text/javascript"
+    src="[% ctx.media_prefix %]/js/ui/default/vandelay/treetest.js"></script>
+[% END %]