989c49715c56401450d62510c35b59ac4656a132
[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 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 dbListUrl : 'id=0&search_levels=4&search_field=',
35 displayField: 'title',
36 emptyText: null,
37 enableKeyEvents: true,
38 helpTitle: null,
39 itemSelector: 'div.search-item-title',
40 listAlign : 'tr-br',
41 listClass: 'live-search-list',
42 listEmptyText: null,
43 listWidth: 315,
44 loadingText: null,
45 minChars: 2,
46 resizable: false,
47 title: null,
48 width: 205,
49
50 triggerClass : 'x-form-clear-trigger',
51 triggerConfig: '<span tag="a" class="t3-icon t3-icon-actions t3-icon-actions-input t3-icon-input-clear t3-tceforms-input-clearer">&nbsp;</span>',
52 onTriggerClick: function() {
53 // Empty the form field, give it focus, and collapse the results
54 this.reset(this);
55 this.focus();
56 this.collapse();
57 },
58 tpl: new Ext.XTemplate(
59 '<table border="0" cellspacing="0">',
60 '<tpl for=".">',
61 '<tr class="search-item">',
62 '<td class="search-item-type" width="105" align="right">{recordTitle}</td>',
63 '<td class="search-item-content" width="195">',
64 '<div class="search-item-title">{iconHTML} {title}</div>',
65 '</td>',
66 '</tr>',
67 '</tpl>',
68 '</table>'
69 ),
70
71 dataReader : new Ext.data.JsonReader({
72 idProperty : 'type',
73 root : 'searchItems',
74 fields : [
75 {name: 'recordTitle'},
76 {name: 'id'},
77 {name: 'iconHTML'},
78 {name: 'title'},
79 {name: 'editLink'}
80 ]
81 }),
82 listeners: {
83 select : {
84 scope: this,
85 fn: function (combo, record, index) {
86 jump(record.data.editLink, 'web_list', 'web');
87 }
88 },
89 focus : {
90 fn: function() {
91 if (this.getValue() == this.emptyText) {
92 this.reset(this);
93 }
94 }
95 },
96 specialkey : function (field, e) {
97 if (e.getKey() == e.RETURN || e.getKey() == e.ENTER) {
98 if (this.dataReader.jsonData.pageJump != '') {
99 jump(this.dataReader.jsonData.pageJump, 'web_list', 'web');
100 } else {
101 TYPO3.ModuleMenu.App.showModule('web_list', this.dbListUrl + this.getValue());
102 }
103 }
104 }
105 },
106
107 /**
108 * Initializes the component.
109 */
110 initComponent: function() {
111 this.store = new Ext.data.DirectStore({
112 directFn: this.dataProvider.find,
113 reader: this.dataReader
114 });
115 TYPO3.BackendLiveSearch.superclass.initComponent.apply(this, arguments);
116 },
117
118 restrictHeight : function(){
119 this.innerList.dom.style.height = '';
120 var inner = this.innerList.dom;
121 var pad = this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight + 30; // @todo Remove hardcoded 30
122 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
123 var ha = this.getPosition()[1]-Ext.getBody().getScroll().top;
124 var hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height;
125 var space = Math.max(ha, hb, this.minHeight || 0)-pad-2;
126 /** BUG FIX **/
127 if (this.shadow === true) { space-=this.list.shadow.offset; }
128
129 h = Math.min(h, space, this.maxHeight);
130
131 /**
132 * @internal The calcated height of "h" in the line before seems not working as expected.
133 * If i define a min height, the box shold at least use this height also if only one entry is in there
134 */
135 //h = this.maxHeight;
136
137 this.innerList.setHeight(h);
138 this.list.beginUpdate();
139 this.list.setHeight(h+pad);
140 this.list.alignTo(this.el, this.listAlign);
141 this.list.endUpdate();
142 },
143
144 initList : function () {
145 TYPO3.BackendLiveSearch.superclass.initList.apply(this, arguments);
146
147 var cls = 'x-combo-list';
148
149 /**
150 * Create bottom Toolbar to the result layer
151 */
152 this.footer = this.list.createChild({cls:cls+'-ft'});
153
154 this.pageTb = new Ext.Toolbar({
155 renderTo:this.footer,
156 height: 30,
157 items: [{
158 xtype: 'tbfill',
159 autoWidth : true
160 },{
161 xtype: 'button',
162 text: TYPO3.LLL.liveSearch.showAllResults,
163 arrowAlign : 'right',
164 shadow: false,
165 icon : '../typo3/sysext/t3skin/icons/module_web_list.gif',
166 listeners : {
167 scope : this,
168 click : function () {
169 // go to db_list.php and search for given search value
170 // @todo the current selected page ID from the page tree is required, also we need the
171 // values of $BE_USER->returnWebmounts() to search only during the allowed pages
172 TYPO3.ModuleMenu.App.showModule('web_list', this.dbListUrl + this.getValue());
173 this.collapse();
174 }
175 }
176 }]
177 });
178 this.assetHeight += this.footer.getHeight();
179 },
180
181 // private
182 onLoad : function(){
183 TYPO3.BackendLiveSearch.superclass.onLoad.apply(this, arguments);
184
185 // If an pageJump request is done this will immediately load the record for editing.
186 // if (this.dataReader.jsonData.pageJump != '') {
187 // this.collapse();
188 // jump(this.dataReader.jsonData.pageJump, 'web_list', 'web');
189 // } else {
190 // Add an event handler to each iframe, closing the search window when there's a click inside the iframe
191 // @todo Is there a cleaner way to handle this?
192 var iframes = Ext.query('iframe');
193 Ext.each(iframes, function(item, index, allItems) {
194 item.contentWindow.document.body.onclick = function() {
195 if (parent.TYPO3LiveSearch && parent.TYPO3LiveSearch.isExpanded()) {
196 parent.TYPO3LiveSearch.collapse();
197 }
198 };
199 }, this);
200 //}
201 },
202
203 initQuery : function(){
204 TYPO3.BackendLiveSearch.superclass.initQuery.apply(this, arguments);
205 this.removeHelp();
206 },
207 initHelp : function () {
208 if(!this.helpList){
209 var cls = 'search-list-help';
210
211 this.helpList = new Ext.Layer({
212 parentEl: this.getListParent(),
213 shadow: this.shadow,
214 cls: [cls, this.listClass].join(' '),
215 constrain:false
216 });
217
218 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
219 this.helpList.setSize(lw);
220 this.helpList.swallowEvent('mousewheel');
221 if(this.syncFont !== false){
222 this.helpList.setStyle('font-size', this.el.getStyle('font-size'));
223 }
224
225 this.innerHelpList = this.helpList.createChild({cls:cls+'-inner'});
226 this.mon(this.innerHelpList, 'mouseover', this.onViewOver, this);
227 this.mon(this.innerHelpList, 'mousemove', this.onViewMove, this);
228 this.innerHelpList.setWidth(lw - this.helpList.getFrameWidth('lr'));
229
230 if(!this.helpTpl){
231 this.helpTpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
232 }
233
234 /**
235 * The {@link Ext.DataView DataView} used to display the ComboBox's options.
236 * @type Ext.DataView
237 */
238 this.helpView = new Ext.DataView({
239 applyTo: this.innerHelpList,
240 tpl: this.helpTpl,
241 singleSelect: true,
242 selectedClass: this.selectedClass,
243 itemSelector: this.itemSelector || '.' + cls + '-item',
244 emptyText: this.listEmptyText
245 });
246
247 this.helpList.createChild({
248 cls: cls + '-content',
249 // @todo Can we grab this content via ExtDirect?
250 html: '<strong>' + this.helpTitle + '</strong><p>' + TYPO3.LLL.liveSearch.helpDescription + '<br /> ' + TYPO3.LLL.liveSearch.helpDescriptionPages + '</p>'
251 });
252
253 this.helpList.alignTo(this.wrap, this.listAlign);
254 this.helpList.show();
255
256 var iframes = Ext.query('iframe');
257 Ext.each(iframes, function(item, index, allItems) {
258 item.contentWindow.document.body.onclick = function() {
259 if (parent.TYPO3LiveSearch && parent.TYPO3LiveSearch.helpList.isVisible()) {
260 parent.TYPO3LiveSearch.helpList.remove();
261 }
262 };
263 }, this);
264
265 }
266 },
267
268 removeHelp : function() {
269 if (this.helpList) {
270 this.helpList.destroy();
271 }
272 },
273
274 onFocus : function() {
275 TYPO3.BackendLiveSearch.superclass.onFocus.apply(this, arguments);
276
277 // If search is blank, show the help on focus. Otherwise, show last results
278 if (this.getValue() == '') {
279 this.initHelp();
280 } else {
281 this.expand();
282 }
283 },
284
285 postBlur : function() {
286 TYPO3.BackendLiveSearch.superclass.postBlur.apply(this, arguments);
287 this.removeHelp();
288 },
289
290 getTriggerWidth : function() {
291 // Trigger is inset, so width used in calculations is 0
292 return 0;
293 },
294
295 reset : function() {
296 this.originalValue = this.emptyText;
297 TYPO3.BackendLiveSearch.superclass.reset.apply(this, arguments);
298 }
299 });
300
301 var TYPO3LiveSearch;
302
303 Ext.onReady(function() {
304 TYPO3LiveSearch = new TYPO3.BackendLiveSearch({
305 dataProvider: TYPO3.LiveSearchActions.ExtDirect,
306 title: TYPO3.LLL.liveSearch.title,
307 helpTitle: TYPO3.LLL.liveSearch.helpTitle,
308 emptyText: TYPO3.LLL.liveSearch.emptyText,
309 loadingText: TYPO3.LLL.liveSearch.loadingText,
310 listEmptyText: TYPO3.LLL.liveSearch.listEmptyText
311 });
312
313 TYPO3LiveSearch.applyToMarkup(Ext.get('live-search-box'));
314 });