1 /* ---------------------------------------------------------------------------
2 * Copyright (C) 2008 Georgia Public Library Service
3 * Bill Erickson <erickson@esilibrary.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * ---------------------------------------------------------------------------
18 if(!dojo._hasResource["openils.widget.GridColumnPicker"]) {
19 dojo.provide('openils.widget.GridColumnPicker');
21 dojo.require('dijit.Dialog');
22 dojo.require('dijit.form.Button');
23 dojo.require('dijit.form.NumberSpinner');
24 dojo.require('openils.User');
25 dojo.require('openils.Event');
26 dojo.require('openils.Util');
27 dojo.require('fieldmapper.Fieldmapper');
28 dojo.requireLocalization('openils.widget', 'AutoFieldWidget');
30 dojo.declare('openils.widget.GridColumnPicker', null, {
32 USER_PERSIST_SETTING : 'ui.grid_columns',
34 constructor : function (authtoken, persistKey, grid, structure) {
36 this.nls = dojo.i18n.getLocalization('openils.widget', 'AutoFieldWidget');
38 this.persistKey = this.USER_PERSIST_SETTING+'.'+persistKey;
39 this.authtoken = authtoken || openils.User.authtoken;
40 this.structure = structure || this.grid.structure;
41 this.cells = this.structure[0].cells[0].slice();
43 this.dialog = this.buildDialog();
44 this.dialogTable = this.dialog.containerNode.getElementsByTagName('tbody')[0];
47 // replace: called after any sort changes
48 this.onSortChange = function(list) {console.log('onSortChange()')}
49 // replace: called after user settings are first retrieved
50 this.onLoad = function(opts) {console.log('onLoad()')};
52 // internal onload handler
54 this._onLoad = function(opts) {_this.loaded = true; _this.onLoad(opts)};
56 this.grid.onHeaderContextMenu = function(e) {
63 /** Loads any grid column label changes, clears any
64 * non-visible fields from the structure, and passes
65 * the structure back to the grid to force a UI refresh.
67 reloadStructure : function() {
69 // update our copy of the column labels
72 this.grid.structure[0].cells[0],
74 var cell = _this.cells.filter(
75 function(c) { return c.field == gcell.field }
77 cell.name = gcell.name;
81 this.pruneInvisibleFields();
82 this.grid.setStructure(this.structure);
86 // determine the visible sorting from the
87 // view and update our list of cells to match
88 refreshCells : function() {
89 var cells = this.cells;
94 _this.grid.views.views[0].structure.cells[0],
96 for (var i = 0; i < cells.length; i++) {
97 if (cells[i].field == vCell.field) {
98 cells[i]._visible = true;
99 _this.cells.push(cells[i]);
106 // Depending on how the grid structure is built, there may be
107 // cells in the structure that are not yet in the view. Push
108 // any remaining cells onto the end.
112 existing = _this.cells.filter(function(s){return s.field == cell.field})[0]
114 cell._visible = false;
115 _this.cells.push(cell);
121 buildDialog : function() {
124 var dialog = new dijit.Dialog({title : this.nls.COLUMN_PICKER});
125 var table = dojo.create('table', {'class':'oils-generic-table', innerHTML :
126 "<table><thead><tr><th width='30%'>" + this.nls.COLUMN + "</th>" +
127 "<th width='23%'>" + this.nls.DISPLAY + "</th>" +
128 "<th width='23%'>" + this.nls.AUTO_WIDTH + "</th>" +
129 "<th width='23%'>" + this.nls.SORT_PRIORITY + "</th></tr></thead>" +
132 var tDiv = dojo.create('div');
133 tDiv.appendChild(table);
135 var bDiv = dojo.create('div', {style : 'text-align:right; width:100%;',
136 innerHTML : "<span name='cancel_button'></span><span name='save_button'></span>"});
138 var textDiv = dojo.create('div', {style : 'padding:5px; margin-top:5px; border-top:1px solid #333',
140 "<i>" + this.nls.SORT_PRIORITY_ZERO + "<br/>" +
141 "<i>" + this.nls.SORT_PRIORITY_MINUS});
143 var wrapper = dojo.create('div');
144 wrapper.appendChild(tDiv);
145 wrapper.appendChild(textDiv);
146 wrapper.appendChild(bDiv);
147 dialog.containerNode.appendChild(wrapper);
149 var button = new dijit.form.Button({label: this.nls.SAVE },
150 dojo.query('[name=save_button]', bDiv)[0]);
151 button.onClick = function() { dialog.hide(); self.update(true); };
153 button = new dijit.form.Button({label: this.nls.CANCEL },
154 dojo.query('[name=cancel_button]', bDiv)[0]);
155 button.onClick = function() { dialog.hide(); };
160 // builds the column-picker dialog table
163 var rows = dojo.query('tr', this.dialogTable);
165 for(var i = 0; i < rows.length; i++) {
166 if(rows[i].getAttribute('picker'))
167 this.dialogTable.removeChild(rows[i]);
170 rows = dojo.query('tr', this.dialogTable);
171 var lastChild = null;
173 lastChild = rows[rows.length-1];
175 for(var i = 0; i < this.cells.length; i++) {
176 // setting table.innerHTML breaks stuff, so do it the hard way
177 var cell = this.cells[i];
178 tr = document.createElement('tr');
179 tr.setAttribute('picker', 'picker');
180 td0 = document.createElement('td');
181 td1 = document.createElement('td');
182 td2 = document.createElement('td');
183 td3 = document.createElement('td');
185 ipt = document.createElement('input');
186 ipt.setAttribute('type', 'checkbox');
187 ipt.setAttribute('name', 'selector');
189 ipt2 = document.createElement('input');
190 ipt2.setAttribute('type', 'checkbox');
191 ipt2.setAttribute('name', 'width');
193 ipt3 = document.createElement('div');
195 if (cell.nonSelectable) {
196 ipt.setAttribute('checked', 'checked');
197 ipt.setAttribute('disabled', true);
198 ipt2.setAttribute('disabled', true);
202 ipt.setAttribute('checked', 'checked');
203 if (cell.width == 'auto')
204 ipt2.setAttribute('checked', 'checked');
206 ipt.removeAttribute('checked');
210 if (cell.field == '+selector') {
211 // pick up the unescaped unicode checkmark char
212 td0.innerHTML = cell.name;
214 td0.appendChild(document.createTextNode(cell.name));
216 td1.appendChild(ipt);
217 td2.appendChild(ipt2);
218 td3.appendChild(ipt3);
225 this.dialogTable.insertBefore(tr, lastChild);
227 this.dialogTable.appendChild(tr);
229 if (this.grid.canSort(
230 i + 1, /* column index is 1-based */
231 true /* skip structure test (API abuse) */
234 /* Ugly kludge. When using with FlattenerGrid the
235 * conditional is needed. Shouldn't hurt usage with
237 if (typeof cell.fsort == "undefined" || cell.fsort) {
238 // must be added after its parent node is inserted into the DOM.
239 var ns = new dijit.form.NumberSpinner(
240 { constraints : {places : 0},
241 value : cell._sort || 0,
251 // update the grid based on the items selected in the picker dialog
252 update : function(persist) {
253 var rows = dojo.query('[picker=picker]', this.dialogTable);
255 var displayCells = [];
256 var sortUpdated = false;
258 for (var i = 0; i < rows.length; i++) {
260 var selector = dojo.query('[name=selector]', row)[0];
261 var width = dojo.query('[name=width]', row)[0];
262 var sort = dojo.query('[name=sort]', row)[0];
263 var cell = this.cells[i]; // index should match dialog
265 if (sort && cell._sort != sort.value) {
267 cell._sort = sort.value;
270 if (selector.checked) {
271 cell._visible = true;
274 } else if(cell.width == 'auto') {
277 displayCells.push(cell);
280 cell._visible = false;
285 if (sortUpdated && this.onSortChange)
286 this.onSortChange(this.buildSortList());
288 this.structure[0].cells[0] = displayCells;
289 this.grid.setStructure(this.structure);
292 if (persist) this.persist(true);
295 // extract cells that have sorting applied, order lowest to highest
296 buildSortList : function() {
297 var sortList = this.cells.filter(
298 function(cella) { return Number(cella._sort) }
301 if (Math.abs(a._sort) < Math.abs(b._sort)) return -1;
306 return sortList.map(function(f){
307 var dir = f._sort < 0 ? 'desc' : 'asc';
308 return {field : f.field, direction : dir};
312 // save me as a user setting
313 persist : function(noRefresh) {
316 if (!noRefresh) this.refreshCells();
318 for(var i = 0; i < this.cells.length; i++) {
319 var cell = this.cells[i];
321 list.push(cell.field);
322 if(cell.width == 'auto')
323 autos.push(cell.field);
328 setting[this.persistKey] = {
331 'sort' : this.buildSortList().map(function(f){return f.field})
335 fieldmapper.standardRequest(
336 ['open-ils.actor', 'open-ils.actor.patron.settings.update'],
338 params: [this.authtoken, null, setting],
339 oncomplete: function(r) {
340 var stat = openils.Util.readResponse(r);
342 onmethoderror : function() {},
343 onerror : function() {
344 console.log("No user setting '" + _this.persistKey + "' configured. Cannot persist")
350 loadColsFromSetting : function(setting) {
352 this.setting = setting;
353 var displayCells = [];
355 // new component, existing settings may not have this
356 if (!setting.sort) setting.sort = [];
358 dojo.forEach(setting.columns,
360 var cell = _this.cells.filter(function(c){return c.field == col})[0];
362 cell._visible = true;
363 displayCells.push(cell);
365 if(setting.auto.indexOf(cell.field) > -1) {
368 if(cell.width == 'auto')
371 cell._sort = setting.sort.indexOf(cell.field) + 1;
374 console.log('Unknown setting column '+col+'. Ignoring...');
379 // any cells not in the setting must be marked as non-visible
380 dojo.forEach(this.cells, function(cell) {
381 if (setting.columns.indexOf(cell.field) == -1) {
382 cell._visible = false;
387 this.structure[0].cells[0] = displayCells;
388 this.grid.setStructure(this.structure);
392 // *only* call this when no usr setting tells us what columns
393 // are visible or not.
394 pruneInvisibleFields : function() {
395 this.structure[0].cells[0] = dojo.filter(
396 this.structure[0].cells[0],
397 dojo.hitch(this, function(c) {
398 // keep true or undef, lose false
399 return typeof c._visible == "undefined" || c._visible;
407 // if load is called before the user has logged in,
408 // queue the loading up for after authentication.
409 if (!this.authtoken) {
411 openils.Util.addOnLoad(function() {
412 _this.authtoken = openils.User.authtoken;
419 this.loadColsFromSetting(this.setting);
420 this._onLoad({sortFields : this.buildSortList()});
424 fieldmapper.standardRequest(
425 ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve'],
427 params: [this.authtoken, null, this.persistKey],
428 oncomplete: function(r) {
429 var set = openils.Util.readResponse(r);
431 _this.loadColsFromSetting(set);
433 _this.grid.setStructure(_this.structure);
436 _this._onLoad({sortFields : _this.buildSortList()});