Fix a bug in magic scroll; start hooking up Edit{Pane,Dialog}
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 27 Mar 2012 18:25:33 +0000 (14:25 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 27 Mar 2012 18:25:33 +0000 (14:25 -0400)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/templates/conify/flattener_test.tt2
Open-ILS/web/js/dojo/openils/FlattenerStore.js
Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js

index 8a36ec2..0bcad40 100644 (file)
@@ -3,36 +3,43 @@
 <script type="text/javascript">
     dojo.require("dijit.form.Button");
     dojo.require("openils.widget.FlattenerGrid");
-    //dojo.require("dojo.data.ItemFileReadStore");
-    //dojo.require("openils.DebuggingIFRS");
-
-    /*
-       var gridData = <esc>:r ~/grid-data
-   var gridStore = new openils.DebuggingIFRS(gridData);
-   console.log("gridStore built");
-   */
-
-    /* mapClause="{ 'barcode': 'barcode', 'circ_lib_name': 'circ_lib.name', 'circ_lib': 'circ_lib.shortname', 'call_number': { 'path': 'call_number.label', 'sort': true, 'display': true }, 'id': 'id', 'tcn': { 'path': 'call_number.record.tcn_value', 'filter': true, 'sort': true } }" */
-
-    /* query="{tcn: {'>': 100, circ_lib: 'BR1'}}" */
 </script>
-<table
-    id="gridNode"
-    jsid="grid"
-    dojoType="openils.widget.FlattenerGrid"
-    columnPickerPrefix='"conify.flattener_test"'
-    fmClass="'acp'"
-    autoHeight="10"
-    mapExtras="{copy_status: {path: 'status.name', filter: true}}"
-    query="{'copy_status': ['Available','Reshelving','In process'],'circ_lib': 'BR1'}">
-    <thead>
-        <tr>
-            <th field="barcode" fpath="barcode">Barcode</th>
-            <th field="circ_lib_name" fpath="circ_lib.name">Circulation Library Name</th>
-            <th field="circ_lib" fpath="circ_lib.shortname" ffilter="true">Circulation Library</th>
-            <th field="call_number" fpath="call_number.label">Call Number</th>
-            <th field="shelving_loc" fpath="location.name" ffilter="true">Shelving Location</th>
-        </tr>
-    </thead>
-</table>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <div dojoType="dijit.layout.ContentPane"
+         layoutAlign="top" class="oils-header-panel">
+        <div>Flattener Test</div>
+        <div>
+            <button dojoType="dijit.form.Button"
+                onClick="grid.showCreateDialog()">New Thing</button>
+            <button dojoType="dijit.form.Button"
+                onClick="grid.deleteSelected()">Delete Selected Thing</button>
+        </div>
+    </div>
+    <!-- <div class="oils-acq-basic-roomy">
+        blah, a dropdown or something here (optional; typical interfaces might
+        have a filtering org select here.
+    </div> -->
+    <table
+        id="gridNode"
+        jsid="grid"
+        dojoType="openils.widget.FlattenerGrid"
+        columnPickerPrefix='"conify.flattener_test"'
+        fmClass="'acp'"
+        autoHeight="10"
+        editOnEnter="true"
+        editStyle="pane"
+        defaultSort="['call_number']"
+        mapExtras="{copy_status: {path: 'status.name', filter: true}}"
+        query="{'copy_status': ['Available','Reshelving','In process'],'circ_lib': 'BR1'}">
+        <thead>
+            <tr>
+                <th field="barcode" fpath="barcode">Barcode</th>
+                <th field="circ_lib_name" fpath="circ_lib.name">Circulation Library Name</th>
+                <th field="circ_lib" fpath="circ_lib.shortname" ffilter="true">Circulation Library</th>
+                <th field="call_number" fpath="call_number.label">Call Number</th>
+                <th field="shelving_loc" fpath="location.name" ffilter="true">Shelving Location</th>
+            </tr>
+        </thead>
+    </table>
+</div>
 [% END %]
index f1eaa2f..c9ad647 100644 (file)
@@ -23,7 +23,6 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
         }
     }
 
-
     dojo.declare(
         "openils.FlattenerStore", null, {
 
@@ -37,23 +36,27 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
         "sloClause": null,
         "limit": 25,
         "offset": 0,
-
+        "baseSort": null,
+        "defaultSort": null,
 
         "constructor": function(/* object */ args) {
-            dojo.mixin(this, args); /* XXX do we actually need this? */
+            dojo.mixin(this, args);
             this._current_items = {};
         },
 
         /* turn dojo-style sort into flattener-style sort */
         "_prepare_sort": function(dsort) {
-            if (!dsort)
-                return [];
-            return dsort.map(
-                function(d) {
-                    var o = {};
-                    o[d.attribute] = d.descending ? "desc" : "asc";
-                    return o;
-                }
+            if (!dsort || !dsort.length)
+                return this.baseSort || this.defaultSort || [];
+
+            return (this.baseSort || []).concat(
+                dsort.map(
+                    function(d) {
+                        var o = {};
+                        o[d.attribute] = d.descending ? "desc" : "asc";
+                        return o;
+                    }
+                )
             );
         },
 
@@ -163,7 +166,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
         },
 
         "isItemLoaded": function(/* anything */ something) {
-            console.warning("[unfinished] isItemLoaded(" + something + ")");
+            console.warn("[unfinished] isItemLoaded(" + something + ")");
 
             /* This is assuming items are always loaded, which is probably not right for this particular store.*/
             return this.isItem(something);
@@ -231,16 +234,23 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 if (when < self._last_fetch) /* Stale response. Discard. */
                     return;
 
+                /* The following is apparently the "right" way to call onBegin,
+                 * and is very necessary (at least in Dojo 1.3.3) to get
+                 * the Grid's fetch-more-when-I-need-it logic to work
+                 * correctly. *grumble* crummy documentation *snarl!*
+                 */
                 if (typeof req.onBegin == "function") {
                     /* We lie to onBegin like this because we don't know how
                      * many more rows we might be able to fetch if the
                      * user keeps scrolling.  Once we get a number of
                      * results that is less than the limit we asked for,
-                     * the grid is smart enough to know we're at the end and
-                     * it does the right thing. */
-                    var might_be_a_lie = obj.length;
+                     * we stop exaggerating, and the grid is smart enough to
+                     * know we're at the end and it does the right thing. */
+                    var might_be_a_lie = req.start;
                     if (obj.length >= req.count)
-                        might_be_a_lie += req.count;
+                        might_be_a_lie += obj.length + req.count;
+                    else
+                        might_be_a_lie += obj.length;
 
                     req.onBegin.call(callback_scope, might_be_a_lie, req);
                 }
@@ -302,7 +312,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
              * doesn't use it (or loadItem)?), and needs to be able to call
              * fetch() or to talk to the web service directly. */
 
-            console.warning(
+            console.warn(
                 "[unfinished] fetchItemByIdentity(" +
                 dojo.toJson(keywordArgs) + ")"
             );
index 4fd1b9e..74e76e3 100644 (file)
@@ -4,16 +4,33 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
     dojo.require("DojoSRF");
     dojo.require("dojox.grid.DataGrid");
     dojo.require("openils.FlattenerStore");
+    dojo.require("openils.PermaCrud");
     dojo.require("openils.widget.GridColumnPicker");
+    dojo.require("openils.widget.EditDialog");  /* includes EditPane */
 
     dojo.declare(
         "openils.widget.FlattenerGrid",
         [dojox.grid.DataGrid], {
+            /* These potential constructor arguments are useful to 
+             * lattenerGrid in their own right */
             "columnReordering": true,
+            "columnPickerPrefix": null,
+
+            /* These potential constructor arguments maybe useful to
+             * FlattenerGrid in their own right, and are passed to
+             * FlattenerStore. */
             "fmClass": null,
             "fmIdentifier": null,
             "mapExtras": null,
-            "columnPickerPrefix": null,
+            "defaultSort": null,  /* whatever the UI says /replaces/ this */
+            "baseSort": null,     /* whatever the UI says /follows/ this */
+
+            /* These potential constructor arguments are for functionality
+             * copied from AutoGrid */
+            "editOnEnter": false,       /* also implies edit-on-dblclick */
+            "editStyle": "dialog",      /* "dialog" or "pane" */
+            "requiredFields": null,     /* affects create/edit dialogs */
+            "suppressEditFields": null, /* affects create/edit dialogs */
 
             /* _generate_map() lives to interpret the attributes of the
              * FlattenerGrid dijit itself plus those definined in
@@ -85,6 +102,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
 
             "constructor": function(args) {
                 dojo.mixin(this, args);
+
                 this.fmIdentifier = this.fmIdentifier ||
                     fieldmapper.IDL.fmclasses[this.fmClass].pkey;
             },
@@ -94,7 +112,9 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
                     this.store = new openils.FlattenerStore({
                         "fmClass": this.fmClass,
                         "fmIdentifier": this.fmIdentifier,
-                        "mapClause": (this.mapClause || this._generate_map())
+                        "mapClause": (this.mapClause || this._generate_map()),
+                        "baseSort": this.baseSort,
+                        "defaultSort": this.defaultSort
                     });
                 }
 
@@ -106,6 +126,309 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
                 }
 
                 this.inherited(arguments);
+
+                this._showing_create_pane = false;
+
+                this.overrideEditWidgets = {};
+                this.overrideEditWidgetClass = {};
+                this.overrideWidgetArgs = {};
+
+                if (this.editOnEnter)
+                    this._applyEditOnEnter();
+                else if (this.singleEditStyle)
+                    this._applySingleEditStyle();
+            },
+
+            /* ******** below are methods mostly copied but
+             * slightly changed from AutoGrid ******** */
+
+            "_applySingleEditStyle": function() {
+                this.onMouseOverRow = function(e) {};
+                this.onMouseOutRow = function(e) {};
+                this.onCellFocus = function(cell, rowIndex) {
+                    this.selection.deselectAll();
+                    this.selection.select(this.focus.rowIndex);
+                };
+            },
+
+            /* capture keydown and launch edit dialog on enter */
+            "_applyEditOnEnter": function() {
+                this._applySingleEditStyle();
+
+                dojo.connect(
+                    this, "onRowDblClick", function(e) {
+                        if (this.editStyle == "pane")
+                            this._drawEditPane(
+                                this.selection.getFirstSelected(),
+                                this.focus.rowIndex
+                            );
+                        else
+                            this._drawEditDialog(
+                                this.selection.getFirstSelected(),
+                                this.focus.rowIndex
+                            );
+                    }
+                );
+
+                dojo.connect(
+                    this, "onKeyDown", function(e) {
+                        if (e.keyCode == dojo.keys.ENTER) {
+                            this.selection.deselectAll();
+                            this.selection.select(this.focus.rowIndex);
+                            if (this.editStyle == "pane")
+                                this._drawEditPane(
+                                    this.selection.getFirstSelected(),
+                                    this.focus.rowIndex
+                                );
+                            else
+                                this._drawEditDialog(
+                                    this.selection.getFirstSelected(),
+                                    this.focus.rowIndex
+                                );
+                        }
+                    }
+                );
+            },
+
+            "_makeEditPane": function(storeItem, rowIndex, onPostSubmit, onCancel) {
+                var grid = this;
+                var fmObject = (new openils.PermaCrud()).retrieve(
+                    this.fmClass, 
+                    this.store.getIdentity(storeItem)
+                );
+
+                var pane = new openils.widget.EditPane({
+                    "fmObject": fmObject,
+                    "hideSaveButton": this.editReadOnly,
+                    "readOnly": this.editReadOnly,
+                    "overrideWidgets": this.overrideEditWidgets,
+                    "overrideWidgetClass": this.overrideEditWidgetClass,
+                    "overrideWidgetArgs": this.overrideWidgetArgs,
+                    "disableWidgetTest": this.disableWidgetTest,
+                    "requiredFields": this.requiredFields,
+                    "suppressFields": this.suppressEditFields,
+                    "onPostSubmit": function() {
+                        console.info("onPostSubmit");
+                        console.warn("[unfinished] would update store with fmObject values here");
+    //                    for(var i in fmObject._fields) {
+    //                        var field = fmObject._fields[i];
+    //                        if(idents.filter(function(j){return (j == field)})[0])
+    //                            continue; // don't try to edit an identifier field
+    //                        grid.store.setValue(storeItem, field, fmObject[field]());
+    //                    }
+                        if (grid.onPostUpdate)
+                            grid.onPostUpdate(storeItem, rowIndex);
+                        setTimeout(
+                            function() {
+                                try {
+                                    grid.views.views[0].getCellNode(
+                                        rowIndex, 0
+                                    ).focus();
+                                } catch (E) { }
+                            }, 200
+                        );
+                        if (onPostSubmit)
+                            onPostSubmit();
+                    },
+                    "onCancel": function() {
+                        console.info("onCancel");
+                        setTimeout(
+                            function() {
+                                grid.views.views[0].getCellNode(
+                                    rowIndex, 0
+                                ).focus();
+                            }, 200
+                        );
+                        if (onCancel)
+                            onCancel();
+                    }
+                });
+
+                if (typeof this.editPaneOnSubmit == "function")
+                    pane.onSubmit = this.editPaneOnSubmit;
+
+                pane.fieldOrder = this.fieldOrder;
+                pane.mode = "update";
+                return pane;
+            },
+
+            "_makeCreatePane": function(onPostSubmit, onCancel) {
+                var grid = this;
+                var pane = new openils.widget.EditPane({
+                    "fmClass": this.fmClass,
+                    "overrideWidgets": this.overrideEditWidgets,
+                    "overrideWidgetClass": this.overrideEditWidgetClass,
+                    "overrideWidgetArgs": this.overrideWidgetArgs,
+                    "disableWidgetTest": this.disableWidgetTest,
+                    "requiredFields": this.requiredFields,
+                    "suppressFields": this.suppressEditFields,
+                    "onPostSubmit": function(req, cudResults) {
+                        var fmObject = cudResults[0];
+                        if (grid.onPostCreate)
+                            grid.onPostCreate(fmObject);
+                        if (fmObject) {
+                            //grid.store.newItem(fmObject.toStoreItem());
+                            console.warn("[unfinished] here we have fmObject and must update the grid somehow");
+                        }
+
+                        setTimeout(
+                            function() {
+                                try {
+                                    grid.selection.select(grid.rowCount - 1);
+                                    grid.views.views[0].getCellNode(
+                                        grid.rowCount - 1, 1
+                                    ).focus();
+                                } catch (E) { }
+                            }, 200
+                        );
+
+                        if (onPostSubmit)
+                            onPostSubmit(fmObject);
+                    },
+                    "onCancel": function() { if (onCancel) onCancel(); }
+                });
+
+                if (typeof this.createPaneOnSubmit == "function")
+                    pane.onSubmit = this.createPaneOnSubmit;
+                pane.fieldOrder = this.fieldOrder;
+                pane.mode = "create";
+                return pane;
+            },
+
+            /**
+             * Creates an EditPane with a copy of the data from the provided store
+             * item for cloning said item
+             * @param {Object} storeItem Dojo data item
+             * @param {Number} rowIndex The Grid row index of the item to be cloned
+             * @param {Function} onPostSubmit Optional callback for post-submit behavior
+             * @param {Function} onCancel Optional callback for clone cancelation
+             * @return {Object} The clone EditPane
+             */
+            "_makeClonePane": function(storeItem,rowIndex,onPostSubmit,onCancel) {
+                var clonePane = this._makeCreatePane(onPostSubmit, onCancel);
+                var origPane = this._makeEditPane(storeItem, rowIndex);
+                clonePane.startup();
+                origPane.startup();
+                dojo.forEach(
+                    origPane.fieldList, function(field) {
+                        if (field.widget.widget.attr('disabled'))
+                            return;
+
+                        var w = clonePane.fieldList.filter(
+                            function(i) { return (i.name == field.name) }
+                        )[0];
+
+                        // sync widgets
+                        w.widget.baseWidgetValue(field.widget.widget.attr('value'));
+
+                        // async widgets
+                        w.widget.onload = function() {
+                            w.widget.baseWidgetValue(
+                                field.widget.widget.attr('value')
+                            )
+                        };
+                    }
+                );
+                origPane.destroy();
+                return clonePane;
+            },
+
+
+            "_drawEditDialog": function(storeItem, rowIndex) {
+                var done = dojo.hitch(this, function() { this.hideDialog(); });
+                var pane = this._makeEditPane(storeItem, rowIndex, done, done);
+                this.editDialog = new openils.widget.EditDialog({editPane:pane});
+                this.editDialog.startup();
+                this.editDialog.show();
+            },
+
+            /**
+             * Generates an EditDialog for object creation and displays it to the user
+             */
+            "showCreateDialog": function() {
+                var done = dojo.hitch(this, function() { this.hideDialog(); });
+                var pane = this._makeCreatePane(done, done);
+                this.editDialog = new openils.widget.EditDialog({editPane:pane});
+                this.editDialog.startup();
+                this.editDialog.show();
+            },
+
+            "_drawEditPane": function(storeItem, rowIndex) {
+                var done = dojo.hitch(this, function() { this.hidePane(); });
+
+                dojo.style(this.domNode, "display", "none");
+
+                this.editPane = this._makeEditPane(storeItem, rowIndex, done, done);
+                this.editPane.startup();
+                dojo.place(this.editPane.domNode, this.domNode, "before");
+
+                if (this.onEditPane)
+                    this.onEditPane(this.editPane);
+            },
+
+            "showClonePane": function(onPostSubmit) {
+                var done = dojo.hitch(this, function() { this.hidePane(); });
+                var row = this.getFirstSelectedRow();
+
+                if (!row)
+                    return;
+
+                if (onPostSubmit) {
+                    postSubmit = dojo.hitch(
+                        this, function(result) {
+                            onPostSubmit(this.getItem(row), result);
+                            this.hidePane();
+                        }
+                    );
+                } else {
+                    postSubmit = done;
+                }
+
+                dojo.style(this.domNode, "display", "none");
+                this.editPane = this._makeClonePane(
+                    this.getItem(row), row, postSubmit, done
+                );
+                dojo.place(this.editPane.domNode, this.domNode, "before");
+                if (this.onEditPane)
+                    this.onEditPane(this.editPane);
+            },
+
+            "showCreatePane": function() {
+                if (this._showing_create_pane)
+                    return;
+                this._showing_create_pane = true;
+
+                var done = dojo.hitch(
+                    this, function() {
+                        this._showing_create_pane = false;
+                        this.hidePane();
+                    }
+                );
+
+                dojo.style(this.domNode, "display", "none");
+
+                this.editPane = this._makeCreatePane(done, done);
+                this.editPane.startup();
+
+                dojo.place(this.editPane.domNode, this.domNode, "before");
+
+                if (this.onEditPane)
+                    this.onEditPane(this.editPane);
+            },
+
+            "hideDialog": function() {
+                this.editDialog.hide();
+                this.editDialog.destroy();
+                delete this.editDialog;
+                this.update();
+            },
+
+            "hidePane": function() {
+                this.domNode.parentNode.removeChild(this.editPane.domNode);
+                this.editPane.destroy();
+                delete this.editPane;
+                dojo.style(this.domNode, "display", "block");
+                this.update();
             }
         }
     );