]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/form/GlobalSearchField.js
move chart series initialization into initComponent
[pve-manager.git] / www / manager6 / form / GlobalSearchField.js
CommitLineData
18532680
DC
1/*
2 * This is a global search field
3 * it loads the /cluster/resources on focus
4 * and displays the result in a floating grid
5 *
6 * it filters and sorts the objects by the algorithm in
7 * the customFilter function
8 *
9 * also it does accept key up/down and enter for input
10 * and it opens to ctrl+shift+f and ctrl+space
11 */
12Ext.define('PVE.form.GlobalSearchField', {
13 extend: 'Ext.form.field.Text',
14 alias: 'widget.pveGlobalSearchField',
15
16 emptyText: gettext('Search'),
17 enableKeyEvents: true,
18 selectOnFocus: true,
19 padding: '0 5 0 5',
20
21 grid: {
22 xtype: 'gridpanel',
23 focusOnToFront: false,
24 floating: true,
25 width: 600,
26 maxHeight: 400,
27 scrollable: {
28 xtype: 'scroller',
29 y: true,
30 x:false
31 },
32 store: {
33 model: 'PVEResources',
34 proxy:{
35 type: 'pve',
36 url: '/api2/extjs/cluster/resources'
37 }
38 },
39 plugins: {
40 ptype: 'bufferedrenderer',
41 trailingBufferZone: 20,
42 leadingBufferZone: 20
43 },
44
45 hideMe: function() {
46 var me = this;
47 me.hasFocus = false;
48 if (!me.textfield.hasFocus) {
49 me.hide();
50 }
51 },
52
53 setFocus: function() {
54 var me = this;
55 me.hasFocus = true;
56 },
57
58 listeners: {
59 rowclick: function(grid, record) {
60 var me = this;
61 me.textfield.selectAndHide(record.id);
62 },
63 /* because of lint */
64 focusleave: {
65 fn: 'hideMe'
66 },
67 focusenter: 'setFocus'
68 },
69
70 columns: [
71 {
72 text: gettext('Type'),
73 dataIndex: 'type',
74 width: 100,
75 renderer: PVE.Utils.render_resource_type
76 },
77 {
78 text: gettext('Description'),
79 flex: 1,
80 dataIndex: 'text'
81 },
82 {
83 text: gettext('Node'),
84 dataIndex: 'node'
85 },
86 {
87 text: gettext('Pool'),
88 dataIndex: 'pool'
89 }
90 ]
91 },
92
93 customFilter: function(item) {
94 var me = this;
95 var match = 0;
96 var fieldArr = [];
97 var i,j, fields;
98
99 // different types of objects have different fields to search
100 // for example, a node will never have a pool and vice versa
101 switch (item.data.type) {
102 case 'pool': fieldArr = ['type', 'pool', 'text']; break;
103 case 'node': fieldArr = ['type', 'node', 'text']; break;
104 case 'storage': fieldArr = ['type', 'pool', 'node', 'storage']; break;
105 default: fieldArr = ['name', 'type', 'node', 'pool', 'vmid'];
106 }
107 if (me.filterVal === '') {
108 item.data.relevance = 0;
109 return true;
110 }
111
112 // all text is case insensitive and each word is
113 // searched alone
114 // for every partial match, the row gets
115 // 1 match point, for every exact match
116 // it gets 2 points
117 //
118 // results gets sorted by points (descending)
119 fields = me.filterVal.split(/\s+/);
120 for(i = 0; i < fieldArr.length; i++) {
121 var v = item.data[fieldArr[i]];
122 if (v !== undefined) {
123 v = v.toString().toLowerCase();
124 for(j = 0; j < fields.length; j++) {
125 if (v.indexOf(fields[j]) !== -1) {
126 match++;
127 if(v === fields[j]) {
128 match++;
129 }
130 }
131 }
132 }
133 }
134 // give the row the 'relevance' value
135 item.data.relevance = match;
136 return (match > 0);
137 },
138
139 updateFilter: function(field, newValue, oldValue) {
140 var me = this;
141 // parse input and filter store,
142 // show grid
143 me.grid.store.filterVal = newValue.toLowerCase().trim();
144 me.grid.store.clearFilter(true);
145 me.grid.store.filterBy(me.customFilter);
146 me.grid.getSelectionModel().select(0);
147 },
148
149 selectAndHide: function(id) {
150 var me = this;
151 me.tree.selectById(id);
152 me.grid.hide();
153 me.setValue('');
154 me.blur();
155 },
156
157 onKey: function(field, e) {
158 var me = this;
159 var key = e.getKey();
160
161 switch(key) {
162 case Ext.event.Event.ENTER:
163 // go to first entry if there is one
164 if (me.grid.store.getCount() > 0) {
165 me.selectAndHide(me.grid.getSelection()[0].data.id);
166 }
167 break;
168 case Ext.event.Event.UP:
169 me.grid.getSelectionModel().selectPrevious();
170 break;
171 case Ext.event.Event.DOWN:
172 me.grid.getSelectionModel().selectNext();
173 break;
174 case Ext.event.Event.ESC:
175 me.grid.hide();
176 me.blur();
177 break;
178 }
179 },
180
181 loadValues: function(field) {
182 var me = this;
183 var records = [];
184
185 me.hasFocus = true;
186 me.grid.textfield = me;
187 me.grid.store.load();
188 me.grid.showBy(me, 'tl-bl');
189 },
190
191 hideGrid: function() {
192 var me = this;
193
194 me.hasFocus = false;
195 if (!me.grid.hasFocus) {
196 me.grid.hide();
197 }
198 },
199
200 listeners: {
201 change: {
202 fn: 'updateFilter',
203 buffer: 250
204 },
205 specialkey: 'onKey',
206 focusenter: 'loadValues',
207 focusleave: {
208 fn: 'hideGrid',
209 delay: 100
210 }
211 },
212
213 toggleFocus: function() {
214 var me = this;
215 if (!me.hasFocus) {
216 me.focus();
217 } else {
218 me.blur();
219 }
220 },
221
222 initComponent: function() {
223 var me = this;
224
225 if (!me.tree) {
226 throw "no tree given";
227 }
228
229 me.grid = Ext.create(me.grid);
230
231 me.callParent();
232
233 /*jslint confusion: true*/
234 /*because shift is also a function*/
235 // bind ctrl+shift+f and ctrl+space
236 // to open/close the search
237 me.keymap = new Ext.KeyMap({
238 target: Ext.get(document),
239 binding: [{
240 key:'F',
241 ctrl: true,
242 shift: true,
243 fn: me.toggleFocus,
244 scope: me
245 },{
246 key:' ',
247 ctrl: true,
248 fn: me.toggleFocus,
249 scope: me
250 }]
251 });
252
253 // always select first item and
254 // sort by relevance after load
255 me.mon(me.grid.store, 'load', function() {
256 me.grid.getSelectionModel().select(0);
257 me.grid.store.sort({
258 property: 'relevance',
259 direction: 'DESC'
260 });
261 });
262 }
263
264});