2a6f892690463a1391e8c83a1c92f86edc0db9fe
[Packages/TYPO3.CMS.git] / typo3 / js / extjs / debugPanel.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2010 Stefan Galinski <stefan.galinski@gmail.com>
5 * All rights reserved
6 *
7 * This script is part of the TYPO3 project. The TYPO3 project is
8 * free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * The GNU General Public License can be found at
14 * http://www.gnu.org/copyleft/gpl.html.
15 * A copy is found in the textfile GPL.txt and important notices to the license
16 * from the author is found in LICENSE.txt distributed with these scripts.
17 *
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 Ext.ns('TYPO3');
28
29 /**
30 * Debug panel based upon the widget tab panel
31 *
32 * If you want to add a new tab, you can use the addTab or addTabWidget methods. The first one
33 * creates the widget itself. If you need the latter one, you must create the widget yourself.
34 *
35 * The drag&drop functionality introduced a new attribute for the widget that should be added
36 * as a tab. It's called "draggableTab" and needs to be set to true, if you want activated
37 * drag&drop for the new tab.
38 *
39 * Additional Features:
40 * - Drag&Drop
41 * - Close tabs with a simple wheel/middle click
42 * - utilization of the tabCloseMenu context menu (several closing options)
43 * - Grouping of tabs (Only one nested level allowed!)
44 *
45 * @author Stefan Galinski <stefan.galinski@gmail.com>
46 */
47 TYPO3.DebugPanel = Ext.extend(Ext.TabPanel, {
48 /**
49 * Tab Groups
50 *
51 * @var Ext.util.MixedCollection
52 */
53 tabGroups: new Ext.util.MixedCollection(),
54
55 /**
56 * Initializes the widget and merges our defaults with the user-defined ones. The
57 * user-defined settings are preferred.
58 *
59 * @return void
60 */
61 initComponent: function(config) {
62 config = config || {};
63 Ext.apply(this, config, {
64 // activate general tab navigation with mouse wheel support
65 enableTabScroll: true,
66 defaults: {
67 autoScroll: true
68 },
69
70 // add the context menu actions
71 plugins: new Ext.ux.TabCloseMenu({
72 closeTabText: TYPO3.LLL.core.tabs_close,
73 closeOtherTabsText: TYPO3.LLL.core.tabs_closeOther,
74 closeAllTabsText: TYPO3.LLL.core.tabs_closeAll
75 })
76 });
77
78 // create a drop arrow indicator
79 this.on('render', function() {
80 this.arrow = Ext.DomHelper.append(
81 Ext.getBody(),
82 '<div class="typo3-debugPanel-dragDropArrowDown">&nbsp;</div>',
83 true
84 );
85 this.arrow.hide();
86 }, this);
87
88 TYPO3.DebugPanel.superclass.initComponent.call(this);
89 },
90
91 /**
92 * Cleanup
93 *
94 * @return void
95 */
96 onDestroy: function() {
97 Ext.destroy(this.arrow);
98 TYPO3.DebugPanel.superclass.onDestroy.call(this);
99 },
100
101 /**
102 * Adds a new tab
103 *
104 * If you need more possibilites, you should use the addTabWidget method.
105 *
106 * @see addTabWidget()
107 * @param tabContent String content of the new tab
108 * @param header String tab header
109 * @param group String tab group
110 * @param position Integer position of the new tab
111 * @return void
112 */
113 addTab: function(tabContent, header, group, position) {
114 var tabWidget = new Ext.Panel({
115 title: header,
116 html: tabContent,
117 border: false,
118 autoScroll: true,
119 closable: true,
120 draggableTab: true
121 });
122
123 this.addTabWidget(tabWidget, group, position);
124 },
125
126 /**
127 * Adds a new tab to the widget
128 *
129 * You can inject any Ext component, but you need to create it yourself. If you just
130 * want to add some text into a new tab, you should use the addTab function.
131 *
132 * @see addTab()
133 * @param tabWidget Component the component that should be added as a new tab
134 * @param group String tab group
135 * @param position Integer position of the new tab
136 * @return void
137 */
138 addTabWidget: function(tabWidget, group, position) {
139 if (this.hidden) {
140 this.show();
141 } else if (this.collapsed) {
142 this.expand();
143 }
144
145 // Move the widget into a tab group?
146 var tabGroup = this;
147 if (typeof group !== 'undefined' && group != '' && !this.isTabChildren) {
148 if (this.tabGroups.indexOfKey(group) === -1) {
149 tabGroup = new TYPO3.DebugPanel({
150 border: false,
151 title: group,
152 autoScroll: true,
153 closable: true,
154 isTabChildren: true,
155 tabParent: this,
156 draggableTab: true
157 });
158 this.addTabWidget(tabGroup);
159
160 this.tabGroups.add(group, tabGroup);
161 } else {
162 tabGroup = this.tabGroups.key(group);
163 }
164 }
165
166 // recalculate position if necessary
167 if (typeof position === 'undefined') {
168 position = tabGroup.items.getCount();
169 }
170
171 // hide the debug panel if the last element is closed
172 tabWidget.on('destroy', function(element) {
173 if (this.isTabChildren) {
174 if (!this.items.getCount()) {
175 this.tabParent.remove(this.tabParent.tabGroups.key(this.title));
176 }
177 } else {
178 if (!this.items.getCount()) {
179 this.hide();
180 this.fireEvent('resize');
181 }
182 this.tabGroups.removeKey(element.title);
183 }
184 }, tabGroup);
185
186 // add drag&drop and the wheel click functionality
187 tabWidget.on('afterlayout', function(element) {
188 Ext.get(this.id + '__' + element.id).on('mousedown', function(event) {
189 if (!Ext.isIE6 && !Ext.isIE7) {
190 if ((Ext.isIE && event.button === 1) ||
191 (!Ext.isIE && event.browserEvent.button === 1)
192 ) {
193 event.stopEvent();
194 this.remove(tabWidget);
195 return false;
196 }
197 }
198 return true;
199 }, this);
200
201 if (tabWidget.draggableTab) {
202 this.initDragAndDropForTab(tabWidget);
203 }
204 }, tabGroup);
205
206 // add the widget as a new tab
207 tabGroup.insert(position, tabWidget).show();
208 tabGroup.ownerCt.doLayout();
209 },
210
211 /**
212 * Extends the tab item with drag&drop functionality.
213 *
214 * @param item Component the tab widget
215 * @return void
216 */
217 initDragAndDropForTab: function(item) {
218 item.tabDragZone = new Ext.dd.DragZone(this.id + '__' + item.id, {
219 ddGroup: this.id,
220
221 /**
222 * Reintroduces the simple click event on a tab element.
223 *
224 * @return void
225 */
226 b4MouseDown : function() {
227 item.show();
228 Ext.dd.DragZone.superclass.b4MouseDown.apply(this, arguments);
229 },
230
231 /**
232 * On receipt of a mousedown event, see if it is within a draggable element.
233 * Return a drag data object if so. The data object can contain arbitrary application
234 * data, but it should also contain a DOM element in the ddel property to provide
235 * a proxy to drag.
236 *
237 * @param event Ext.EventObject
238 * @return drag data
239 */
240 getDragData: function(event) {
241 var sourceElement = event.getTarget(item.itemSelector, 10);
242 if (sourceElement) {
243 var dragComponent = sourceElement.cloneNode(true);
244 dragComponent.id = Ext.id();
245 item.dragData = {
246 ddel: dragComponent,
247 sourceEl: sourceElement,
248 repairXY: Ext.fly(sourceElement).getXY()
249 };
250 return item.dragData;
251 }
252
253 return false;
254 },
255
256 /**
257 * Provide coordinates for the proxy to slide back to on failed drag.
258 * This is the original XY coordinates of the draggable element.
259 *
260 * @return x,y coordinations of the original component position
261 */
262 getRepairXY: function() {
263 return this.dragData.repairXY;
264 }
265 });
266
267 item.tabDropZone = new Ext.dd.DropZone(this.id + '__' + item.id, {
268 debugPanel: this,
269 ddGroup: this.id,
270
271 /**
272 * If the mouse is over a tab element, return that node. This is
273 * provided as the "target" parameter in all "onNodeXXXX" node event
274 * handling functions
275 *
276 * @param event Ext.EventObject
277 * @return the tab element or boolean false
278 */
279 getTargetFromEvent: function(event) {
280 var tabElement = Ext.get(event.getTarget()).findParentNode('li');
281 if (tabElement !== null) {
282 return tabElement;
283 }
284
285 return false;
286 },
287
288 /**
289 * On entry into a target node, highlight that node.
290 *
291 * @param target string id of the target element
292 * @return void
293 */
294 onNodeEnter : function(target) {
295 Ext.get(target).addClass('typo3-debugPanel-dragDropOver');
296 },
297
298 /**
299 * On exit from a target node, unhighlight that node.
300 *
301 * @param target string id of the target element
302 * @return void
303 */
304 onNodeOut : function(target) {
305 Ext.get(target).removeClass('typo3-debugPanel-dragDropOver');
306 this.debugPanel.arrow.hide();
307 },
308
309 /**
310 * While over a target node, return the default drop allowed class which
311 * places a "tick" icon into the drag proxy. Also the arrow position is
312 * recalculated.
313 *
314 * @param target string id of the target element
315 * @param proxy Ext.dd.DDProxy proxy element
316 * @param event Ext.EventObject
317 * @return default dropAllowed class or a boolean false
318 */
319 onNodeOver : function(target, proxy, event) {
320 // set arrow position
321 var element = Ext.get(target);
322 var left = 0;
323 var tabLeft = element.getX();
324 var tabMiddle = tabLeft + element.dom.clientWidth / 2;
325 var tabRight = tabLeft + element.dom.clientWidth;
326 if (event.getPageX() <= tabMiddle) {
327 left = tabLeft;
328 } else {
329 left = tabRight;
330 }
331 this.debugPanel.arrow.setTop(this.el.getY() - 8).setLeft(left - 9).show();
332
333 // drop allowed?
334 if (proxy.handleElId !== target.id) {
335 return Ext.dd.DropZone.prototype.dropAllowed;
336 }
337
338 return false;
339 },
340
341 /**
342 * On node drop we move the dragged tab element at the position of
343 * the dropped element.
344 *
345 * @param target string id of the target element
346 * @param proxy Ext.dd.DDProxy proxy element
347 * @param event Ext.EventObject
348 * @return true or false
349 */
350 onNodeDrop : function(target, proxy, event) {
351 if (proxy.handleElId === target.id) {
352 return false;
353 }
354
355 var dropPanelId = target.id.substring(this.debugPanel.id.length + 2);
356 var dragPanelId = proxy.handleElId.substring(this.debugPanel.id.length + 2);
357
358 var dropPanelPosition = this.debugPanel.items.indexOfKey(dropPanelId);
359 var dragPanelPosition = this.debugPanel.items.indexOfKey(dragPanelId);
360
361 if (dropPanelPosition !== undefined &&
362 dropPanelPosition !== -1 &&
363 dropPanelPosition <= this.debugPanel.items.getCount()
364 ) {
365 // calculate arrow position to decide if the elements needs
366 // to be inserted on the right or left
367 var element = Ext.get(target);
368 var tabMiddle = element.getX() + element.dom.clientWidth / 2;
369 if (dragPanelPosition > dropPanelPosition) {
370 if (event.getPageX() > tabMiddle) {
371 dropPanelPosition += 1;
372 }
373 } else {
374 if (event.getPageX() <= tabMiddle) {
375 dropPanelPosition -= 1;
376 }
377 }
378
379 var dropEl = this.debugPanel.remove(dragPanelId, false);
380 this.debugPanel.addTabWidget(dropEl, '', dropPanelPosition);
381 }
382
383 this.debugPanel.arrow.hide();
384 return true;
385 }
386 });
387 }
388 });
389
390 Ext.reg('typo3DebugPanel', TYPO3.DebugPanel);