1
|
|
|
/** |
2
|
|
|
* EGroupware desktop framework |
3
|
|
|
* |
4
|
|
|
* @package framework |
5
|
|
|
* @author Hadi Nategh <[email protected]> |
6
|
|
|
* @author Andreas Stoeckel <[email protected]> |
7
|
|
|
* @copyright Stylite AG 2014 |
8
|
|
|
* @description Create jdots framework |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
/*egw:uses |
12
|
|
|
vendor.bower-asset.jquery.dist.jquery; |
13
|
|
|
framework.fw_base; |
14
|
|
|
framework.fw_browser; |
15
|
|
|
framework.fw_ui; |
16
|
|
|
framework.fw_classes; |
17
|
|
|
egw_inheritance.js; |
18
|
|
|
*/ |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* |
22
|
|
|
* @param {DOMWindow} window |
23
|
|
|
*/ |
24
|
|
|
(function(window) |
25
|
|
|
{ |
26
|
|
|
"use strict"; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* |
30
|
|
|
* @type @exp;fw_ui_sidemenu_entry@call;extend |
31
|
|
|
*/ |
32
|
|
|
var desktop_ui_sidemenu_entry = fw_ui_sidemenu_entry.extend({ |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Override fw_ui_sidemenu_entry class constructor |
36
|
|
|
* |
37
|
|
|
* @returns {undefined} |
38
|
|
|
*/ |
39
|
|
|
init: function() |
40
|
|
|
{ |
41
|
|
|
this._super.apply(this,arguments); |
42
|
|
|
|
43
|
|
|
this.setBottomLine(this.parent.entries); |
44
|
|
|
//Make the base Div sortable. Set all elements with the style "egw_fw_ui_sidemenu_entry_header" |
45
|
|
|
//as handle |
46
|
|
|
if(jQuery(this.elemDiv).data('uiSortable')) |
47
|
|
|
{ |
48
|
|
|
jQuery(this.elemDiv).sortable("destroy"); |
49
|
|
|
} |
50
|
|
|
jQuery(this.elemDiv).sortable({ |
51
|
|
|
handle: ".egw_fw_ui_sidemenu_entry_header", |
52
|
|
|
distance: 15, |
53
|
|
|
start: function(event, ui) |
54
|
|
|
{ |
55
|
|
|
var parent = ui.item.context._parent; |
56
|
|
|
parent.isDraged = true; |
57
|
|
|
parent.parent.startDrag.call(parent.parent); |
58
|
|
|
}, |
59
|
|
|
stop: function(event, ui) |
60
|
|
|
{ |
61
|
|
|
var parent = ui.item.context._parent; |
62
|
|
|
parent.parent.stopDrag.call(parent.parent); |
63
|
|
|
parent.parent.refreshSort.call(parent.parent); |
64
|
|
|
}, |
65
|
|
|
opacity: 0.7, |
66
|
|
|
axis: 'y' |
67
|
|
|
}); |
68
|
|
|
}, |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* setBottomLine marks this element as the bottom element in the application list. |
72
|
|
|
* This adds the egw_fw_ui_sidemenu_entry_content_bottom/egw_fw_ui_sidemenu_entry_header_bottom CSS classes |
73
|
|
|
* which should care about adding an closing bottom line to the sidemenu. These classes are removed from |
74
|
|
|
* all other entries in the side menu. |
75
|
|
|
* @param {type} _entryList is a reference to the list which contains the sidemenu_entry entries. |
76
|
|
|
*/ |
77
|
|
|
setBottomLine: function(_entryList) |
78
|
|
|
{ |
79
|
|
|
//If this is the last tab in the tab list, the bottom line must be closed |
80
|
|
|
for (var i = 0; i < _entryList.length; i++) |
81
|
|
|
{ |
82
|
|
|
jQuery(_entryList[i].contentDiv).removeClass("egw_fw_ui_sidemenu_entry_content_bottom"); |
83
|
|
|
jQuery(_entryList[i].headerDiv).removeClass("egw_fw_ui_sidemenu_entry_header_bottom"); |
84
|
|
|
} |
85
|
|
|
jQuery(this.contentDiv).addClass("egw_fw_ui_sidemenu_entry_content_bottom"); |
86
|
|
|
jQuery(this.headerDiv).addClass("egw_fw_ui_sidemenu_entry_header_bottom"); |
87
|
|
|
} |
88
|
|
|
}); |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* |
92
|
|
|
* @type @exp;fw_ui_sidemenu@call;extend |
93
|
|
|
*/ |
94
|
|
|
var desktop_ui_sidemenu = fw_ui_sidemenu.extend( |
95
|
|
|
{ |
96
|
|
|
init: function(_baseDiv, _sortCallback) |
97
|
|
|
{ |
98
|
|
|
this._super.apply(this,arguments); |
99
|
|
|
this.sortCallback = _sortCallback; |
100
|
|
|
}, |
101
|
|
|
/** |
102
|
|
|
* |
103
|
|
|
* @returns {undefined} |
104
|
|
|
*/ |
105
|
|
|
startDrag: function() |
106
|
|
|
{ |
107
|
|
|
if (this.activeEntry) |
108
|
|
|
{ |
109
|
|
|
jQuery(this.activeEntry.marker).show(); |
110
|
|
|
jQuery(this.elemDiv).sortable("refresh"); |
111
|
|
|
} |
112
|
|
|
}, |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* |
116
|
|
|
* @returns {undefined} |
117
|
|
|
*/ |
118
|
|
|
stopDrag: function() |
119
|
|
|
{ |
120
|
|
|
if (this.activeEntry) |
121
|
|
|
{ |
122
|
|
|
jQuery(this.activeEntry.marker).hide(); |
123
|
|
|
jQuery(this.elemDiv).sortable("refresh"); |
124
|
|
|
} |
125
|
|
|
}, |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Called by the sidemenu elements whenever they were sorted. An array containing |
129
|
|
|
* the sidemenu_entries ui-objects is generated and passed to the sort callback |
130
|
|
|
*/ |
131
|
|
|
refreshSort: function() |
132
|
|
|
{ |
133
|
|
|
//Step through all children of elemDiv and add all markers to the result array |
134
|
|
|
var resultArray = new Array(); |
135
|
|
|
this._searchMarkers(resultArray, this.elemDiv.childNodes); |
136
|
|
|
|
137
|
|
|
//Call the sort callback with the array containing the sidemenu_entries |
138
|
|
|
this.sortCallback(resultArray); |
139
|
|
|
}, |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Adds an entry to the sidemenu. |
143
|
|
|
* @param {type} _name specifies the title of the new sidemenu entry |
144
|
|
|
* @param {type} _icon specifies the icon displayed aside the title |
145
|
|
|
* @param {type} _callback specifies the function which should be called when a callback is clicked |
146
|
|
|
* @param {type} _tag extra data |
147
|
|
|
* @param {type} _app application name |
148
|
|
|
* |
149
|
|
|
* @returns {desktop_ui_sidemenu_entry} |
150
|
|
|
*/ |
151
|
|
|
addEntry: function(_name, _icon, _callback, _tag, _app) |
152
|
|
|
{ |
153
|
|
|
//Create a new sidemenu entry and add it to the list |
154
|
|
|
var entry = new desktop_ui_sidemenu_entry(this, this.baseDiv, this.elemDiv, _name, _icon, |
155
|
|
|
_callback, _tag, _app); |
156
|
|
|
this.entries[this.entries.length] = entry; |
157
|
|
|
|
158
|
|
|
return entry; |
159
|
|
|
} |
160
|
|
|
}); |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* jdots framework object defenition |
164
|
|
|
* here we can add framework methods and also override fw_base methods if it is neccessary |
165
|
|
|
* @type @exp;fw_base@call;extend |
166
|
|
|
*/ |
167
|
|
|
window.fw_desktop = fw_base.extend({ |
168
|
|
|
/** |
169
|
|
|
* jdots framework constructor |
170
|
|
|
* |
171
|
|
|
* @param {string} _sidemenuId sidebar menu div id |
172
|
|
|
* @param {string} _tabsId tab area div id |
173
|
|
|
* @param {string} _splitterId splitter div id |
174
|
|
|
* @param {string} _webserverUrl specifies the egroupware root url |
175
|
|
|
* @param {function} _sideboxSizeCallback |
176
|
|
|
* @param {int} _sideboxStartSize sidebox start size |
177
|
|
|
* @param {int} _sideboxMinSize sidebox minimum size |
178
|
|
|
*/ |
179
|
|
|
init:function (_sidemenuId, _tabsId, _webserverUrl, _sideboxSizeCallback, _splitterId, _sideboxStartSize, _sideboxMinSize) |
180
|
|
|
{ |
181
|
|
|
// call fw_base constructor, in order to build basic DOM elements |
182
|
|
|
this._super.apply(this,arguments); |
183
|
|
|
|
184
|
|
|
this.splitterDiv = document.getElementById(_splitterId); |
185
|
|
|
if (this.sidemenuDiv && this.tabsDiv && this.splitterDiv) |
186
|
|
|
{ |
187
|
|
|
//Wrap a scroll area handler around the applications |
188
|
|
|
this.scrollAreaUi = new egw_fw_ui_scrollarea(this.sidemenuDiv); |
189
|
|
|
|
190
|
|
|
// Create toggleSidebar menu |
191
|
|
|
this.toggleSidebarUi = new egw_fw_ui_toggleSidebar('#egw_fw_basecontainer', this._toggleSidebarCallback,this); |
192
|
|
|
|
193
|
|
|
//Create the sidemenu, the tabs area and the splitter |
194
|
|
|
this.sidemenuUi = new desktop_ui_sidemenu(this.scrollAreaUi.contentDiv, |
195
|
|
|
this.sortCallback); |
196
|
|
|
this.tabsUi = new egw_fw_ui_tabs(this.tabsDiv); |
197
|
|
|
this.splitterUi = new egw_fw_ui_splitter(this.splitterDiv, |
198
|
|
|
EGW_SPLITTER_VERTICAL, this.splitterResize, |
199
|
|
|
[ |
200
|
|
|
{ |
201
|
|
|
"size": _sideboxStartSize, |
202
|
|
|
"minsize": _sideboxMinSize, |
203
|
|
|
"maxsize": screen.availWidth - 50 |
204
|
|
|
} |
205
|
|
|
], this); |
206
|
|
|
|
207
|
|
|
var egw_script = document.getElementById('egw_script_id'); |
208
|
|
|
var apps = egw_script ? egw_script.getAttribute('data-navbar-apps') : null; |
209
|
|
|
this.loadApplications(JSON.parse(apps)); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
_sideboxSizeCallback(_sideboxStartSize); |
213
|
|
|
|
214
|
|
|
// warn user about using IE not compatibilities |
215
|
|
|
// we need to wait until common translations are loaded |
216
|
|
|
egw.langRequireApp(window, 'common', function() |
217
|
|
|
{ |
218
|
|
|
if (navigator && navigator.userAgent.match(/Trident|msie/ig)) |
219
|
|
|
{ |
220
|
|
|
egw.message(egw.lang('Browser %1 %2 is not recommended. You may experience issues and not working features. Please use the latest version of Chrome, Firefox or Edge. Thank You!', 'IE',''), 'info', 'browser:ie:warning'); |
221
|
|
|
} |
222
|
|
|
}); |
223
|
|
|
}, |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* |
227
|
|
|
* @param {array} apps |
228
|
|
|
*/ |
229
|
|
|
loadApplications: function (apps) |
230
|
|
|
{ |
231
|
|
|
var restore = this._super.apply(this, arguments); |
232
|
|
|
|
233
|
|
|
//Generate an array with all tabs which shall be restored sorted in by |
234
|
|
|
//their active state |
235
|
|
|
|
236
|
|
|
//Fill in the sorted_restore array... |
237
|
|
|
var sorted_restore = []; |
238
|
|
|
for (this.appName in restore) |
239
|
|
|
sorted_restore[sorted_restore.length] = restore[this.appName]; |
240
|
|
|
|
241
|
|
|
//...and sort it |
242
|
|
|
sorted_restore.sort(function (a, b) { |
243
|
|
|
return ((a.active < b.active) ? 1 : ((a.active == b.active) ? 0 : -1)); |
244
|
|
|
}); |
245
|
|
|
|
246
|
|
|
//Now actually restore the tabs by passing the application, the url, whether |
247
|
|
|
//this is an legacyApp (null triggers the application default), whether the |
248
|
|
|
//application is hidden (only the active tab is shown) and its position |
249
|
|
|
//in the tab list. |
250
|
|
|
for (var i = 0; i < sorted_restore.length; i++) |
251
|
|
|
this.applicationTabNavigate( |
252
|
|
|
sorted_restore[i].app, sorted_restore[i].url, i != 0, |
253
|
|
|
sorted_restore[i].position, sorted_restore[i]['status']); |
254
|
|
|
|
255
|
|
|
//Set the current state of the tabs and activate TabChangeNotification. |
256
|
|
|
this.serializedTabState = egw.jsonEncode(this.assembleTabList()); |
257
|
|
|
this.notifyTabChangeEnabled = true; |
258
|
|
|
|
259
|
|
|
this.scrollAreaUi.update(); |
260
|
|
|
// Disable loader, if present |
261
|
|
|
jQuery('#egw_fw_loading').hide(); |
262
|
|
|
|
263
|
|
|
}, |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* |
267
|
|
|
* @param {type} _app |
268
|
|
|
* @returns {undefined} |
269
|
|
|
*/ |
270
|
|
|
setActiveApp: function(_app) |
271
|
|
|
{ |
272
|
|
|
var result = this._super.apply(this, arguments); |
273
|
|
|
|
274
|
|
|
if (_app == _app.parentFw.activeApp) |
275
|
|
|
{ |
276
|
|
|
//Set the sidebox width if a application specific sidebox width is set |
277
|
|
|
// do not trigger resize if the sidebar is already in toggle on mode and |
278
|
|
|
// the next set state is the same |
279
|
|
|
if (_app.sideboxWidth !== false && egw.preference('toggleSidebar',_app.appName) == 'off') |
280
|
|
|
{ |
281
|
|
|
this.sideboxSizeCallback(_app.sideboxWidth); |
282
|
|
|
this.splitterUi.constraints[0].size = _app.sideboxWidth; |
283
|
|
|
} |
284
|
|
|
_app.parentFw.scrollAreaUi.update(); |
285
|
|
|
_app.parentFw.scrollAreaUi.setScrollPos(0); |
286
|
|
|
} |
287
|
|
|
//Resize the scroll area... |
288
|
|
|
this.scrollAreaUi.update(); |
289
|
|
|
|
290
|
|
|
//...and scroll to the top |
291
|
|
|
this.scrollAreaUi.setScrollPos(0); |
292
|
|
|
|
293
|
|
|
// Handles toggleSidebar initialization |
294
|
|
|
if (typeof framework != 'undefined') |
295
|
|
|
{ |
296
|
|
|
framework.getToggleSidebarState(); |
297
|
|
|
framework.activeApp.browser.callResizeHandler(); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
return result; |
301
|
|
|
}, |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Function called whenever the sidemenu entries are sorted |
305
|
|
|
* @param {type} _entriesArray |
306
|
|
|
*/ |
307
|
|
|
sortCallback: function(_entriesArray) |
308
|
|
|
{ |
309
|
|
|
//Create an array with the names of the applications in their sort order |
310
|
|
|
var name_array = []; |
311
|
|
|
for (var i = 0; i < _entriesArray.length; i++) |
312
|
|
|
{ |
313
|
|
|
name_array.push(_entriesArray[i].tag.appName); |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
//Send the sort order to the server via ajax |
317
|
|
|
var req = egw.jsonq('EGroupware\\Api\\Framework\\Ajax::ajax_appsort', [name_array]); |
318
|
|
|
}, |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Splitter resize callback |
322
|
|
|
* @param {type} _width |
323
|
|
|
* @param {string} _toggleMode if mode is "toggle" then resize happens without changing splitter preference |
324
|
|
|
* @returns {undefined} |
325
|
|
|
*/ |
326
|
|
|
splitterResize: function(_width, _toggleMode) |
327
|
|
|
{ |
328
|
|
|
if (this.tag.activeApp) |
329
|
|
|
{ |
330
|
|
|
|
331
|
|
|
if (_toggleMode !== "toggle") |
332
|
|
|
{ |
333
|
|
|
egw.set_preference(this.tag.activeApp.internalName, 'jdotssideboxwidth', _width); |
334
|
|
|
|
335
|
|
|
//If there are no global application width values, set the sidebox width of |
336
|
|
|
//the application every time the splitter is resized |
337
|
|
|
if (this.tag.activeApp.sideboxWidth !== false) |
338
|
|
|
{ |
339
|
|
|
this.tag.activeApp.sideboxWidth = _width; |
340
|
|
|
} |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
this.tag.sideboxSizeCallback(_width); |
344
|
|
|
|
345
|
|
|
// Notify app about change |
346
|
|
|
if(this.tag.activeApp && this.tag.activeApp.browser != null) |
347
|
|
|
{ |
348
|
|
|
this.tag.activeApp.browser.callResizeHandler(); |
349
|
|
|
} |
350
|
|
|
}, |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* |
354
|
|
|
*/ |
355
|
|
|
resizeHandler: function() |
356
|
|
|
{ |
357
|
|
|
// Tabs overflow needs to be checked again |
358
|
|
|
this.checkTabOverflow(); |
359
|
|
|
//Resize the browser area of the applications |
360
|
|
|
for (var app in this.applications) |
361
|
|
|
{ |
362
|
|
|
if (this.applications[app].browser != null) |
363
|
|
|
{ |
364
|
|
|
this.applications[app].browser.resize(); |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
//Update the scroll area |
368
|
|
|
this.scrollAreaUi.update(); |
369
|
|
|
}, |
370
|
|
|
/** |
371
|
|
|
* Sets the sidebox data of an application |
372
|
|
|
* @param {object} _app the application whose sidebox content should be set. |
373
|
|
|
* @param {object} _data an array/object containing the data of the sidebox content |
374
|
|
|
* @param {string} _md5 an md5 hash of the sidebox menu content: Only if this hash differs between two setSidebox calles, the sidebox menu will be updated. |
375
|
|
|
*/ |
376
|
|
|
setSidebox: function(_app, _data, _md5) |
377
|
|
|
{ |
378
|
|
|
this._super.apply(this,arguments); |
379
|
|
|
|
380
|
|
|
if (typeof _app == 'string') _app = this.getApplicationByName(_app); |
381
|
|
|
//Set the sidebox width if a application specific sidebox width is set |
382
|
|
|
if (_app && _app == _app.parentFw.activeApp && _app.sideboxWidth !== false ) |
383
|
|
|
{ |
384
|
|
|
this.splitterUi.constraints[0].size = _app.sideboxWidth; |
385
|
|
|
} |
386
|
|
|
this.getToggleSidebarState(); |
387
|
|
|
}, |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* |
391
|
|
|
* @param {app object} _app |
392
|
|
|
* @param {int} _pos |
393
|
|
|
* Checks whether the application already owns a tab and creates one if it doesn't exist |
394
|
|
|
*/ |
395
|
|
|
createApplicationTab: function(_app, _pos) |
396
|
|
|
{ |
397
|
|
|
this._super.apply(this, arguments); |
398
|
|
|
}, |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* Runs after et2 is loaded |
402
|
|
|
* |
403
|
|
|
*/ |
404
|
|
|
et2_loadingFinished: function() { |
405
|
|
|
this.checkTabOverflow(); |
406
|
|
|
var $logout = jQuery('#topmenu_logout'); |
407
|
|
|
var self = this; |
408
|
|
|
if (!$logout.hasClass('onLogout')) |
409
|
|
|
{ |
410
|
|
|
$logout.on('click', function(e){ |
411
|
|
|
e.preventDefault(); |
412
|
|
|
self.callOnLogout(e); |
413
|
|
|
window.framework.redirect(this.href); |
414
|
|
|
}); |
415
|
|
|
$logout.addClass('onLogout'); |
416
|
|
|
} |
417
|
|
|
}, |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* Check to see if the tab header will overflow and want to wrap. |
421
|
|
|
* Deal with it by setting some smaller widths on the tabs. |
422
|
|
|
*/ |
423
|
|
|
checkTabOverflow: function() |
424
|
|
|
{ |
425
|
|
|
var width = 0; |
426
|
|
|
var outer_width = jQuery(this.tabsUi.contHeaderDiv).width(); |
427
|
|
|
var spans = jQuery(this.tabsUi.contHeaderDiv).children('span'); |
428
|
|
|
spans.css('max-width',''); |
429
|
|
|
spans.each(function() { width += jQuery(this).outerWidth(true);}); |
430
|
|
|
if(width > outer_width) |
431
|
|
|
{ |
432
|
|
|
var max_width = Math.floor(outer_width / this.tabsUi.contHeaderDiv.childElementCount) - |
433
|
|
|
(spans.outerWidth(true) - spans.width()); |
434
|
|
|
spans.css('max-width',max_width + 'px'); |
435
|
|
|
} |
436
|
|
|
}, |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* @param {function} _opened |
440
|
|
|
* Sends sidemenu entry category open/close information to the server using an AJAX request |
441
|
|
|
*/ |
442
|
|
|
categoryOpenCloseCallback: function(_opened) |
443
|
|
|
{ |
444
|
|
|
egw.set_preference(this.tag.appName, 'jdots_sidebox_'+this.catName, _opened); |
445
|
|
|
}, |
446
|
|
|
|
447
|
|
|
categoryAnimationCallback: function() |
448
|
|
|
{ |
449
|
|
|
this.tag.parentFw.scrollAreaUi.update(); |
450
|
|
|
}, |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* toggleSidebar callback function, handles preference and resize |
454
|
|
|
* @param {string} _state state can be on/off |
455
|
|
|
*/ |
456
|
|
|
_toggleSidebarCallback: function (_state) |
457
|
|
|
{ |
458
|
|
|
var splitterWidth = egw.preference('jdotssideboxwidth',this.activeApp.appName) || this.activeApp.sideboxWidth; |
459
|
|
|
if (_state === "on") |
460
|
|
|
{ |
461
|
|
|
this.splitterUi.resizeCallback(70,'toggle'); |
462
|
|
|
egw.set_preference(this.activeApp.appName, 'toggleSidebar', 'on'); |
463
|
|
|
} |
464
|
|
|
else |
465
|
|
|
{ |
466
|
|
|
this.splitterUi.resizeCallback(splitterWidth); |
467
|
|
|
egw.set_preference(this.activeApp.appName, 'toggleSidebar', 'off'); |
468
|
|
|
} |
469
|
|
|
}, |
470
|
|
|
|
471
|
|
|
/** |
472
|
|
|
* function to get the stored toggleSidebar state and set the sidebar accordingly |
473
|
|
|
*/ |
474
|
|
|
getToggleSidebarState: function() |
475
|
|
|
{ |
476
|
|
|
var toggleSidebar = egw.preference('toggleSidebar',this.activeApp.appName); |
477
|
|
|
this.toggleSidebarUi.set_toggle(toggleSidebar?toggleSidebar:"off", this._toggleSidebarCallback, this); |
478
|
|
|
}, |
479
|
|
|
|
480
|
|
|
toggle_avatar_menu: function () |
481
|
|
|
{ |
482
|
|
|
var $menu = jQuery('#egw_fw_topmenu'); |
483
|
|
|
var $body = jQuery('body'); |
484
|
|
|
if (!$menu.is(":visible")) |
485
|
|
|
{ |
486
|
|
|
$body.on('click', function(e){ |
487
|
|
|
if (e.target.id != 'topmenu_info_user_avatar' && jQuery(e.target).parents('#topmenu_info_user_avatar').length < 1) |
488
|
|
|
{ |
489
|
|
|
jQuery(this).off(e); |
490
|
|
|
$menu.toggle(); |
491
|
|
|
} |
492
|
|
|
}); |
493
|
|
|
} |
494
|
|
|
else |
495
|
|
|
{ |
496
|
|
|
$body.off('click'); |
497
|
|
|
} |
498
|
|
|
$menu.toggle(); |
499
|
|
|
}, |
500
|
|
|
|
501
|
|
|
callOnLogout: function(e) { |
502
|
|
|
var apps = Object.keys(framework.applications); |
503
|
|
|
for(var i in apps) |
504
|
|
|
{ |
505
|
|
|
if (app[apps[i]] && typeof app[apps[i]].onLogout === "function") |
506
|
|
|
{ |
507
|
|
|
app[apps[i]].onLogout.call(e); |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
} |
511
|
|
|
}); |
512
|
|
|
})(window); |
513
|
|
|
|