ccaa881241959679f7f43465e2a6fad74e407013
[Packages/TYPO3.CMS.git] / typo3 / js / modulemenu.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2010-2011 Steffen Kamper <steffen@typo3.org>
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
28 /**
29 * Class to render the module menu and handle the BE navigation
30 *
31 * @author Steffen Kamper
32 */
33
34
35 Ext.ns('TYPO3', 'ModuleMenu');
36
37 TYPO3.ModuleMenu = {};
38
39 TYPO3.ModuleMenu.Store = new Ext.data.JsonStore({
40 storeId: 'ModuleMenuStore',
41 root: 'root',
42 fields: [
43 {name: 'index', type: 'int', mapping: 'sub.index'},
44 {name: 'key', type: 'string'},
45 {name: 'label', type: 'string'},
46 {name: 'menuState', type: 'int'},
47 {name: 'subitems', type: 'int'},
48 'sub'
49 ],
50 url: 'ajax.php?ajaxID=ModuleMenu::getData',
51 baseParams: {
52 'action': 'getModules'
53 },
54 listeners: {
55 beforeload: function(store) {
56 this.loaded = false;
57 },
58 load: function(store) {
59 this.loaded = true;
60 }
61 },
62 // Custom indicator for loaded store:
63 loaded: false,
64 isLoaded: function() {
65 return this.loaded;
66 }
67
68 });
69
70 TYPO3.ModuleMenu.Template = new Ext.XTemplate(
71 '<div id="typo3-docheader">',
72 ' <div id="typo3-docheader-row1">',
73 ' <div class="buttonsleft"></div>',
74 ' <div class="buttonsright"></div>',
75 ' </div>',
76 '</div>',
77 '<ul id="typo3-menu">',
78 '<tpl for=".">',
79 ' <li class="menuSection" id="{key}">',
80 ' <div class="modgroup {[this.getStateClass(values)]}">{label}</div>',
81 ' <ul {[this.getStateStyle(values)]}>',
82 ' <tpl for="sub">',
83 ' <li id="{name}" class="submodule mod-{name}">',
84 ' <a title="{description}" href="#" class="modlink">',
85 ' <span class="submodule-icon">',
86 ' <img width="16" height="16" alt="{label}" title="{label}" src="{icon}" />',
87 ' </span>',
88 ' <span>{label}</span>',
89 ' </a>',
90 ' </li>',
91 ' </tpl>',
92 ' </ul>',
93 ' </li>',
94 '</tpl>',
95 '</ul>',
96 {
97 getStateClass: function(value) {
98 return value.menuState ? 'collapsed' : 'expanded';
99 },
100 getStateStyle: function(value) {
101 return value.menuState ? 'style="display:none"' : '';
102 }
103 }
104 );
105
106 TYPO3.ModuleMenu.App = {
107 loadedModule: null,
108 loadedNavigationComponentId: '',
109 availableNavigationComponents: {},
110
111 init: function() {
112 TYPO3.ModuleMenu.Store.load({
113 scope: this,
114 callback: function(records, options) {
115 this.renderMenu(records);
116 if (top.startInModule) {
117 this.showModule(top.startInModule[0], top.startInModule[1]);
118 } else {
119 this.loadFirstAvailableModule();
120 }
121 }
122 });
123 },
124
125 renderMenu: function(records) {
126 TYPO3.Backend.ModuleMenuContainer.removeAll();
127 TYPO3.Backend.ModuleMenuContainer.add({
128 xtype: 'dataview',
129 animCollapse: true,
130 store: TYPO3.ModuleMenu.Store,
131 tpl: TYPO3.ModuleMenu.Template,
132 singleSelect: true,
133 itemSelector: 'li.submodule',
134 overClass: 'x-view-over',
135 selectedClass: 'highlighted',
136 autoHeight: true,
137 itemId: 'modDataView',
138 tbar: [{text: 'test'}],
139 listeners: {
140 click: function(view, index, node, event) {
141 var el = Ext.fly(node);
142 if (el.hasClass('submodule')) {
143 TYPO3.ModuleMenu.App.showModule(el.getAttribute('id'));
144 }
145 },
146 containerclick: function(view, event) {
147 var item = event.getTarget('li.menuSection', view.getEl());
148 if (item) {
149 var el = Ext.fly(item);
150 var id = el.getAttribute('id');
151 var section = el.first('div'), state;
152 if (section.hasClass('expanded')) {
153 state = true;
154 section.removeClass('expanded').addClass('collapsed');
155 el.first('ul').slideOut('t', {
156 easing: 'easeOut',
157 duration: .2,
158 remove: false,
159 useDisplay: true
160 });
161
162 } else {
163 state = false;
164 section.removeClass('collapsed').addClass('expanded');
165 el.first('ul').slideIn('t', {
166 easing: 'easeIn',
167 duration: .2,
168 remove: false,
169 useDisplay: true
170 });
171 }
172 // save menu state
173 Ext.Ajax.request({
174 url: 'ajax.php?ajaxID=ModuleMenu::saveMenuState',
175 params: {
176 'menuid': 'modmenu_' + id,
177 'state': state
178 }
179 });
180 }
181 return false;
182 },
183 scope: this
184 }
185 });
186 TYPO3.Backend.ModuleMenuContainer.doLayout();
187 },
188
189 getRecordFromIndex: function(index) {
190 var i, record;
191 for (i = 0; i < TYPO3.ModuleMenu.Store.getCount(); i++) {
192 record = TYPO3.ModuleMenu.Store.getAt(i);
193 if (index < record.data.subitems) {
194 return record.data.sub[index];
195 }
196 index -= record.data.subitems;
197 }
198 },
199
200 getRecordFromName: function(name) {
201 var i, j, record;
202 for (i = 0; i < TYPO3.ModuleMenu.Store.getCount(); i++) {
203 record = TYPO3.ModuleMenu.Store.getAt(i);
204 for (j = 0; j < record.data.subitems; j++) {
205 if (record.data.sub[j].name === name) {
206 return record.data.sub[j];
207 }
208 }
209 }
210 },
211
212 showModule: function(mod, params) {
213 params = params || '';
214 this.selectedModule = mod;
215
216 params = this.includeId(mod, params);
217 var record = this.getRecordFromName(mod);
218
219 if (record) {
220 this.loadModuleComponents(record, params);
221 } else {
222 //defined startup module is not present, use the first available instead
223 this.loadFirstAvailableModule(params);
224 }
225 },
226
227 loadFirstAvailableModule: function(params) {
228 params = params || '';
229 if (TYPO3.ModuleMenu.Store.isLoaded() === false) {
230 new Ext.util.DelayedTask(
231 this.loadFirstAvailableModule,
232 this,
233 [params]
234 ).delay(250);
235 } else if (TYPO3.ModuleMenu.Store.getCount() === 0) {
236 // Store is empty, something went wrong
237 TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Module loader', 'No module found. If this is a temporary error, please reload the Backend!', 50000);
238 } else {
239 mod = TYPO3.ModuleMenu.Store.getAt(0).data.sub[0];
240 this.loadModuleComponents(mod, params);
241 }
242 },
243
244 loadModuleComponents: function(record, params) {
245 var mod = record.name;
246 if (record.navigationComponentId) {
247 this.loadNavigationComponent(record.navigationComponentId);
248 TYPO3.Backend.NavigationDummy.hide();
249 TYPO3.Backend.NavigationIframe.getEl().parent().setStyle('overflow', 'auto');
250 } else if (record.navframe || record.navigationFrameScript) {
251 TYPO3.Backend.NavigationDummy.hide();
252 TYPO3.Backend.NavigationContainer.show();
253 this.loadNavigationComponent('typo3-navigationIframe');
254 this.openInNavFrame(record.navigationFrameScript || record.navframe, record.navigationFrameScriptParam);
255 TYPO3.Backend.NavigationIframe.getEl().parent().setStyle('overflow', 'hidden');
256 } else {
257 TYPO3.Backend.NavigationContainer.hide();
258 TYPO3.Backend.NavigationDummy.show();
259 }
260 this.openInContentFrame(record.originalLink, params);
261 this.loadedModule = mod;
262 this.highlightModuleMenuItem(mod);
263
264 // compatibility
265 top.currentSubScript = record.originalLink;
266 top.currentModuleLoaded = mod;
267
268 TYPO3.Backend.doLayout();
269 },
270
271 includeId: function(mod, params) {
272 //get id
273 var section = mod.split('_')[0];
274 if (top.fsMod.recentIds[section]) {
275 params = 'id=' + top.fsMod.recentIds[section] + '&' + params;
276 }
277
278 return params;
279 },
280
281 loadNavigationComponent: function(navigationComponentId) {
282 if (navigationComponentId === this.loadedNavigationComponentId) {
283 if (TYPO3.Backend.NavigationContainer.hidden) {
284 TYPO3.Backend.NavigationContainer.show();
285 }
286
287 return;
288 }
289
290 if (this.loadedNavigationComponentId !== '') {
291 Ext.getCmp(this.loadedNavigationComponentId).hide();
292 }
293
294 var component = Ext.getCmp(navigationComponentId);
295 if (typeof component !== 'object') {
296 if (typeof this.availableNavigationComponents[navigationComponentId] !== 'function') {
297 throw 'The navigation component "' + navigationComponentId + '" is not available ' +
298 'or has no valid callback function';
299 }
300
301 component = this.availableNavigationComponents[navigationComponentId]();
302 TYPO3.Backend.NavigationContainer.add(component);
303 }
304
305 component.show()
306
307 // backwards compatibility
308 top.nav = component;
309
310 TYPO3.Backend.NavigationContainer.show();
311 this.loadedNavigationComponentId = navigationComponentId;
312 },
313
314 registerNavigationComponent: function(componentId, initCallback) {
315 this.availableNavigationComponents[componentId] = initCallback;
316 },
317
318 openInNavFrame: function(url, params) {
319 var navUrl = url + (params ? (url.indexOf('?') !== -1 ? '&' : '?') + params : '');
320 var currentUrl = this.relativeUrl(TYPO3.Backend.NavigationIframe.getUrl());
321 if (currentUrl !== navUrl) {
322 TYPO3.Backend.NavigationIframe.setUrl(navUrl);
323 }
324 },
325
326 openInContentFrame: function(url, params) {
327 if (top.nextLoadModuleUrl) {
328 TYPO3.Backend.ContentContainer.setUrl(top.nextLoadModuleUrl);
329 top.nextLoadModuleUrl = '';
330 } else {
331 TYPO3.Backend.ContentContainer.setUrl(url + (params ? (url.indexOf('?') !== -1 ? '&' : '?') + params : ''));
332 }
333 },
334
335 highlightModuleMenuItem: function(module, mainModule) {
336 TYPO3.Backend.ModuleMenuContainer.getComponent('modDataView').select(module, false, false);
337 },
338
339 relativeUrl: function(url) {
340 return url.replace(TYPO3.configuration.siteUrl + 'typo3/', '');
341 },
342
343 refreshMenu: function() {
344 TYPO3.ModuleMenu.Store.load({
345 scope: this,
346 callback: function(records, options) {
347 this.renderMenu(records);
348 if (this.loadedModule) {
349 this.highlightModuleMenuItem(this.loadedModule);
350 }
351 }
352 });
353 },
354
355 reloadFrames: function() {
356 TYPO3.Backend.NavigationIframe.refresh();
357 TYPO3.Backend.ContentContainer.refresh();
358 }
359
360 };
361
362
363
364 Ext.onReady(function() {
365 TYPO3.ModuleMenu.App.init();
366
367 // keep backward compatibility
368 top.list = TYPO3.Backend.ContentContainer;
369 top.list_frame = top.list.getIframe();
370 top.nav_frame = TYPO3.Backend.NavigationContainer.PageTree;
371
372 top.TYPO3ModuleMenu = TYPO3.ModuleMenu.App;
373 top.content = {
374 nav_frame: TYPO3.Backend.NavigationContainer.PageTree,
375 list_frame: TYPO3.Backend.ContentContainer.getIframe(),
376 location: TYPO3.Backend.ContentContainer.getIframe().location,
377 document: TYPO3.Backend.ContentContainer.getIframe()
378 }
379 });
380
381
382 /*******************************************************************************
383 *
384 * Backwards compatability handling down here
385 *
386 ******************************************************************************/
387
388 /**
389 * Highlight module:
390 */
391 var currentlyHighLightedId = '';
392 var currentlyHighLighted_restoreValue = '';
393 var currentlyHighLightedMain = '';
394 function highlightModuleMenuItem(trId, mainModule) {
395 TYPO3.ModuleMenu.App.highlightModule(trId, mainModule);
396 }