').attr('class', 'input-group-addon icon ' + input.attr('name').replace('_', ''));
$(this).addClass('form-group row');
label.parent().css('display', 'none');
input.addClass('form-control')
.attr('placeholder', label.text())
.before(icon)
.parent().addClass('input-group');
});
}
};
/**
* Initializes popup menus
*/
function dropdowns_init()
{
$('*[data-popup]').each(function() { popup_init(this); });
// close popups on click in an iframe on the page
var close_all_popups = function(e) {
$('.popover-body:visible').each(function() {
var button = $(this).children('*:first').data('button');
if (e.target != button) {
$(button).popover('hide');
}
});
};
$(document).on('click', popups_close);
rcube_webmail.set_iframe_events({mousedown: popups_close});
};
/**
* Init content frame
*/
function content_frame_init()
{
// when loading content-frame in small-screen mode display it
layout.content.find('iframe').on('load', function(e) {
var show = true;
try {
show = !e.target.contentWindow.location.href.endsWith(rcmail.env.blankpage);
}
catch(e) { /* ignore */ }
if (show && !layout.content.is(':visible')) {
env.last_selected = layout.content[0];
screen_resize();
}
else if (!show) {
$('.header > .header-title', layout.content).text('');
}
});
// display the list widget after 'list' and 'listgroup' commands
// @TODO: plugins should be able to do the same
var list_handler = function(e) {
if (mode != 'wide') {
if (rcmail.env.task == 'addressbook' || (rcmail.env.task == 'mail' && !rcmail.env.action)) {
show_list();
}
}
// display current folder name in list header
if (rcmail.env.task == 'mail' && !rcmail.env.action) {
var name = $.type(e) == 'string' ? e : rcmail.env.mailbox;
var folder = rcmail.env.mailboxes[name];
$('.header > .header-title', layout.list).text(folder ? folder.name : '');
}
};
rcmail
.addEventListener('afterlist', list_handler)
.addEventListener('afterlistgroup', list_handler)
.addEventListener('afterlistsearch', list_handler);
};
/**
* Handler for editor-init event
*/
function tinymce_init(o)
{
// Enable autoresize plugin
o.config.plugins += ' autoresize';
// FIXME: only for mobile?
if (mode == 'phone') {
// Make the toolbar icons bigger
o.config.toolbar_items_size = null;
// Use minimalistic toolbar
o.config.toolbar = 'undo redo | insert | styleselect';
if (o.config.plugins.match(/emoticons/)) {
o.config.toolbar += ' emoticons';
}
}
};
/**
* Handler for some Roundcube core popups
*/
function rcmail_popup_init(o)
{
// Add some common styling to the autocomplete/googiespell popups
$('table,ul', o.obj).addClass('listing iconized');
$(o.obj).addClass('popupmenu');
bootstrap_style(o.obj);
};
/**
* Window resize handler
* Does layout reflows e.g. on screen orientation change
*/
function resize()
{
var size, width = $(window).width();
if (width <= 480)
size = 'phone';
else if (width > 1200)
size = 'wide';
else if (width <= 768)
size = 'tablet';
else
size = 'normal';
mode = size;
screen_resize();
screen_resize_body();
display_screen_size(); // debug info
};
// for development only (to be removed)
function display_screen_size()
{
if (rcmail.is_framed()) {
return;
}
var div = $('#screen-size'), win = $(window);
if (!div.length) {
div = $('').attr({
id: 'screen-size',
style: 'position:absolute;display:block;right:0;z-index:100;'
+ (rcmail.is_framed() ? 'top:0;' : 'bottom:0;')
+ 'opacity:0.5;color:white;background-color:black;white-space:nowrap'
}).appendTo(document.body);
}
div.text(win.width() + ' x ' + win.height() + ' (' + mode + ')');
};
function screen_resize()
{
// TODO: Shall we do this in iframes?
switch (mode) {
case 'phone': screen_resize_phone(); break;
case 'tablet': screen_resize_tablet(); break;
case 'normal': screen_resize_normal(); break;
case 'wide': screen_resize_wide(); break;
}
};
/**
* Assigns layout-mode-* class to the 'body' element
*
* If we're inside an iframe that is small we have to
* check if the parent window is also small (mobile).
* We use that e.g. to still display desktop-like popovers in dialogs
*/
function screen_resize_body()
{
var _mode = mode, body = $('body');
if (rcmail.is_framed() && parent.$('body')[0].className.match(/layout-mode-([a-z]+)/)) {
_mode = RegExp.$1;
}
if (body[0].className.match(/layout-mode-([a-z]+)/)) {
if (RegExp.$1 == _mode) {
return;
}
body.removeClass('layout-mode-' + RegExp.$1);
}
body.addClass('layout-mode-' + _mode);
};
function screen_resize_phone()
{
screen_resize_small();
layout.menu.hide();
};
function screen_resize_tablet()
{
screen_resize_small();
layout.menu.css('display', 'flex');
};
function screen_resize_small()
{
var show, got_content = false;
if (layout.content.length) {
show = got_content = layout.content.is(env.last_selected);
layout.content.css('display', show ? 'flex' : 'none');
}
if (layout.list.length) {
show = !got_content && layout.list.is(env.last_selected);
layout.list.css('display', show ? 'flex' : 'none');
}
if (layout.sidebar.length) {
show = !got_content && (layout.sidebar.is(env.last_selected) || !layout.list.length);
layout.sidebar.css('display', show ? 'flex' : 'none');
}
if (got_content) {
buttons.back_list.show();
}
$('.header > ul.toolbar', layout.content).addClass('popupmenu');
$.each(content_buttons, function() { $(this).hide(); });
// disable ext-windows and other features
rcmail.set_env(env.small_screen_config);
rcmail.enable_command('extwin', false);
};
function screen_resize_normal()
{
var show;
if (layout.list.length) {
show = layout.list.is(env.last_selected) || !layout.sidebar.is(env.last_selected);
layout.list.css('display', show ? 'flex' : 'none');
}
if (layout.sidebar.length) {
show = !layout.list.length || layout.sidebar.is(env.last_selected);
layout.sidebar.css('display', show ? 'flex' : 'none');
}
layout.content.css('display', 'flex');
layout.menu.css('display', 'flex');
buttons.back_list.hide();
$.each(content_buttons, function() { $(this).show(); });
$('ul.toolbar.popupmenu').removeClass('popupmenu');
// re-enable ext-windows
rcmail.set_env(env.config);
rcmail.enable_command('extwin', true);
};
function screen_resize_wide()
{
$.each(layout, function(name, item) { item.css('display', 'flex'); });
buttons.back_list.hide();
$.each(content_buttons, function() { $(this).show(); });
$('ul.toolbar.popupmenu').removeClass('popupmenu');
// re-enable ext-windows
rcmail.set_env(env.config);
rcmail.enable_command('extwin', true);
};
function show_sidebar()
{
// show sidebar and hide list
layout.list.hide();
layout.sidebar.css('display', 'flex');
};
function show_list()
{
if (!layout.list.length && !layout.sidebar.length) {
history.back();
}
else {
// show list and hide sidebar and content
layout.sidebar.hide();
layout.list.css('display', 'flex');
hide_content();
}
};
function hide_content()
{
// show sidebar or list, hide content frame
//$(layout.content).hide();
env.last_selected = layout.list[0] || layout.sidebar[0];
screen_resize();
// reset content frame, so we can load it again
rcmail.show_contentframe(false);
// now we have to unselect selected row on the list
$('[data-list]', layout.list).each(function() {
var list = $(this).data('list');
if (rcmail[list]) {
if (rcmail[list].clear_selection) {
rcmail[list].clear_selection(); // list widget
}
else if (rcmail[list].select) {
rcmail[list].select(); // treelist widget
}
}
});
};
// show menu widget
function show_menu()
{
var display = 'flex';
if (mode == 'phone') {
display = layout.menu.is(':visible') ? 'none' : 'block';
}
layout.menu.css('display', display);
};
/**
* Triggered when a UI message is displayed
*/
function message_displayed(p)
{
alert_style(p.object, p.type);
$(p.object).attr('role', 'alert');
$('a', p.object).addClass('alert-link');
/*
var siblings = $(p.object).siblings('div');
if (siblings.length)
$(p.object).insertBefore(siblings.first());
// show a popup dialog on errors
if (p.type == 'error' && rcmail.env.task != 'login') {
// hide original message object, we don't want both
rcmail.hide_message(p.object);
}
*/
};
/**
* Applies some styling and icon to an alert object
*/
function alert_style(object, type)
{
var cl, classes = 'ui alert',
map = {
information: 'alert-success',
confirmation: 'alert-success',
notice: 'alert-info',
error: 'alert-danger',
warning: 'alert-warning',
loading: 'alert-info loading'
};
if (cl = map[type]) {
classes += ' ' + cl;
$('').attr('class', 'icon').prependTo(object);
}
$(object).addClass(classes);
}
/**
* Set UI dialogs size/style depending on screen size
*/
function dialog_open(dialog)
{
var me = $(dialog.uiDialog),
width = me.width(),
height = me.height(),
maxWidth = $(window).width(),
maxHeight = $(window).height();
if (maxWidth <= 480) {
me.css({width: '100%', height: '100%'});
}
else {
if (height > maxHeight) {
me.css('height', '100%');
}
if (width > maxWidth) {
me.css('width', '100%');
}
}
// Display loader when the dialog has an iframe
iframe_loader($('div.popup > iframe', me));
// TODO: style buttons/forms
bootstrap_style(dialog.uiDialog);
};
/**
* Initializes searchbar widget
*/
function searchbar_init(bar)
{
var input = $('input', bar),
button = $('a.button.search', bar),
settings_button = $('a.button.settings', bar)[0],
form = $('form', bar),
all_elements = $('form, a.button.options, a.button.reset', bar),
is_search_pending = function() {
// TODO: This have to be improved to detect real searching state
// There are cases when search is active but the input is empty
return input.val();
},
hide_func = function(event, focus) {
if (button.is(':visible')) {
return;
}
$(bar).animate({'width': '0'}, 200, 'swing', function() {
all_elements.hide();
$(bar).width('auto'); // fixes search button position in Chrome
button[is_search_pending() ? 'addClass' : 'removeClass']('active')
.css('display', 'block');
if (focus) {
button.focus();
}
});
};
if (is_search_pending()) {
button.addClass('active');
}
// Display search form (with animation effect)
button.on('click', function() {
$(bar).animate({'width': '100%'}, 200);
all_elements.css('display', 'table-cell');
button.hide();
input.focus();
});
// Search reset action
$('a.button.reset', bar).on('click', function(e) {
// for treelist widget's search setting val and keyup.treelist is needed
// in normal search form reset-search command will do the trick
// TODO: This calls for some generalization, what about two searchboxes on a page?
input.val('').change().trigger('keyup.treelist', {keyCode: 27});
hide_func(e, true);
});
// These will hide the form, but not reset it
rcube_webmail.set_iframe_events({mousedown: hide_func});
$('body').on('mousedown', function(e) {
if ($.inArray(bar, $(e.target).parents()) == -1) {
hide_func(e);
}
});
};
/**
* Converts toolbar menu into popup-menu for small screens
*/
function toolbar_init()
{
if (env.got_smart_toolbar) {
return;
}
env.got_smart_toolbar = true;
// TODO: if the toolbar contains "global or list only" buttons
// another popup menu with these options should be created
// on the list (or sidebar if there's no list element).
// TODO: spacer item
// TODO: a way to inject buttons to the menu from content iframe
// or automatically add all buttons except Save and Cancel
// (example QR Code button in contact frame)
var items = [];
// convert toolbar to a popup list
$('.header > .toolbar', layout.content).each(function() {
var toolbar = $(this);
toolbar.children().each(function() {
var button = $(this).detach();
// Remove empty text nodes that break alignment of text of the menu item
button.contents().filter(function() { if (this.nodeType == 3 && !$.trim(this.nodeValue).length) $(this).remove(); });
items.push($('').append(button));
});
toolbar.remove();
});
// append the new toolbar and menu button
if (items.length) {
var container = layout.content.children('.header'),
menu_attrs = {'class': 'toolbar popupmenu listing', id: 'toolbar-menu'},
menu_button = $('