define([
    'jquery',
    '/customize/application_config.js',
    '/api/config',
], function ($, Config, ApiConfig) {
    var Messages = {};
    var Cryptpad;
    var Common;

    var Bar = {
        constants: {},
    };

    var SPINNER_DISAPPEAR_TIME = 1000;

    // Toolbar parts
    var TOOLBAR_CLS = Bar.constants.toolbar = 'cp-toolbar';
    var TOP_CLS = Bar.constants.top = 'cp-toolbar-top';
    var LEFTSIDE_CLS = Bar.constants.leftside = 'cp-toolbar-leftside';
    var RIGHTSIDE_CLS = Bar.constants.rightside = 'cp-toolbar-rightside';
    var DRAWER_CLS = Bar.constants.drawer = 'cp-toolbar-drawer-content';
    var HISTORY_CLS = Bar.constants.history = 'cp-toolbar-history';

    // Userlist
    var USERLIST_CLS = Bar.constants.userlist = "cp-toolbar-users";
    var EDITSHARE_CLS = Bar.constants.editShare = "cp-toolbar-share-edit";
    var VIEWSHARE_CLS = Bar.constants.viewShare = "cp-toolbar-share-view";
    var SHARE_CLS = Bar.constants.viewShare = "cp-toolbar-share";

    // Top parts
    var USER_CLS = Bar.constants.userAdmin = "cp-toolbar-user";
    var SPINNER_CLS = Bar.constants.spinner = 'cp-toolbar-spinner';
    var LIMIT_CLS = Bar.constants.limit = 'cp-toolbar-limit';
    var TITLE_CLS = Bar.constants.title = "cp-toolbar-title";
    var NEWPAD_CLS = Bar.constants.newpad = "cp-toolbar-new";
    var LINK_CLS = Bar.constants.link = "cp-toolbar-link";

    // User admin menu
    var USERADMIN_CLS = Bar.constants.user = 'cp-toolbar-user-dropdown';
    var USERNAME_CLS = Bar.constants.username = 'cp-toolbar-user-name';
    /*var READONLY_CLS = */Bar.constants.readonly = 'cp-toolbar-readonly';
    var USERBUTTON_CLS = Bar.constants.changeUsername = "cp-toolbar-user-rename";

    // Create the toolbar element

    var uid = function () {
        return 'cp-toolbar-uid-' + String(Math.random()).substring(2);
    };

    var createRealtimeToolbar = function (config) {
        if (!config.$container) { return; }
        var $container = config.$container;

        var isEmbed = Bar.isEmbed = config.metadataMgr.getPrivateData().isEmbed;
        if (isEmbed) {
            $container.hide();
        }

        var $toolbar = $('<div>', {
            'class': TOOLBAR_CLS,
            id: uid(),
        });

        var $topContainer = $('<div>', {'class': TOP_CLS});
        $('<span>', {'class': 'cp-toolbar-top-filler'}).appendTo($topContainer);
        var $userContainer = $('<span>', {
            'class': USER_CLS
        }).appendTo($topContainer);
        $('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer);
        $('<span>', {'class': NEWPAD_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
        $('<span>', {'class': USERADMIN_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);

        $toolbar.append($topContainer)
        .append($('<div>', {'class': LEFTSIDE_CLS}))
        .append($('<div>', {'class': RIGHTSIDE_CLS}))
        .append($('<div>', {'class': HISTORY_CLS}));

        var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS);
        if (!config.hideDrawer) {
            var $drawerContent = $('<div>', {
                'class': DRAWER_CLS,
                'tabindex': 1
            }).appendTo($rightside).hide();
            var $drawer = Common.createButton('more', true).appendTo($rightside);
            $drawer.click(function () {
                $drawerContent.toggle();
                $drawer.removeClass('cp-toolbar-button-active');
                if ($drawerContent.is(':visible')) {
                    $drawer.addClass('cp-toolbar-button-active');
                    $drawerContent.focus();
                }
            });
            var onBlur = function (e) {
                if (e.relatedTarget) {
                    if ($(e.relatedTarget).is('.cp-toolbar-drawer-button')) { return; }
                    if ($(e.relatedTarget).parents('.'+DRAWER_CLS).length) {
                        $(e.relatedTarget).blur(onBlur);
                        return;
                    }
                }
                $drawer.removeClass('cp-toolbar-button-active');
                $drawerContent.hide();
            };
            $drawerContent.blur(onBlur);
        }

        // The 'notitle' class removes the line added for the title with a small screen
        if (!config.title || typeof config.title !== "object") {
            $toolbar.addClass('cp-toolbar-notitle');
        }

        $container.prepend($toolbar);

        $container.on('drop dragover', function (e) {
            e.preventDefault();
            e.stopPropagation();
        });
        return $toolbar;
    };

    // Userlist elements

    var getOtherUsers = function(config) {
        //var userList = config.userList.getUserlist();
        var userData = config.metadataMgr.getMetadata().users;

        var i = 0; // duplicates counter
        var list = [];

        // Display only one time each user (if he is connected in multiple tabs)
        var uids = [];
        Object.keys(userData).forEach(function(user) {
            //if (user !== userNetfluxId) {
                var data = userData[user] || {};
                var userId = data.uid;
                if (!userId) { return; }
                //data.netfluxId = user;
                if (uids.indexOf(userId) === -1) {// && (!myUid || userId !== myUid)) {
                    uids.push(userId);
                    list.push(data);
                } else { i++; }
            //}
        });
        return {
            list: list,
            duplicates: i
        };
    };

    var avatars = {};
    var updateUserList = function (toolbar, config) {
        // Make sure the elements are displayed
        var $userButtons = toolbar.userlist;
        var $userlistContent = toolbar.userlistContent;

        var metadataMgr = config.metadataMgr;
        var userData = metadataMgr.getMetadata().users;
        var viewers = metadataMgr.getViewers();
        var priv = metadataMgr.getPrivateData();
        var origin = priv.origin;
        var friends = priv.friends;
        var user = metadataMgr.getUserData();

        // If we are using old pads (readonly unavailable), only editing users are in userList.
        // With new pads, we also have readonly users in userList, so we have to intersect with
        // the userData to have only the editing users. We can't use userData directly since it
        // may contain data about users that have already left the channel.
        //userList = config.readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));

        // Names of editing users
        var others = getOtherUsers(config);
        var editUsersNames = others.list;
        var duplicates = others.duplicates; // Number of duplicates

        editUsersNames.sort(function (a, b) {
            var na = a.name || Messages.anonymous;
            var nb = b.name || Messages.anonymous;
            return na.toLowerCase() > nb.toLowerCase();
        });

        var numberOfEditUsers = Object.keys(userData).length - duplicates;
        var numberOfViewUsers = viewers;

        // Update the userlist
        var $editUsers = $userlistContent.find('.' + USERLIST_CLS).html('');

        var $editUsersList = $('<div>', {'class': 'cp-toolbar-userlist-others'});

        // Editors
        var pendingFriends = Common.getPendingFriends();
        editUsersNames.forEach(function (data) {
            var name = data.name || Messages.anonymous;
            var $span = $('<span>', {'class': 'cp-avatar'});
            var $rightCol = $('<span>', {'class': 'cp-toolbar-userlist-rightcol'});
            var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).text(name).appendTo($rightCol);
            var isMe = data.curvePublic === user.curvePublic;
            if (Common.isLoggedIn() && data.curvePublic) {
                if (isMe) {
                    $span.attr('title', Messages._getKey('userlist_thisIsYou', [
                        name
                    ]));
                    $nameSpan.text(name);
                } else if (!friends[data.curvePublic]) {
                    if (pendingFriends.indexOf(data.netfluxId) !== -1) {
                        $('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
                            .appendTo($rightCol);
                    } else {
                        $('<span>', {
                            'class': 'fa fa-user-plus cp-toolbar-userlist-friend',
                            'title': Messages._getKey('userlist_addAsFriendTitle', [
                                name
                            ])
                        }).appendTo($rightCol).click(function (e) {
                            e.stopPropagation();
                            Common.sendFriendRequest(data.netfluxId);
                        });
                    }
                }
            }
            if (data.profile) {
                $span.addClass('cp-userlist-clickable');
                $span.click(function () {
                    window.open(origin+'/profile/#' + data.profile);
                });
            }
            if (data.avatar && avatars[data.avatar]) {
                $span.append(avatars[data.avatar]);
                $span.append($rightCol);
            } else {
                Common.displayAvatar($span, data.avatar, name, function ($img) {
                    if (data.avatar && $img) {
                        avatars[data.avatar] = $img[0].outerHTML;
                    }
                    $span.append($rightCol);
                });
            }
            $span.data('uid', data.uid);
            $editUsersList.append($span);
        });
        $editUsers.append($editUsersList);

        // Viewers
        if (numberOfViewUsers > 0) {
            var viewText = '<div class="cp-toolbar-userlist-viewer">';
            var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
            viewText += numberOfViewUsers + ' ' + viewerText + '</div>';
            $editUsers.append(viewText);
        }

        // Update the buttons
        var fa_editusers = '<span class="fa fa-users"></span>';
        var fa_viewusers = '<span class="fa fa-eye"></span>';
        var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
        $userButtons.find('.cp-dropdown-button-title').html('').append($spansmall);
    };

    var initUserList = function (toolbar, config) {
        if (config.metadataMgr) {
            var metadataMgr = config.metadataMgr;
            metadataMgr.onChange(function () {
                if (metadataMgr.isConnected()) {toolbar.connected = true;}
                if (!toolbar.connected) { return; }
                updateUserList(toolbar, config);
            });
        }
    };


    // Create sub-elements

    var createUserList = function (toolbar, config) {
        if (!config.metadataMgr) {
            throw new Error("You must provide a `metadataMgr` to display the userlist");
        }
        var $content = $('<div>', {'class': 'cp-toolbar-userlist-drawer'});
        $content.on('drop dragover', function (e) {
            e.preventDefault();
            e.stopPropagation();
        });
        var $closeIcon = $('<span>', {"class": "fa fa-window-close cp-toolbar-userlist-drawer-close"}).appendTo($content);
        $('<h2>').text(Messages.users).appendTo($content);
        $('<p>', {'class': USERLIST_CLS}).appendTo($content);

        toolbar.userlistContent = $content;

        var $container = $('<span>', {id: 'cp-toolbar-userlist-drawer-open', title: Messages.userListButton});

        var $button = $('<button>').appendTo($container);
        $('<span>',{'class': 'cp-dropdown-button-title'}).appendTo($button);

        toolbar.$leftside.prepend($container);

        if (config.$contentContainer) {
            config.$contentContainer.prepend($content);
        }

        var $ck = config.$container.find('.cke_toolbox_main');
        var mobile = $('body').width() <= 600;
        var hide = function () {
            $content.hide();
            $button.removeClass('cp-toolbar-button-active');
            $ck.css({
                'padding-left': '',
            });
        };
        var show = function () {
            if (Bar.isEmbed) { $content.hide(); return; }
            $content.show();
            if (mobile) {
                $ck.hide();
            }
            $button.addClass('cp-toolbar-button-active');
            $ck.css({
                'padding-left': '175px',
            });
            var h = $ck.is(':visible') ? -$ck.height() : 0;
            $content.css('margin-top', h+'px');
        };
        $(window).on('cryptpad-ck-toolbar', function () {
            if (mobile && $ck.is(':visible')) { return void hide(); }
            if ($content.is(':visible')) { return void show(); }
            hide();
        });
        $(window).on('resize', function () {
            mobile = $('body').width() <= 600;
            var h = $ck.is(':visible') ? -$ck.height() : 0;
            $content.css('margin-top', h+'px');
        });
        $closeIcon.click(function () {
            Common.setAttribute(['toolbar', 'userlist-drawer'], false);
            hide();
        });
        $button.click(function () {
            var visible = $content.is(':visible');
            if (visible) { hide(); }
            else { show(); }
            visible = !visible;
            Common.setAttribute(['toolbar', 'userlist-drawer'], visible);
            Common.feedback(visible?'USERLIST_SHOW': 'USERLIST_HIDE');
        });
        show();
        Common.getAttribute(['toolbar', 'userlist-drawer'], function (err, val) {
            if (val === false || mobile) { return void hide(); }
            show();
        });

        initUserList(toolbar, config);
        return $container;
    };

    var createShare = function (toolbar, config) {
        if (!config.metadataMgr) {
            throw new Error("You must provide a `metadataMgr` to display the userlist");
        }
        var metadataMgr = config.metadataMgr;
        var origin = config.metadataMgr.getPrivateData().origin;
        var pathname = config.metadataMgr.getPrivateData().pathname;
        var hashes = metadataMgr.getPrivateData().availableHashes;
        var readOnly = metadataMgr.getPrivateData().readOnly;

        var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
        var options = [];

        if (hashes.editHash) {
            options.push({
                tag: 'a',
                attributes: {title: Messages.editShareTitle, 'class': 'cp-toolbar-share-edit-copy'},
                content: '<span class="fa fa-users"></span> ' + Messages.editShare
            });
            if (readOnly) {
                // We're in view mode, display the "open editing link" button
                options.push({
                    tag: 'a',
                    attributes: {
                        title: Messages.editOpenTitle,
                        'class': 'cp-toolbar-share-edit-open',
                        href: origin + pathname + '#' + hashes.editHash,
                        target: '_blank'
                    },
                    content: '<span class="fa fa-users"></span> ' + Messages.editOpen
                });
            }
            options.push({tag: 'hr'});
        }
        if (hashes.viewHash) {
            options.push({
                tag: 'a',
                attributes: {title: Messages.viewShareTitle, 'class': 'cp-toolbar-share-view-copy'},
                content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
            });
            if (!readOnly) {
                // We're in edit mode, display the "open readonly" button
                options.push({
                    tag: 'a',
                    attributes: {
                        title: Messages.viewOpenTitle,
                        'class': 'cp-toolbar-share-view-open',
                        href: origin + pathname + '#' + hashes.viewHash,
                        target: '_blank'
                    },
                    content: '<span class="fa fa-eye"></span> ' + Messages.viewOpen
                });
            }
            options.push({tag: 'hr'});
            options.push({
                tag: 'a',
                attributes: {
                    title: Messages.viewEmbedTitle,
                    'class': 'cp-toolbar-share-view-embed',
                },
                content: '<span class="fa fa-eye"></span> ' + Messages.getEmbedCode
            });
        }
        var dropdownConfigShare = {
            text: $('<div>').append($shareIcon).html(),
            options: options,
            feedback: 'SHARE_MENU',
        };
        var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
        $shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
        $shareBlock.addClass('cp-toolbar-share-button');
        $shareBlock.find('button').attr('title', Messages.shareButton);

        if (hashes.editHash) {
            $shareBlock.find('a.cp-toolbar-share-edit-copy').click(function () {
                /*Common.storeLinkToClipboard(false, function (err) {
                    if (!err) { Cryptpad.log(Messages.shareSuccess); }
                });*/
                var url = origin + pathname + '#' + hashes.editHash;
                var success = Cryptpad.Clipboard.copy(url);
                if (success) { Cryptpad.log(Messages.shareSuccess); }
            });
        }
        if (hashes.viewHash) {
            $shareBlock.find('a.cp-toolbar-share-view-copy').click(function () {
                /*Common.storeLinkToClipboard(true, function (err) {
                    if (!err) { Cryptpad.log(Messages.shareSuccess); }
                });*/
                var url = origin + pathname + '#' + hashes.viewHash;
                var success = Cryptpad.Clipboard.copy(url);
                if (success) { Cryptpad.log(Messages.shareSuccess); }
            });
            $shareBlock.find('a.cp-toolbar-share-view-embed').click(function () {
                var url = origin + pathname + '#' + hashes.viewHash;
                var parsed = Cryptpad.parsePadUrl(url);
                url = origin + parsed.getUrl({embed: true});
                // Alertify content
                var $content = $('<div>');
                $('<input>', {'style':'display:none;'}).appendTo($content);
                $('<h3>').text(Messages.viewEmbedTitle).appendTo($content);
                var $tag = $('<p>').text(Messages.fileEmbedTag).appendTo($content);
                $('<br>').appendTo($tag);
                var iframeId = uid();
                var iframeEmbed = '<iframe src="' + url + '"></iframe>';
                $('<input>', {
                    type: 'text',
                    id: iframeId,
                    readonly: 'readonly',
                    value: iframeEmbed,
                }).appendTo($tag);
                Cryptpad.alert($content.html(), null, true);
                $('#'+iframeId).click(function () {
                    this.select();
                });
                //var success = Cryptpad.Clipboard.copy(url);
                //if (success) { Cryptpad.log(Messages.shareSuccess); }
            });
        }

        toolbar.$leftside.append($shareBlock);
        toolbar.share = $shareBlock;

        return "Loading share button";
    };

    var createFileShare = function (toolbar, config) {
        if (!config.metadataMgr) {
            throw new Error("You must provide a `metadataMgr` to display the userlist");
        }
        var metadataMgr = config.metadataMgr;
        var origin = config.metadataMgr.getPrivateData().origin;
        var pathname = config.metadataMgr.getPrivateData().pathname;
        var hashes = metadataMgr.getPrivateData().availableHashes;
        var url = origin + pathname + '#' + hashes.fileHash;


        var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
        var options = [];
        options.push({
            tag: 'a',
            attributes: {title: Messages.editShareTitle, 'class': 'cp-toolbar-share-file-copy'},
            content: '<span class="fa fa-file"></span> ' + Messages.fileShare
        });
        options.push({
            tag: 'a',
            attributes: {title: Messages.fileEmbedTitle, 'class': 'cp-toolbar-share-file-embed'},
            content: '<span class="fa fa-file"></span> ' + Messages.getEmbedCode
        });
        var dropdownConfigShare = {
            text: $('<div>').append($shareIcon).html(),
            options: options,
            feedback: 'FILESHARE_MENU',
        };
        var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
        $shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS);
        $shareBlock.addClass('cp-toolbar-share-button');
        $shareBlock.find('button').attr('title', Messages.shareButton);

        // Add handlers
        $shareBlock.find('a.cp-toolbar-share-file-copy').click(function () {
            var success = Cryptpad.Clipboard.copy(url);
            if (success) { Cryptpad.log(Messages.shareSuccess); }
        });
        $shareBlock.find('a.cp-toolbar-share-file-embed').click(function () {
            var $content = $('<div>');
            $('<input>', {'style':'display:none;'}).appendTo($content);
            $('<h3>').text(Messages.fileEmbedTitle).appendTo($content);
            var $script = $('<p>').text(Messages.fileEmbedScript).appendTo($content);
            $('<br>').appendTo($script);
            $script.append(Cryptpad.dialog.selectable(Common.getMediatagScript()));
            var $tag = $('<p>').text(Messages.fileEmbedTag).appendTo($content);
            $('<br>').appendTo($tag);
            $tag.append(Cryptpad.dialog.selectable(Common.getMediatagFromHref(url)));
            Cryptpad.alert($content[0], null, true);
        });

        toolbar.$leftside.append($shareBlock);
        return $shareBlock;
    };

    var createTitle = function (toolbar, config) {
        var $titleContainer = $('<span>', {
            'class': TITLE_CLS
        }).appendTo(toolbar.$top);

        var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);

        if (typeof config.title !== "object") {
            console.error("config.title", config);
            throw new Error("config.title is not an object");
        }
        var updateTitle = config.title.updateTitle;
        var placeholder = config.title.defaultName;
        var suggestName = config.title.suggestName;

        // Buttons
        var $text = $('<span>', {
            'class': 'cp-toolbar-title-value'
        }).appendTo($hoverable);
        var $pencilIcon = $('<span>', {
            'class': 'cp-toolbar-title-edit',
            'title': Messages.clickToEdit
        });
        var $saveIcon = $('<span>', {
            'class': 'cp-toolbar-title-save',
            'title': Messages.saveTitle
        }).hide();
        if (config.readOnly === 1) {
            $titleContainer.append($('<span>', {'class': 'cp-toolbar-title-readonly'})
                .text('('+Messages.readonly+')'));
        }
        if (config.readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
        var $input = $('<input>', {
            type: 'text',
            placeholder: placeholder
        }).appendTo($hoverable).hide();
        if (config.readOnly !== 1) {
            $text.attr("title", Messages.clickToEdit);
            $text.addClass("cp-toolbar-title-editable");
            var $icon = $('<span>', {
                'class': 'fa fa-pencil cp-toolbar-title-icon-readonly',
                style: 'font-family: FontAwesome;'
            });
            $pencilIcon.append($icon).appendTo($hoverable);
            var $icon2 = $('<span>', {
                'class': 'fa fa-check cp-toolbar-title-icon-readonly',
                style: 'font-family: FontAwesome;'
            });
            $saveIcon.append($icon2).appendTo($hoverable);
        }

        // Events
        $input.on('mousedown', function (e) {
            if (!$input.is(":focus")) {
                $input.focus();
            }
            e.stopPropagation();
            return true;
        });
        var save = function () {
            var name = $input.val().trim();
            if (name === "") {
                name = $input.attr('placeholder');
            }
            updateTitle(name, function (err/*, newtitle*/) {
                if (err) { return console.error(err); }
                //$text.text(newtitle);
                $input.hide();
                $text.show();
                $pencilIcon.show();
                $saveIcon.hide();
            });
        };
        $input.on('keyup', function (e) {
            if (e.which === 13 && toolbar.connected === true) {
                save();
            } else if (e.which === 27) {
                $input.hide();
                $text.show();
                $pencilIcon.show();
                $saveIcon.hide();
                //$pencilIcon.css('display', '');
            } else if (e.which === 32) {
                e.stopPropagation();
            }
        });
        $saveIcon.click(save);

        var displayInput = function () {
            if (toolbar.connected === false) { return; }
            $input.width(Math.max($text.width(), 300)+'px');
            $text.hide();
            //$pencilIcon.css('display', 'none');
            var inputVal = suggestName() || "";
            $input.val(inputVal);
            $input.show();
            $input.focus();
            $pencilIcon.hide();
            $saveIcon.show();
        };
        $text.on('click', displayInput);
        $pencilIcon.on('click', displayInput);
        return $titleContainer;
    };

    var createPageTitle = function (toolbar, config) {
        if (config.title || !config.pageTitle) { return; }
        var $titleContainer = $('<span>', {
            'class': TITLE_CLS
        }).appendTo(toolbar.$top);

        toolbar.$top.find('.filler').hide();

        var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);

        // Buttons
        $('<span>', {
            'class': 'cp-toolbar-title-value cp-toolbar-title-value-page'
        }).appendTo($hoverable).text(config.pageTitle);
    };

    var createLinkToMain = function (toolbar, config) {
        var $linkContainer = $('<span>', {
            'class': LINK_CLS
        }).appendTo(toolbar.$top);

        // We need to override the "a" tag action here because it is inside the iframe!
        var inDrive = /^\/drive/;

        var origin = config.metadataMgr.getPrivateData().origin;

        var href = inDrive.test(origin) ? origin+'/index.html' : origin+'/drive/';
        var buttonTitle = inDrive ? Messages.header_homeTitle : Messages.header_logoTitle;

        var $aTag = $('<a>', {
            href: href,
            title: buttonTitle,
            'class': "cp-toolbar-link-logo"
        }).append($('<img>', {
            src: '/customize/images/logo_white.png?' + ApiConfig.requireConf.urlArgs
        }));
        var onClick = function (e) {
            e.preventDefault();
            if (e.ctrlKey) {
                window.open(href);
                return;
            }
            window.parent.location = href;
        };

        var onContext = function (e) { e.stopPropagation(); };

        $aTag.click(onClick).contextmenu(onContext);

        $linkContainer.append($aTag);

        return $linkContainer;
    };

    var typing = -1;
    var kickSpinner = function (toolbar, config/*, local*/) {
        if (!toolbar.spinner) { return; }
        var $spin = toolbar.spinner;

        if (typing === -1) {
            typing = 1;
            $spin.text(Messages.typing);
            $spin.interval = window.setInterval(function () {
                var dots = Array(typing+1).join('.');
                $spin.text(Messages.typing + dots);
                typing++;
                if (typing > 3) { typing = 0; }
            }, 500);
        }
        var onSynced = function () {
            if ($spin.timeout) { clearTimeout($spin.timeout); }
            $spin.timeout = setTimeout(function () {
                window.clearInterval($spin.interval);
                typing = -1;
                $spin.text(Messages.saved);
            }, /*local ? 0 :*/ SPINNER_DISAPPEAR_TIME);
        };
        config.sfCommon.whenRealtimeSyncs(onSynced);
    };
    var ks = function (toolbar, config, local) {
        return function () {
            if (toolbar.connected) { kickSpinner(toolbar, config, local); }
        };
    };
    var createSpinner = function (toolbar, config) {
        var $spin = $('<span>', {'class': SPINNER_CLS}).appendTo(toolbar.$leftside);
        $spin.text(Messages.synchronizing);

        if (config.realtime) {
            config.realtime.onPatch(ks(toolbar, config));
            config.realtime.onMessage(ks(toolbar, config, true));
        }
        // without this, users in read-only mode say 'synchronizing' until they
        // receive a patch.
        typing = 0;
        config.sfCommon.whenRealtimeSyncs(function () {
            kickSpinner(toolbar, config);
        });
        return $spin;
    };

    var createLimit = function (toolbar) {
        if (!Config.enablePinning) { return; }
        var $limitIcon = $('<span>', {'class': 'fa fa-exclamation-triangle'});
        var $limit = toolbar.$userAdmin.find('.'+LIMIT_CLS).attr({
            'title': Messages.pinLimitReached
        }).append($limitIcon).hide();
        var todo = function (e, overLimit) {
            if (e) { return void console.error("Unable to get the pinned usage"); }
            if (overLimit) {
                var key = 'pinLimitReachedAlert';
                if (ApiConfig.noSubscriptionButton === true) {
                    key = 'pinLimitReachedAlertNoAccounts';
                }
                $limit.show().click(function () {
                    Cryptpad.alert(Messages._getKey(key, [encodeURIComponent(window.location.hostname)]), null, true);
                });
            }
        };
        Common.isOverPinLimit(todo);

        return $limit;
    };

    var createNewPad = function (toolbar, config) {
        var $newPad = toolbar.$top.find('.'+NEWPAD_CLS).show();

        var origin = config.metadataMgr.getPrivateData().origin;

        var pads_options = [];
        Config.availablePadTypes.forEach(function (p) {
            if (p === 'drive') { return; }
            if (!Common.isLoggedIn() && Config.registeredOnlyTypes &&
                Config.registeredOnlyTypes.indexOf(p) !== -1) { return; }
            pads_options.push({
                tag: 'a',
                attributes: {
                    'target': '_blank',
                    'href': origin + '/' + p + '/',
                },
                content: $('<div>').append(Cryptpad.getIcon(p)).html() + Messages.type[p]
            });
        });
        var dropdownConfig = {
            text: '', // Button initial text
            options: pads_options, // Entries displayed in the menu
            container: $newPad,
            left: true,
            feedback: /drive/.test(window.location.pathname)?
                'DRIVE_NEWPAD': 'NEWPAD',
        };
        var $newPadBlock = Cryptpad.createDropdown(dropdownConfig);
        $newPadBlock.find('button').attr('title', Messages.newButtonTitle);
        $newPadBlock.find('button').addClass('fa fa-th');
        return $newPadBlock;
    };

    var createUserAdmin = function (toolbar, config) {
        if (!config.metadataMgr) {
            throw new Error("You must provide a `metadataMgr` to display the user menu");
        }
        var metadataMgr = config.metadataMgr;
        var $userAdmin = toolbar.$userAdmin.find('.'+USERADMIN_CLS).show();
        var userMenuCfg = {
            $initBlock: $userAdmin,
        };
        if (!config.hideDisplayName) {
            $.extend(true, userMenuCfg, {
                displayNameCls: USERNAME_CLS,
                changeNameButtonCls: USERBUTTON_CLS,
            });
        }
        if (config.readOnly !== 1) {
            userMenuCfg.displayName = 1;
            userMenuCfg.displayChangeName = 1;
        }
        Common.createUserAdminMenu(userMenuCfg);
        $userAdmin.find('button').attr('title', Messages.userAccountButton);

        var $userButton = toolbar.$userNameButton = $userAdmin.find('a.' + USERBUTTON_CLS);
        $userButton.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            var myData = metadataMgr.getMetadata().users[metadataMgr.getNetfluxId()];
            var lastName = myData.name;
            Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
                if (newName === null && typeof(lastName) === "string") { return; }
                if (newName === null) { newName = ''; }
                else { Common.feedback('NAME_CHANGED'); }
                Common.setDisplayName(newName, function (err) {
                    if (err) {
                        console.log("Couldn't set username");
                        console.error(err);
                        return;
                    }
                    //Cryptpad.changeDisplayName(newName, true); Already done?
                });
            });
        });

        return $userAdmin;
    };

    // Events
    var initClickEvents = function (toolbar) {
        var removeDropdowns =  function () {
            window.setTimeout(function () {
                toolbar.$toolbar.find('.cp-dropdown-content').hide();
            });
        };
        var cancelEditTitle = function (e) {
            // Now we want to apply the title even if we click somewhere else
            if ($(e.target).parents('.' + TITLE_CLS).length || !toolbar.title) {
                return;
            }
            var $title = toolbar.title;
            if (!$title.find('input').is(':visible')) { return; }

            // Press enter
            var ev = $.Event("keyup");
            ev.which = 13;
            $title.find('input').trigger(ev);
        };
        // Click in the main window
        var w = window;
        $(w).on('click', removeDropdowns);
        $(w).on('click', cancelEditTitle);
        // Click in iframes
        try {
            if (w.$ && w.$('iframe').length) {
                w.$('iframe').each(function (i, el) {
                    $(el.contentWindow).on('click', removeDropdowns);
                    $(el.contentWindow).on('click', cancelEditTitle);
                });
            }
        } catch (e) {
            // empty try catch in case this iframe is problematic
        }
    };

    // Notifications
    var initNotifications = function (toolbar, config) {
        // Display notifications when users are joining/leaving the session
        var oldUserData;
        if (!config.metadataMgr) { return; }
        var metadataMgr = config.metadataMgr;
        var userNetfluxId = metadataMgr.getNetfluxId();
        if (typeof Cryptpad !== "undefined") {
            var notify = function(type, name, oldname) {
                // type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
                if (typeof name === "undefined") { return; }
                name = name || Messages.anonymous;
                switch(type) {
                    case 1:
                        Cryptpad.log(Messages._getKey("notifyJoined", [name]));
                        break;
                    case 0:
                        oldname = (!oldname) ? Messages.anonymous : oldname;
                        Cryptpad.log(Messages._getKey("notifyRenamed", [oldname, name]));
                        break;
                    case -1:
                        Cryptpad.log(Messages._getKey("notifyLeft", [name]));
                        break;
                    default:
                        console.log("Invalid type of notification");
                        break;
                }
            };

            var userPresent = function (id, user, data) {
                if (!(user && user.uid)) {
                    console.log('no uid');
                    return 0;
                }
                if (!data) {
                    console.log('no data');
                    return 0;
                }

                var count = 0;
                Object.keys(data).forEach(function (k) {
                    if (data[k] && data[k].uid === user.uid) { count++; }
                });
                return count;
            };

            metadataMgr.onChange(function () {
                var newdata = metadataMgr.getMetadata().users;
                var netfluxIds = Object.keys(newdata);
                // Notify for disconnected users
                if (typeof oldUserData !== "undefined") {
                    for (var u in oldUserData) {
                        // if a user's uid is still present after having left, don't notify
                        if (netfluxIds.indexOf(u) === -1) {
                            var temp = JSON.parse(JSON.stringify(oldUserData[u]));
                            delete oldUserData[u];
                            if (temp && newdata[userNetfluxId] && temp.uid === newdata[userNetfluxId].uid) { return; }
                            if (userPresent(u, temp, newdata || oldUserData) < 1) {
                                notify(-1, temp.name);
                            }
                        }
                    }
                }
                // Update the "oldUserData" object and notify for new users and names changed
                if (typeof newdata === "undefined") { return; }
                if (typeof oldUserData === "undefined") {
                    oldUserData = JSON.parse(JSON.stringify(newdata));
                    return;
                }
                if (config.readOnly === 0 && !oldUserData[userNetfluxId]) {
                    oldUserData = JSON.parse(JSON.stringify(newdata));
                    return;
                }
                for (var k in newdata) {
                    if (k !== userNetfluxId && netfluxIds.indexOf(k) !== -1) {
                        if (typeof oldUserData[k] === "undefined") {
                            // if the same uid is already present in the userdata, don't notify
                            if (!userPresent(k, newdata[k], oldUserData)) {
                                notify(1, newdata[k].name);
                            }
                        } else if (oldUserData[k].name !== newdata[k].name) {
                            notify(0, newdata[k].name, oldUserData[k].name);
                        }
                    }
                }
                oldUserData = JSON.parse(JSON.stringify(newdata));
            });
        }
    };



    // Main

    Bar.create = function (cfg) {
        var config = cfg || {};
        Cryptpad = config.common;
        Common = config.sfCommon;
        Messages = Cryptpad.Messages;
        config.readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
        config.displayed = config.displayed || [];

        var toolbar = {};

        toolbar.connected = false;
        toolbar.firstConnection = true;

        var $toolbar = toolbar.$toolbar = createRealtimeToolbar(config);
        toolbar.$leftside = $toolbar.find('.'+Bar.constants.leftside);
        toolbar.$rightside = $toolbar.find('.'+Bar.constants.rightside);
        toolbar.$drawer = $toolbar.find('.'+Bar.constants.drawer);
        toolbar.$top = $toolbar.find('.'+Bar.constants.top);
        toolbar.$history = $toolbar.find('.'+Bar.constants.history);

        toolbar.$userAdmin = $toolbar.find('.'+Bar.constants.userAdmin);

        // Create the subelements
        var tb = {};
        tb['userlist'] = createUserList;
        tb['share'] = createShare;
        tb['fileshare'] = createFileShare;
        tb['title'] = createTitle;
        tb['pageTitle'] = createPageTitle;
        tb['lag'] = $.noop;
        tb['spinner'] = createSpinner;
        tb['state'] = $.noop;
        tb['limit'] = createLimit; // TODO
        tb['upgrade'] = $.noop;
        tb['newpad'] = createNewPad;
        tb['useradmin'] = createUserAdmin;

        var addElement = toolbar.addElement = function (arr, additionnalCfg, init) {
            if (typeof additionnalCfg === "object") { $.extend(true, config, additionnalCfg); }
            arr.forEach(function (el) {
                if (typeof el !== "string" || !el.trim()) { return; }
                if (typeof tb[el] === "function") {
                    if (!init && config.displayed.indexOf(el) !== -1) { return; } // Already done
                    toolbar[el] = tb[el](toolbar, config);
                    if (!init) { config.displayed.push(el); }
                }
            });
        };

        addElement(config.displayed, {}, true);

        toolbar['linkToMain'] = createLinkToMain(toolbar, config);

        if (!config.realtime) { toolbar.connected = true; }

        initClickEvents(toolbar, config);
        initNotifications(toolbar, config);

        var failed = toolbar.failed = function () {
            toolbar.connected = false;

            if (toolbar.spinner) {
                toolbar.spinner.text(Messages.disconnected);
            }
            //checkLag(toolbar, config);
        };
        toolbar.reconnecting = function (/*userId*/) {
            //if (config.metadataMgr) { config.userList.userNetfluxId = userId; } TODO
            toolbar.connected = false;
            if (toolbar.spinner) {
                toolbar.spinner.text(Messages.reconnecting);
            }
            //checkLag(toolbar, config);
        };

        // On log out, remove permanently the realtime elements of the toolbar
        Cryptpad.onLogout(function () {
            failed();
            if (toolbar.useradmin) { toolbar.useradmin.hide(); }
            if (toolbar.userlist) { toolbar.userlist.hide(); }
        });

        return toolbar;
    };

    return Bar;
});