0e0370e253979544d9e939194faa67b9f506e94a
[Packages/TYPO3.CMS.git] / typo3 / tree.js
1 /***************************************************************
2 *
3 * javascript functions regarding the page & folder tree
4 * relies on the javascript library "prototype"
5 *
6 *
7 * Copyright notice
8 *
9 * (c) 2006 Benjamin Mack <www.xnos.org>
10 * All rights reserved
11 *
12 * This script is part of the TYPO3 t3lib/ library provided by
13 * Kasper Skaarhoj <kasper@typo3.com> together with TYPO3
14 *
15 * Released under GNU/GPL (see license file in tslib/)
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 * This copyright notice MUST APPEAR in all copies of this script
22 *
23 * TYPO3 SVN ID: $Id$
24 *
25 ***************************************************************/
26
27 // Call this function, refresh_nav(), from another script in the backend if you want
28 // to refresh the navigation frame (eg. after having changed a page title or moved pages etc.)
29 // See t3lib_BEfunc::getSetUpdateSignal()
30 // please use the function in the "Tree" object for future implementations
31 function refresh_nav() { window.setTimeout('Tree.refresh();',0); }
32
33 // Deprecated since 4.1.
34 // Another JS function, for highlighting rows in the page tree, kept alive for backwards
35 // compatibility. Please use the function in the "Tree" object for future implementations.
36 function hilight_row(frameSetModule, highLightID) { Tree.highlightActiveItem(frameSetModule, highlightID); }
37
38 // Filters the tree by setting a class on items not matching search input string
39 function filter(strToDim) {
40 filterTraverse($("bodyTag").getElementsByClassName("tree")[0],strToDim);
41 }
42
43
44 // returns the inner content of an item, this is need because gecko does not know the innerText property
45 function getInnerText(el) {
46 if (el.innerText) {
47 return el.innerText;
48 } else {
49 return el.textContent;
50 }
51 }
52
53 function filterTraverse (eUL,strToDim) {
54 var searchRegex = new RegExp(strToDim, "i");
55 eUL.immediateDescendants().each(function(item) {
56 item.immediateDescendants().each(function(eLI) {
57 if (eLI.nodeName=="UL") {
58 filterTraverse(eLI,strToDim);
59 };
60 if (eLI.nodeName=="SPAN") {
61 if (strToDim) {
62 if (getInnerText(eLI).search(searchRegex) != -1) {
63 eLI.removeClassName("not-found");
64 } else {
65 eLI.addClassName("not-found");
66 }
67 } else {
68 eLI.removeClassName("not-found");
69 }
70 }
71 });
72 });
73 }
74
75
76 var Tree = {
77 thisScript: 'ajax.php',
78 ajaxID: 'pagetree_ExpandCollapse', // has to be either "pagetree_ExpandCollapse" or "foldertree_ExpandCollapse"
79 frameSetModule: null,
80 activateDragDrop: true,
81 highlightClass: 'active',
82
83 // reloads a part of the page tree (useful when "expand" / "collapse")
84 load: function(params, isExpand, obj) {
85 // fallback if AJAX is not possible (e.g. IE < 6)
86 if (typeof Ajax.getTransport() != 'object') {
87 window.location.href = this.thisScript + '?ajaxID=' + this.ajaxID + '&PM=' + params;
88 return;
89 }
90
91 // immediately collapse the subtree and change the plus to a minus when collapsing
92 // without waiting for the response
93 if (!isExpand) {
94 var ul = obj.parentNode.getElementsByTagName('ul')[0];
95 obj.parentNode.removeChild(ul); // no remove() directly because of IE 5.5
96 var pm = Selector.findChildElements(obj.parentNode, ['.pm'])[0]; // Getting pm object by CSS selector (because document.getElementsByClassName() doesn't seem to work on Konqueror)
97 if (pm) {
98 pm.onclick = null;
99 Element.cleanWhitespace(pm);
100 pm.firstChild.src = pm.firstChild.src.replace('minus', 'plus');
101 }
102 } else {
103 obj.style.cursor = 'wait';
104 }
105
106 new Ajax.Request(this.thisScript, {
107 method: 'get',
108 parameters: 'ajaxID=' + this.ajaxID + '&PM=' + params,
109 onComplete: function(xhr) {
110 // the parent node needs to be overwritten, not the object
111 $(obj.parentNode).replace(xhr.responseText);
112 this.registerDragDropHandlers();
113 this.reSelectActiveItem();
114 filter($('_livesearch').value);
115 }.bind(this),
116 onT3Error: function(xhr) {
117 // if this is not a valid ajax response, the whole page gets refreshed
118 this.refresh();
119 }.bind(this)
120 });
121 },
122
123 // does the complete page refresh (previously known as "_refresh_nav()")
124 refresh: function() {
125 var r = new Date();
126 // randNum is useful so pagetree does not get cached in browser cache when refreshing
127 window.location.href = '?randNum=' + r.getTime();
128 },
129
130 // attaches the events to the elements needed for the drag and drop (for the titles and the icons)
131 registerDragDropHandlers: function() {
132 if (!this.activateDragDrop) return;
133 this._registerDragDropHandlers('dragTitle');
134 this._registerDragDropHandlers('dragIcon');
135 },
136
137 _registerDragDropHandlers: function(className) {
138 var elements = Selector.findChildElements($('tree'), ['.'+className]); // using Selector because document.getElementsByClassName() doesn't seem to work on Konqueror
139 for (var i = 0; i < elements.length; i++) {
140 Event.observe(elements[i], 'mousedown', function(event) { DragDrop.dragElement(event); }, true);
141 Event.observe(elements[i], 'dragstart', function(event) { DragDrop.dragElement(event); }, false);
142 Event.observe(elements[i], 'mouseup', function(event) { DragDrop.dropElement(event); }, false);
143 }
144 },
145
146 // selects the activated item again, in case it collapsed and got expanded again
147 reSelectActiveItem: function() {
148 obj = $(top.fsMod.navFrameHighlightedID[this.frameSetModule]);
149 if (obj) Element.addClassName(obj, this.highlightClass);
150 },
151
152 // highlights an active list item in the page tree and registers it to the top-frame
153 // used when loading the page for the first time
154 highlightActiveItem: function(frameSetModule, highlightID) {
155 this.frameSetModule = frameSetModule;
156
157 // Remove all items that are already highlighted
158 obj = $(top.fsMod.navFrameHighlightedID[frameSetModule]);
159 if (obj) {
160 var classes = $w(this.highlightClass);
161 for (var i = 0; i < classes.length; i++)
162 Element.removeClassName(obj, classes[i]);
163 }
164
165 // Set the new item
166 top.fsMod.navFrameHighlightedID[frameSetModule] = highlightID;
167 if ($(highlightID)) Element.addClassName(highlightID, this.highlightClass);
168 }
169 }
170
171
172
173 // new object-oriented drag and drop - code,
174 // tested in IE 6, Firefox 2, Opera 9
175 var DragDrop = {
176 dragID: null,
177
178 // options needed for doing the changes when dropping
179 table: null, // can be "pages" or "folders"
180 changeURL: null,
181 backPath: null,
182
183
184 dragElement: function(event, elementID) {
185 Event.stop(event); // stop bubbling
186 this.dragID = this.getIdFromEvent(event);
187 if (this.dragID == 0) return false;
188
189 if (!elementID) elementID = this.dragID;
190 if (!$('dragIcon')) this.addDragIcon();
191
192 $('dragIcon').innerHTML = $('dragIconID_'+elementID).innerHTML
193 + $('dragTitleID_'+elementID).firstChild.innerHTML;
194
195 document.onmouseup = function(event) { DragDrop.cancelDragEvent(event); };
196 document.onmousemove = function(event) { DragDrop.mouseMoveEvent(event); };
197 return false;
198 },
199
200 dropElement: function(event) {
201 var dropID = this.getIdFromEvent(event);
202 if ((this.dragID) && (this.dragID != dropID)) {
203 var url = this.changeURL
204 + '?dragDrop=' + this.table
205 + '&srcId=' + this.dragID
206 + '&dstId=' + dropID;
207 + '&backPath=' + this.backPath;
208 showClickmenu_raw(url);
209 }
210 this.cancelDragEvent();
211 return false;
212 },
213
214
215 cancelDragEvent: function(event) {
216 this.dragID = null;
217 if ($('dragIcon') && $('dragIcon').style.visibility == 'visible') {
218 $('dragIcon').style.visibility = 'hidden';
219 }
220
221 document.onmouseup = null;
222 document.onmousemove = null;
223 },
224
225 mouseMoveEvent: function(event) {
226 if (!event) {
227 event = window.event;
228 }
229 $('dragIcon').style.left = (Event.pointerX(event) + 5) + 'px';
230 $('dragIcon').style.top = (Event.pointerY(event) - 5) + 'px';
231 $('dragIcon').style.visibility = 'visible';
232 return false;
233 },
234
235
236 // -- helper functions --
237 getIdFromEvent: function(event) {
238 var obj = Event.element(event);
239 while (obj.id == false && obj.parentNode) { obj = obj.parentNode; }
240 return obj.id.substring(obj.id.indexOf('_')+1);
241 },
242
243 // dynamically manipulates the DOM to add the div needed for drag&drop at the bottom of the <body>-tag
244 addDragIcon: function() {
245 var code = '<div id="dragIcon" style="visibility: hidden;">&nbsp;</div>';
246 new Insertion.Bottom(document.getElementsByTagName('body')[0], code);
247 }
248 }