531a379c2b7a88ca32460c082e1bf6019ce4c222
[Packages/TYPO3.CMS.git] / typo3 / js / livesearch.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2009-2010 Michael Klapper <michael.klapper@aoemedia.de>
5 * (c) 2010-2011 Jeff Segars <jeff@webempoweredchurch.org>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 Ext.namespace('TYPO3');
29
30 TYPO3.BackendLiveSearch = Ext.extend(Ext.form.ComboBox, {
31 autoSelect: false,
32 ctCls: 'live-search-results',
33 dataProvider: null,
34 searchResultsPid : 0,
35 displayField: 'title',
36 emptyText: null,
37 enableKeyEvents: true,
38 helpTitle: null,
39 hideTrigger: true,
40 itemSelector: 'div.search-item-title',
41 listAlign : 'tr-br',
42 listClass: 'live-search-list',
43 listEmptyText: null,
44 listWidth: 315,
45 listHovered: false,
46 loadingText: null,
47 minChars: 1,
48 resizable: false,
49 title: null,
50 width: 205,
51 triggerClass : 'x-form-clear-trigger',
52 triggerConfig: '<span tag="a" class="t3-icon t3-icon-actions t3-icon-actions-input t3-icon-input-clear t3-tceforms-input-clearer">&nbsp;</span>',
53 onTriggerClick: function() {
54 // Empty the form field, give it focus, and collapse the results
55 this.reset(this);
56 this.focus();
57 this.collapse();
58 },
59 tpl: new Ext.XTemplate(
60 '<table border="0" cellspacing="0">',
61 '<tpl for=".">',
62 '<tr class="search-item">',
63 '<td class="search-item-type" width="105" align="right">{recordTitle}</td>',
64 '<td class="search-item-content" width="195">',
65 '<div class="search-item-title">{iconHTML} {title}</div>',
66 '</td>',
67 '</tr>',
68 '</tpl>',
69 '</table>'
70 ),
71
72 dataReader : new Ext.data.JsonReader({
73 idProperty : 'type',
74 root : 'searchItems',
75 fields : [
76 {name: 'recordTitle'},
77 {name: 'id'},
78 {name: 'iconHTML'},
79 {name: 'title'},
80 {name: 'editLink'}
81 ]
82 }),
83 listeners: {
84 select : {
85 scope: this,
86 fn: function (combo, record, index) {
87 jump(record.data.editLink, 'web_list', 'web');
88 }
89 },
90 focus : {
91 fn: function() {
92 if (this.getValue() == this.emptyText) {
93 this.reset(this);
94 }
95 }
96 },
97 specialkey : function (field, e) {
98 if (e.getKey() == e.RETURN || e.getKey() == e.ENTER) {
99 if (this.dataReader.jsonData.pageJump != '') {
100 jump(this.dataReader.jsonData.pageJump, 'web_list', 'web');
101 } else {
102 TYPO3.ModuleMenu.App.showModule('web_list', this.getSearchResultsUrl(this.getValue()));
103 }
104 }
105 },
106 keyup : function() {
107 if ((this.getValue() == this.emptyText) || (this.getValue() == '')) {
108 this.setHideTrigger(true);
109 } else {
110 this.setHideTrigger(false);
111 }
112 }
113 },
114
115 /**
116 * Initializes the component.
117 */
118 initComponent: function() {
119 this.store = new Ext.data.DirectStore({
120 directFn: this.dataProvider.find,
121 reader: this.dataReader
122 });
123 TYPO3.BackendLiveSearch.superclass.initComponent.apply(this, arguments);
124 },
125
126 restrictHeight : function(){
127 this.innerList.dom.style.height = '';
128 var inner = this.innerList.dom;
129 var pad = this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight + 30; // @todo Remove hardcoded 30
130 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
131 var ha = this.getPosition()[1]-Ext.getBody().getScroll().top;
132 var hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height;
133 var space = Math.max(ha, hb, this.minHeight || 0)-pad-2;
134 /** BUG FIX **/
135 if (this.shadow === true) { space-=this.list.shadow.offset; }
136
137 h = Math.min(h, space, this.maxHeight);
138
139 /**
140 * @internal The calcated height of "h" in the line before seems not working as expected.
141 * If i define a min height, the box shold at least use this height also if only one entry is in there
142 */
143 //h = this.maxHeight;
144
145 this.innerList.setHeight(h);
146 this.list.beginUpdate();
147 this.list.setHeight(h+pad);
148 this.list.alignTo(this.el, this.listAlign);
149 this.list.endUpdate();
150 },
151
152 initList : function () {
153 TYPO3.BackendLiveSearch.superclass.initList.apply(this, arguments);
154 var cls = 'x-combo-list';
155
156 // Track whether the hovering over the results list or not, to aid in detecting iframe clicks.
157 this.mon(this.list, 'mouseover', function() {this.listHovered = true;}, this);
158 this.mon(this.list, 'mouseout', function() {this.listHovered = false; }, this);
159
160 /**
161 * Create bottom Toolbar to the result layer
162 */
163 this.footer = this.list.createChild({cls:cls+'-ft'});
164
165 this.pageTb = new Ext.Toolbar({
166 renderTo:this.footer,
167 height: 30,
168 items: [{
169 xtype: 'tbfill',
170 autoWidth : true
171 },{
172 xtype: 'button',
173 text: TYPO3.LLL.liveSearch.showAllResults,
174 arrowAlign : 'right',
175 shadow: false,
176 icon : '../typo3/sysext/t3skin/icons/module_web_list.gif',
177 listeners : {
178 scope : this,
179 click : function () {
180 // go to db_list.php and search for given search value
181 // @todo the current selected page ID from the page tree is required, also we need the
182 // values of $GLOBALS['BE_USER']->returnWebmounts() to search only during the allowed pages
183 TYPO3.ModuleMenu.App.showModule('web_list', this.getSearchResultsUrl(this.getValue()));
184 this.collapse();
185 }
186 }
187 }]
188 });
189 this.assetHeight += this.footer.getHeight();
190 },
191
192 initQuery : function(){
193 TYPO3.BackendLiveSearch.superclass.initQuery.apply(this, arguments);
194 this.removeHelp();
195 },
196 initHelp : function () {
197 if(!this.helpList){
198 var cls = 'search-list-help';
199
200 this.helpList = new Ext.Layer({
201 parentEl: this.getListParent(),
202 shadow: this.shadow,
203 cls: [cls, this.listClass].join(' '),
204 constrain:false
205 });
206
207 // Track whether the hovering over the help list or not, to aid in detecting iframe clicks.
208 this.mon(this.helpList, 'mouseover', function() {this.listHovered = true;}, this);
209 this.mon(this.helpList, 'mouseout', function() {this.listHovered = false; }, this);
210
211 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
212 this.helpList.setSize(lw);
213 this.helpList.swallowEvent('mousewheel');
214 if(this.syncFont !== false){
215 this.helpList.setStyle('font-size', this.el.getStyle('font-size'));
216 }
217
218 this.innerHelpList = this.helpList.createChild({cls:cls+'-inner'});
219 this.mon(this.innerHelpList, 'mouseover', this.onViewOver, this);
220 this.mon(this.innerHelpList, 'mousemove', this.onViewMove, this);
221 this.innerHelpList.setWidth(lw - this.helpList.getFrameWidth('lr'));
222
223 if(!this.helpTpl){
224 this.helpTpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
225 }
226
227 /**
228 * The {@link Ext.DataView DataView} used to display the ComboBox's options.
229 * @type Ext.DataView
230 */
231 this.helpView = new Ext.DataView({
232 applyTo: this.innerHelpList,
233 tpl: this.helpTpl,
234 singleSelect: true,
235 selectedClass: this.selectedClass,
236 itemSelector: this.itemSelector || '.' + cls + '-item',
237 emptyText: this.listEmptyText
238 });
239
240 this.helpList.createChild({
241 cls: cls + '-content',
242 // @todo Can we grab this content via ExtDirect?
243 html: '<strong>' + this.helpTitle + '</strong><p>' + TYPO3.LLL.liveSearch.helpDescription + '<br /> ' + TYPO3.LLL.liveSearch.helpDescriptionPages + '</p>'
244 });
245
246 this.helpList.alignTo(this.wrap, this.listAlign);
247 this.helpList.show();
248 }
249 },
250
251 removeHelp : function() {
252 if (this.helpList) {
253 this.helpList.destroy();
254 delete this.helpList;
255 }
256 },
257
258 onFocus : function() {
259 TYPO3.BackendLiveSearch.superclass.onFocus.apply(this, arguments);
260 TYPO3BackendToolbarManager.hideAll();
261
262 // If search is blank, show the help on focus. Otherwise, show last results
263 if (this.getValue() == '') {
264 this.initHelp();
265 } else {
266 this.expand();
267 }
268 },
269
270 /**
271 * Fired when search results are clicked. We do not want the search result
272 * appear so we always set doFocus = false
273 */
274 onViewClick : function(doFocus){
275 doFocus = false;
276 TYPO3.BackendLiveSearch.superclass.onViewClick.apply(this, arguments);
277 },
278
279 postBlur : function() {
280 TYPO3.BackendLiveSearch.superclass.postBlur.apply(this, arguments);
281 this.removeHelp();
282 },
283
284 getTriggerWidth : function() {
285 // Trigger is inset, so width used in calculations is 0
286 return 0;
287 },
288
289 reset : function() {
290 this.originalValue = this.emptyText;
291 this.setHideTrigger(true);
292 TYPO3.BackendLiveSearch.superclass.reset.apply(this, arguments);
293 },
294
295 getSearchResultsUrl : function(searchTerm) {
296 return 'id=' + this.searchResultsPid + '&search_levels=4&search_field=' + searchTerm;
297 },
298
299 handleBlur : function(e) {
300
301 if (!this.listHovered) {
302 this.hasFocus = false;
303 if (this.getValue() == '') {
304 this.reset();
305 }
306 this.postBlur();
307 }
308
309 }
310 });
311
312 var TYPO3LiveSearch;
313
314 Ext.onReady(function() {
315 TYPO3LiveSearch = new TYPO3.BackendLiveSearch({
316 dataProvider: TYPO3.LiveSearchActions.ExtDirect,
317 title: TYPO3.LLL.liveSearch.title,
318 helpTitle: TYPO3.LLL.liveSearch.helpTitle,
319 emptyText: TYPO3.LLL.liveSearch.emptyText,
320 loadingText: TYPO3.LLL.liveSearch.loadingText,
321 listEmptyText: TYPO3.LLL.liveSearch.listEmptyText,
322 searchResultsPid: TYPO3.configuration.firstWebmountPid
323 });
324
325 TYPO3LiveSearch.applyToMarkup(Ext.get('live-search-box'));
326
327 // Add a blur event listener outside the ExtJS widget to handle clicks in iframes also.
328 Ext.get('live-search-box').on('blur', TYPO3LiveSearch.handleBlur, TYPO3LiveSearch);
329 });