Merge branch 'documents-toolbar' into staging

This commit is contained in:
yflory 2024-03-25 17:18:54 +01:00
commit fcdce45083
17 changed files with 503 additions and 311 deletions

View File

@ -1038,6 +1038,13 @@
align-items: center;
}
}
.cp-dropdown-container {
button {
.cp-icon {
margin-left: 5px;
}
}
}
}
.cp-app-drive-button {

View File

@ -48,6 +48,16 @@
position: relative;
display: inline-block;
// If a menu is displayed "left", pour the submenus on the left too
.cp-dropdown-left {
.cp-dropdown-submenu {
.cp-dropdown-content {
left: unset;
right: 100%;
}
}
}
.fa {
font-family: FontAwesome;
}
@ -61,6 +71,16 @@
}
}
.cp-dropdown-left {
.cp-dropdown-content {
right: 0;
margin-right: 5px;
&.cp-dropdown-submenu {
left: unset;
right: 250px;
}
}
}
.cp-dropdown-content {
list-style-type: none;
display: none;
@ -76,9 +96,8 @@
font: @dropdown_font;
line-height: 1em;
&.cp-dropdown-left {
right: 0;
margin-right: 5px;
&.cp-dropdown-submenu {
left: 250px;
}
&:hover {
@ -140,9 +159,13 @@
}
li[role="menuitem"] {
border-radius: @variables_radius;
&:hover, &:focus {
white-space: nowrap;
&:hover {
background-color: @cp_dropdown-bg-hover !important;
color: @cp_dropdown-fg;
//color: @cp_dropdown-fg;
}
&:focus-visible {
outline-color: @cp_dropdown-fg;
}
}
&> span {

View File

@ -366,7 +366,7 @@
.cp-toolbar {
* {
outline-style: none;
&:focus {
&:focus-visible {
outline: @cryptpad_color_brand solid 2px;
}
}
@ -523,11 +523,6 @@
&::before {
padding-top: 1px;
}
// .cp-dropdown-button-title {
// transform: scale(0.5);
// bottom: -5px;
// right: -5px;
// }
}
}
.cp-toolbar-user-dropdown {
@ -960,6 +955,11 @@
&:empty {
height: 0;
}
& > div:empty {
display: none;
}
.cp-toolbar-users {
pre {
/* needed for ckeditor */
@ -1053,7 +1053,7 @@
&.cp-toolbar-small {
button {
.cp-toolbar-name, .cp-button-name {
.cp-toolbar-name, .cp-button-name, .cp-dropdown-button-title {
display: none;
}
i, span {
@ -1127,27 +1127,29 @@
order: 8;
}
.cp-toolbar-file {
button {
&.fa-plus { order: 0; }
&.fa-history { order: 5; }
&.fa-hashtag { order: 10; }
&.fa-bookmark { order: 15; }
&.fa-upload { order: 20; }
&.fa-files-o { order: 25; }
&.fa-download { order: 30; }
&.fa-print { order: 35; }
&.fa-trash { order: 40; }
&.cp-toolbar-icon-pad-settings { order: 75; }
&.fa-info-circle { order: 100; }
li, button {
.fa-plus { order: 0; }
.fa-history { order: 5; }
.fa-hashtag { order: 10; }
.fa-bookmark { order: 15; }
.fa-upload { order: 20; }
.fa-files-o { order: 25; }
.fa-download { order: 30; }
.fa-print { order: 35; }
.fa-trash { order: 40; }
.cp-toolbar-icon-pad-settings { order: 75; }
.fa-info-circle { order: 100; }
&.cp-toolbar-icon-help { order: 150; }
.fa-question { order: 150; }
span { order: 1000; } // text always after icon
}
}
.cp-toolbar-drawer-content:empty ~ .cp-toolbar-drawer-button {
.cp-toolbar-drawer-contentsss:empty ~ .cp-toolbar-drawer-button {
display: none;
}
.cp-toolbar-drawer-content {
.cp-toolbar-drawer-contentsss {
box-shadow: 0px 1px 5px 0px @cp_shadow-color;
border-radius: @variables_radius;
overflow-y: auto;

View File

@ -18,6 +18,7 @@ define([
'/common/TypingTests.js',
'/customize/messages.js',
'cm/lib/codemirror',
'/common/common-ui-elements.js',
'css!cm/lib/codemirror.css',
@ -64,7 +65,8 @@ define([
Visible,
TypingTest,
Messages,
CMeditor)
CMeditor,
UIElements)
{
window.CodeMirror = CMeditor;
@ -82,19 +84,15 @@ define([
]);
var mkThemeButton = function (framework) {
var $theme = $(h('button.cp-toolbar-appmenu', [
h('i.cptools.cptools-palette'),
h('span.cp-button-name', Messages.toolbar_theme)
]));
var $content = $(h('div.cp-toolbar-drawer-content', {
tabindex: 1
})).hide();
// set up all the necessary events
UI.createDrawer($theme, $content);
framework._.toolbar.$theme = $content;
framework._.toolbar.$bottomL.append($theme);
const $drawer = UIElements.createDropdown({
text: Messages.toolbar_theme,
options: [],
common: framework._.sfCommon,
iconCls: 'cptools cptools-palette'
});
framework._.toolbar.$theme = $drawer.find('ul.cp-dropdown-content');
framework._.toolbar.$bottomL.append($drawer);
$drawer.addClass('cp-toolbar-appmenu');
};
var mkCbaButton = function (framework, markers) {
@ -103,10 +101,12 @@ define([
name: 'authormarks',
icon: 'fa-paint-brush',
}).hide();
framework._.toolbar.$theme.append($showAuthorColorsButton);
markers.setButton($showAuthorColorsButton);
var $showAuthorColors = UIElements.getEntryFromButton($showAuthorColorsButton).hide();
$showAuthorColors.find('span').addClass('cp-toolbar-name cp-toolbar-drawer-element');
framework._.toolbar.$theme.append($showAuthorColors);
markers.setButton($showAuthorColors);
};
var mkPrintButton = function (framework, $content, $print) {
var mkPrintButton = function (framework, $content) {
var $printButton = framework._.sfCommon.createButton('print', true);
$printButton.click(function () {
$print.html($content.html());
@ -114,7 +114,8 @@ define([
window.print();
framework.feedback('PRINT_CODE');
});
framework._.toolbar.$drawer.append($printButton);
var $print = UIElements.getEntryFromButton($printButton);
framework._.toolbar.$drawer.append($print);
};
var mkMarkdownTb = function (editor, framework) {
var $codeMirrorContainer = $('#cp-app-code-container');
@ -137,7 +138,8 @@ define([
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'code']);
$codeMirrorContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
framework._.toolbar.$drawer.append($helpMenuButton);
};
var previews = {};
@ -393,7 +395,8 @@ define([
setButton(!markers.getState());
UI.alert(content);
});
framework._.toolbar.$theme.append($cbaButton);
var $cba = UIElements.getEntryFromButton($cbaButton);
framework._.toolbar.$theme.prepend($cba);
};
var mkFilePicker = function (framework, editor, evModeChange) {

View File

@ -25,7 +25,7 @@ define([
var debug = function () {};
var MARK_OPACITY = 0.5;
var MARK_OPACITY = 0.2;
var DEFAULT = {
authors: {},
marks: [[-1, 0, 0, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]]
@ -624,11 +624,9 @@ define([
if (Env.opacity) {
Env.opacity = 0;
$button.find('.cp-toolbar-drawer-element').text(Messages.cba_show);
$button.removeClass("cp-toolbar-button-active");
} else {
Env.opacity = MARK_OPACITY;
$button.find('.cp-toolbar-drawer-element').text(Messages.cba_hide);
$button.addClass("cp-toolbar-button-active");
}
};
toggle();

View File

@ -1466,44 +1466,6 @@ define([
};
};
/* Given two jquery objects (a 'button' and a 'drawer')
add handlers to make it such that clicking the button
displays the drawer contents, and blurring the button
hides the drawer content. Used for toolbar buttons at the moment.
*/
UI.createDrawer = function ($button, $content) {
$button.click(function () {
var topPos = $button[0].getBoundingClientRect().bottom;
$content.toggle();
$button.removeClass('cp-toolbar-button-active');
if ($content.is(':visible')) {
$button.addClass('cp-toolbar-button-active');
$content.focus();
var wh = $(window).height();
$content.css('max-height', Math.floor(wh - topPos - 1)+'px');
}
});
var onBlur = function (e) {
if (e.relatedTarget) {
var $relatedTarget = $(e.relatedTarget);
if ($relatedTarget.is('.cp-toolbar-drawer-button')) { return; }
if ($relatedTarget.parents('.cp-toolbar-drawer-content').length) {
$relatedTarget.blur(onBlur);
return;
}
}
$button.removeClass('cp-toolbar-button-active');
$content.hide();
};
$content.blur(onBlur).appendTo($button);
$('body').keydown(function (e) {
if (e.which === 27) {
$content.blur();
}
});
};
/* QR code generation is synchronous once the library is loaded
so this could be syncronous if we load the library separately. */
UI.createQRCode = function (data, _cb) {

View File

@ -577,12 +577,38 @@ define([
});
};
UIElements.getEntryFromButton = function ($button) {
if (!$button || !$button.length) { return; }
let $icon = $button.find('> i');
let attributes = {};
let btnClass = $button.attr('class');
let btnId = $button.attr('id');
if (btnClass) { attributes['class'] = btnClass; }
if (btnId) { attributes['id'] = btnId; }
return UIElements.createDropdownEntry({
tag: 'a',
attributes: attributes,
content: [
h('i',{ 'class': $icon.attr('class') }),
h('span', $button.text())
],
action: function () {
$button.click();
return true;
}
});
};
UIElements.createButton = function (common, type, rightside, data, callback) {
var AppConfig = common.getAppConfig();
var button;
var sframeChan = common.getSframeChannel();
var appType = (common.getMetadataMgr().getMetadata().type || 'pad').toUpperCase();
data = data || {};
if (!callback && data.callback) { callback = data.callback; }
switch (type) {
case 'export':
button = $('<button>', {
@ -858,12 +884,12 @@ define([
h('i.fa.fa-file-image-o'),
h('span.cp-toolbar-name.cp-toolbar-drawer-element', Messages.toolbar_savetodrive)
])).click(common.prepareFeedback(type));
if (callback) { button.click(callback); }
break;
case 'storeindrive':
button = $(h('button.cp-toolbar-storeindrive', {
button = $(h('button.cp-toolbar-storeindrive.fa.fa-hdd-o', {
style: 'display:none;'
}, [
h('i.fa.fa-hdd-o'),
h('span.cp-toolbar-name.cp-toolbar-drawer-element', Messages.toolbar_storeInDrive)
])).click(common.prepareFeedback(type)).click(function () {
$(button).hide();
@ -994,7 +1020,7 @@ define([
h('span.cp-toolbar-name'+drawerCls, data.text)
]));
var feedbackHandler = common.prepareFeedback(data.name || 'DEFAULT');
button[0].addEventListener('click', function () {
Util.onClickEnter(button, function () {
feedbackHandler();
if (typeof(callback) !== 'function') { return; }
callback();
@ -1454,6 +1480,94 @@ define([
};
};
UIElements.createDropdownEntry = function (config) {
var hide = function () {};
var allowedTags = ['a', 'li', 'p', 'hr', 'div'];
var isElement = function (o) {
return /HTML/.test(Object.prototype.toString.call(o)) &&
typeof(o.tagName) === 'string';
};
var isValidOption = function (o) {
if (typeof o !== "object") { return false; }
if (isElement(o)) { return true; }
if (!o.tag || allowedTags.indexOf(o.tag) === -1) { return false; }
return true;
};
var entry;
if (!isValidOption(config)) { return; }
if (isElement(config)) {
entry = $(config);
} else {
var $el = $(h(config.tag, (config.attributes || {})));
if (typeof(config.content) === 'string' || (config.content instanceof Element)) {
config.content = [config.content];
}
if (Array.isArray(config.content)) {
config.content.forEach(function (item) {
if (item instanceof Element) {
return void $el.append(item);
}
if (typeof(item) === 'string') {
$el[0].appendChild(document.createTextNode(item));
}
});
}
// Everything is added as an "li" tag
// Links and items with action are focusable
// Add correct "role" attribute
entry = $(h('li'));
if (config.tag === 'a') {
$el.attr('tabindex', '-1');
entry.attr('role', 'menuitem');
entry.attr('tabindex', '0');
} else if (config.tag === 'li') {
entry = $el;
entry.attr('role', 'menuitem');
entry.attr('tabindex', '0');
} else if (config.tag === 'hr') {
entry.attr('role', 'separator');
} else {
entry.attr('role', 'none');
}
entry.append($el);
// Action can be triggered with a click or keyboard event
if (config.tag === 'a' || config.tag === 'li') {
entry.on('mouseenter', (e) => {
e.stopPropagation();
entry.focus();
});
Util.onClickEnter(entry, function(e) {
if (config.isSelect) { return; }
e.stopPropagation();
if (typeof(config.action) === "function") {
var close = config.action(e);
if (close) { hide(); }
} else {
// Click on <a> with an href
if (e.type === 'keydown'){ $el.get(0).click(); }
}
}, {space: true});
}
}
hide = function () {
window.setTimeout(function () {
entry.closest('.cp-dropdown-menu-container').find('.cp-dropdown-submenu').hide();
entry.parents('.cp-dropdown-content').hide();
}, 0);
};
return entry;
};
// Create a button with a dropdown menu
// input is a config object with parameters:
// - container (optional): the dropdown container (span)
@ -1465,18 +1579,6 @@ define([
if (typeof config !== "object" || !Array.isArray(config.options)) { return; }
if (config.feedback && !config.common) { return void console.error("feedback in a dropdown requires sframe-common"); }
var isElement = function (o) {
return /HTML/.test(Object.prototype.toString.call(o)) &&
typeof(o.tagName) === 'string';
};
var allowedTags = ['a', 'li', 'p', 'hr', 'div'];
var isValidOption = function (o) {
if (typeof o !== "object") { return false; }
if (isElement(o)) { return true; }
if (!o.tag || allowedTags.indexOf(o.tag) === -1) { return false; }
return true;
};
// Container
var $container = $(config.container);
var containerConfig = {
@ -1490,47 +1592,45 @@ define([
$container = $('<span>', containerConfig);
}
// Button
var $button;
if (config.buttonContent) {
$button = $(h('button', {
class: config.buttonCls || '',
'aria-haspopup': 'menu',
'aria-expanded': 'false',
'title': config.buttonTitle || '',
'aria-label': config.buttonTitle || '',
}, [
h('span.cp-dropdown-button-title', config.buttonContent),
]));
} else {
$button = $('<button>', {
'class': config.buttonCls || '',
'aria-haspopup': 'menu',
'aria-expanded': 'false',
'title': config.buttonTitle || '',
'aria-label': config.buttonTitle || '',
}).append($('<span>', {'class': 'cp-dropdown-button-title'}).text(config.text || ""));
}
let icon = config.iconCls ? h('i', {class:config.iconCls}) : undefined;
var $button = $(h('button', {
class: config.buttonCls || '',
'aria-haspopup': 'menu',
'aria-expanded': 'false',
'title': config.buttonTitle || '',
'aria-label': config.buttonTitle || '',
}, config.buttonContent || [
icon,
h('span.cp-dropdown-button-title', config.text),
]));
if (config.caretDown) {
$('<span>', {
'class': 'fa fa-caret-down',
}).prependTo($button);
$button.append(h('i.fa.fa-caret-down'));
}
if (config.angleDown) {
$('<span>', {
'class': 'fa fa-angle-down',
}).prependTo($button);
$button.append(h('i.fa.fa-angle-down'));
}
// Menu
var $innerblock = $('<ul>', {'class': 'cp-dropdown-content', 'role': 'menu'});
if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
var $innerblock = $('<ul>', {
'class': 'cp-dropdown-content',
'role': 'menu',
'tabindex': '-1'
});
var $outerblock = $(h('div.cp-dropdown-menu-container', $innerblock[0]));
let $parentMenu = config.isSubmenuOf;
$container.$menu = $innerblock;
if (config.left) { $outerblock.addClass('cp-dropdown-left'); }
if (config.isSubmenuOf && config.isSubmenuOf.length) {
$innerblock.addClass('cp-dropdown-submenu');
}
var hide = function () {
window.setTimeout(function () { $innerblock.hide(); }, 0);
window.setTimeout(function () {
$innerblock.hide();
}, 0);
};
// When the menu is collapsed, update aria-expanded
@ -1539,81 +1639,35 @@ define([
if (mutation.attributeName !== 'style') { return; }
if ($innerblock[0].style.display === 'none') {
$button.attr('aria-expanded', 'false');
if (config.$parentButton) {
config.$parentButton.find('a')
.toggleClass('cp-dropdown-element-active', false);
}
}
});
});
observer.observe($innerblock[0], { attributes: true });
// Add the dropdown content
var setOptions = function (options) {
var setOptions = $container.setOptions = function (options) {
$innerblock.empty();
options.forEach(function (o) {
if (!isValidOption(o)) { return; }
if (isElement(o)) { return void $innerblock.append(o); }
var $el = $(h(o.tag, (o.attributes || {})));
if (typeof(o.content) === 'string' || (o.content instanceof Element)) {
o.content = [o.content];
}
if (Array.isArray(o.content)) {
o.content.forEach(function (item) {
if (item instanceof Element) {
return void $el.append(item);
}
if (typeof(item) === 'string') {
$el[0].appendChild(document.createTextNode(item));
}
});
}
// Everything is added as an "li" tag
// Links and items with action are focusable
// Add correct "role" attribute
var $li = $(h('li'));
if (o.tag === 'a') {
$el.attr('tabindex', '-1');
$li.attr('role', 'menuitem');
$li.attr('tabindex', '0');
} else if (o.tag === 'li') {
$li = $el;
$li.attr('role', 'menuitem');
$li.attr('tabindex', '0');
} else if (o.tag === 'hr') {
$li.attr('role', 'separator');
} else {
$li.attr('role', 'none');
}
$li.append($el);
$li.appendTo($innerblock);
// Action can be triggered with a click or keyboard event
if (o.tag !== 'a' && o.tag !== 'li') { return; }
$li.on('mouseenter', (e) => {
e.stopPropagation();
$li.focus();
});
var onAction = function (e) {
if (config.isSelect) { return; }
if (e.type === 'click' || (e.type === 'keydown' && e.keyCode === 13) || (e.type === 'keydown' && e.keyCode === 32)) {
e.stopPropagation();
if (typeof(o.action) === "function") {
var close = o.action(e);
if (close) { hide(); }
} else {
// Click on <a> with an href
if (e.type === 'keydown'){ $el.get(0).click(); }
}
}
};
$li.on('click keydown', onAction);
if (!o) { return; }
o.isSelect = config.isSelect;
let $entry = UIElements.createDropdownEntry(o);
if (!$entry) { return void console.error('Error adding dropdown entry', o); }
$entry.appendTo($innerblock);
});
};
setOptions(config.options);
$container.setOptions = function (options) {
$innerblock.empty();
setOptions(options);
$container.addOption = function (config) {
let $entry = UIElements.createDropdownEntry(config);
$entry.appendTo($innerblock);
};
$container.append($button).append($innerblock);
$container.append($button).append($outerblock);
if ($parentMenu) { $parentMenu.after($innerblock); }
var value = config.initialValue || '';
@ -1640,15 +1694,40 @@ define([
var topPos = button.bottom;
$button.attr('aria-expanded', 'true');
$innerblock.css('bottom', '');
$innerblock.show();
if ($parentMenu) {
// keep parent open when recursive
$parentMenu.show();
if (config.$parentButton) {
config.$parentButton.find('a')
.toggleClass('cp-dropdown-element-active', true);
}
}
if (config.noscroll) {
var h = $innerblock.outerHeight();
if ((topPos + h) > wh) {
$innerblock.css('bottom', button.height+'px');
}
} else if ($parentMenu) {
let max = $parentMenu.css('max-height');
let css = {
'max-height': max
};
if (config.$parentButton) {
let pos = config.$parentButton.position();
let diff = pos.top;
css['margin-top'] = diff+'px';
css['max-height'] = (parseFloat(max)-diff)+'px';
}
$innerblock.css(css);
if ($innerblock.height() < 50) {
$innerblock.css('max-height', max);
$innerblock.css('margin-top', '');
}
} else {
$innerblock.css('max-height', Math.floor(wh - topPos - 1)+'px');
}
$innerblock.show();
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
setTimeout(() => {
if (config.isSelect && value) {
@ -1671,12 +1750,6 @@ define([
var state = $innerblock.is(':visible');
$('.cp-dropdown-content').hide();
var $c = $container.closest('.cp-toolbar-drawer-content');
$c.removeClass('cp-dropdown-visible');
if (!state) {
$c.addClass('cp-dropdown-visible');
}
try {
$('iframe').each(function (idx, ifrw) {
$(ifrw).contents().find('.cp-dropdown-content').hide();
@ -1693,7 +1766,7 @@ define([
if (config.isSelect) {
$container.onChange = Util.mkEvent();
$container.on('click', 'li', function () {
$innerblock.on('click', 'li', function () {
var $val = $(this).find('a');
value = $val.data('value');
var textValue = $val.text() || value;
@ -1756,7 +1829,10 @@ define([
var next = (idx + 1) % $all.length;
return $($all.get(next));
};
$container.keydown(function (e) {
let $listener = $container;
if ($parentMenu) { $listener = $innerblock; }
$listener.keydown(function (e) {
e.stopPropagation(); // don't propagate event to window if the dropdown is focused
var visible = $innerblock.is(':visible');
@ -1765,6 +1841,7 @@ define([
$container.click();
visible = true;
}
if (!visible) { return; }
if (e.which === 38) { // Up
e.preventDefault();
@ -1807,19 +1884,34 @@ define([
if (e.which === 27) { // Esc
e.preventDefault();
$value.mouseleave();
if ($container.find('.cp-dropdown-submenu:visible').length) {
let $submenu = $container.find('.cp-dropdown-submenu:visible');
if ($submenu[0] !== $innerblock[0]) {
$submenu.hide();
return;
}
}
hide();
$button.focus();
if ($parentMenu) { $button.closest('li').focus(); }
else { $button.focus(); }
}
if (e.which === 9) { // Tab
hide();
if (e.shiftKey) {
$button.focus();
hide();
if ($parentMenu) { $button.closest('li').focus(); }
else { $button.focus(); }
} else {
// Hide parent only if we're not going to focus visible submenu
if ($parentMenu ||
!$container.find('.cp-dropdown-submenu:visible').length) {
hide();
}
if ($parentMenu) { $parentMenu.hide(); }
$innerblock.find('[role="menuitem"]').last().focus();
}
}
});
$container.keypress(function (e) {
$listener.keypress(function (e) {
e.stopPropagation(); // Don't propagate to window
window.clearTimeout(to);
var c = String.fromCharCode(e.which);
@ -1840,6 +1932,8 @@ define([
}, 1000);
});
$container.close = hide;
return $container;
};
@ -2237,7 +2331,7 @@ define([
};
});
var dropdownConfigUser = {
buttonContent: $userButton[0],
text: $userButton[0],
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
container: config.$initBlock, // optional

View File

@ -117,7 +117,7 @@
// "enter" on a button triggers a click, disable it
if (e.type === 'keydown') { e.preventDefault(); }
handler();
handler(e);
});
};

View File

@ -2724,12 +2724,14 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
});
};
common.createButton('', true, {
var $historyButton = common.createButton('', true, {
name: 'history',
icon: 'fa-history',
text: Messages.historyText,
tippy: Messages.historyButton
}).click(function () {
});
$historyButton.click(function () {
ooChannel.historyLastHash = ooChannel.lastHash;
ooChannel.currentIndex = ooChannel.cpIndex;
Feedback.send('OO_HISTORY');
@ -2747,19 +2749,24 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
$toolbar: $('.cp-toolbar-container')
};
History.create(common, histConfig);
}).appendTo(toolbar.$drawer);
});
var $historyDropdown = UIElements.getEntryFromButton($historyButton);
$historyDropdown.appendTo(toolbar.$drawer);
// Snapshots
var $snapshot = common.createButton('snapshots', true, {
var $snapshotButton = common.createButton('snapshots', true, {
remove: deleteSnapshot,
make: makeSnapshot,
load: loadSnapshot
});
var $snapshot = UIElements.getEntryFromButton($snapshotButton);
toolbar.$drawer.append($snapshot);
// Import template
var $template = common.createButton('importtemplate', true, {}, openTemplatePicker);
if ($template && typeof($template.appendTo) === 'function') {
var $importTemplateButton = common.createButton('importtemplate', true, {}, openTemplatePicker);
if ($importTemplateButton && $importTemplateButton.length) {
let $template = UIElements.getEntryFromButton($importTemplateButton);
$template.appendTo(toolbar.$drawer);
}
@ -2782,8 +2789,9 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
APP.FM.handleFile(blob, data);
}
};
var $templateButton = common.createButton('template', true, templateObj);
toolbar.$drawer.append($templateButton);
let $templateButton = common.createButton('template', true, templateObj);
let $template = UIElements.getEntryFromButton($templateButton);
toolbar.$drawer.append($template);
}
})();
}
@ -2807,7 +2815,8 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
}).attr('title', 'Restore last checkpoint').appendTo(toolbar.$bottomM);
}
var $exportXLSX = common.createButton('export', true, {}, exportXLSXFile);
var $exportXLSXButton = common.createButton('export', true, {}, exportXLSXFile);
var $exportXLSX = UIElements.getEntryFromButton($exportXLSXButton);
$exportXLSX.appendTo(toolbar.$drawer);
var type = privateData.ooType;
@ -2835,35 +2844,44 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
if (common.isLoggedIn()) {
window.CryptPad_deleteLastCp = deleteLastCp;
var $importXLSX = common.createButton('import', true, {
var $importXLSXButton = common.createButton('import', true, {
accept: accept,
binary : ["ods", "xlsx", "odt", "docx", "odp", "pptx"],
binary: ["ods", "xlsx", "odt", "docx", "odp", "pptx"],
first: first,
}, importXLSXFile);
var $importXLSX = UIElements.getEntryFromButton($importXLSXButton);
// tag button
var $hashtagButton = common.createButton('hashtag', true);
var $hashtag = UIElements.getEntryFromButton($hashtagButton);
$importXLSX.appendTo(toolbar.$drawer);
common.createButton('hashtag', true).appendTo(toolbar.$drawer);
$hashtag.appendTo(toolbar.$drawer);
}
var $store = common.createButton('storeindrive', true);
toolbar.$drawer.append($store);
var $forget = common.createButton('forget', true, {}, function (err) {
// Move to trash button
var $forgetButton = common.createButton('forget', true, {}, function (err) {
if (err) { return; }
setEditable(false);
});
var $forget = UIElements.getEntryFromButton($forgetButton);
toolbar.$drawer.append($forget);
if (!privateData.isEmbed) {
var helpMenu = APP.helpMenu = common.createHelpMenu(['beta', 'oo']);
$('#cp-app-oo-editor').prepend(common.getBurnAfterReadingWarning());
$('#cp-app-oo-editor').prepend(helpMenu.menu);
toolbar.$drawer.append(helpMenu.button);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
toolbar.$drawer.append($helpMenuButton);
}
var $properties = common.createButton('properties', true);
var $propertiesButton = common.createButton('properties', true);
var $properties = UIElements.getEntryFromButton($propertiesButton);
toolbar.$drawer.append($properties);
var $copy = common.createButton('copy', true);
var $copyButton = common.createButton('copy', true);
var $copy = UIElements.getEntryFromButton($copyButton);
toolbar.$drawer.append($copy);
};

View File

@ -710,7 +710,7 @@ define([
var setFileExporter = function (extension, fe, async) {
fileExporter = fe;
var $export = common.createButton('export', true, {}, function () {
var $exportButton = common.createButton('export', true, {}, function () {
var ext = (typeof(extension) === 'function') ? extension() : extension;
var suggestion = title.suggestTitle('cryptpad-document');
ext = ext || '.txt';
@ -774,6 +774,7 @@ define([
});
$select.find('button').addClass('btn');
});
var $export = UIElements.getEntryFromButton($exportButton);
toolbar.$drawer.append($export);
};
@ -803,9 +804,9 @@ define([
onLocal();
});
};
toolbar.$drawer.append(
common.createButton('import', true, options, fileImporter)
);
var $importButton = common.createButton('import', true, options, fileImporter);
var $import = UIElements.getEntryFromButton($importButton);
toolbar.$drawer.append($import);
};
var feedback = function (action, force) {
@ -998,21 +999,26 @@ define([
},
$toolbar: $(toolbarContainer)
};
var $hist = common.createButton('history', true, {histConfig: histConfig});
var $histButton = common.createButton('history', true, {histConfig: histConfig});
var $hist = UIElements.getEntryFromButton($histButton);
toolbar.$drawer.append($hist);
var $snapshot = common.createButton('snapshots', true, {
var $snapshotButton = common.createButton('snapshots', true, {
remove: deleteSnapshot,
make: makeSnapshot,
load: loadSnapshot
});
var $snapshot = UIElements.getEntryFromButton($snapshotButton);
toolbar.$drawer.append($snapshot);
var $copy = common.createButton('copy', true);
var $copyButton = common.createButton('copy', true);
var $copy = UIElements.getEntryFromButton($copyButton);
toolbar.$drawer.append($copy);
var $store = common.createButton('storeindrive', true);
toolbar.$drawer.append($store);
var $storeButton = UIElements.getEntryFromButton($store);
toolbar.$drawer.append($storeButton);
if (!cpNfInner.metadataMgr.getPrivateData().isTemplate) {
var templateObj = {
@ -1020,26 +1026,34 @@ define([
getTitle: function () { return cpNfInner.metadataMgr.getMetadata().title; }
};
var $templateButton = common.createButton('template', true, templateObj);
toolbar.$drawer.append($templateButton);
var $template = UIElements.getEntryFromButton($templateButton);
toolbar.$drawer.append($template);
}
var $importTemplateButton = common.createButton('importtemplate', true);
if (!readOnly) {
toolbar.$drawer.append($importTemplateButton);
var $importTemplate = UIElements.getEntryFromButton($importTemplateButton);
toolbar.$drawer.append($importTemplate);
}
/* add a forget button */
toolbar.$drawer.append(common.createButton('forget', true, {}, function (err) {
/* add a forget button = trash button */
var $forgetButton = common.createButton('forget', true, {}, function (err) {
if (err) { return; }
stateChange(STATE.FORGOTTEN);
}));
});
var $forget = UIElements.getEntryFromButton($forgetButton);
toolbar.$drawer.append($forget);
if (common.isLoggedIn()) {
var $tags = common.createButton('hashtag', true);
var $tagsButton = common.createButton('hashtag', true);
var $tags = UIElements.getEntryFromButton($tagsButton);
toolbar.$drawer.append($tags);
}
var $properties = common.createButton('properties', true);
var $propertiesButton = common.createButton('properties', true);
var $properties = UIElements.getEntryFromButton($propertiesButton);
toolbar.$drawer.append($properties);
createFilePicker();

View File

@ -13,7 +13,8 @@ define([
'/common/common-util.js',
'/common/text-cursor.js',
'/components/chainpad/chainpad.dist.js',
], function ($, Modes, Themes, Messages, UIElements, MT, Hash, Util, TextCursor, ChainPad) {
'/common/hyperscript.js',
], function ($, Modes, Themes, Messages, UIElements, MT, Hash, Util, TextCursor, ChainPad, h) {
var module = {};
var cursorToPos = module.cursorToPos = function(cursor, oldText) {
@ -274,6 +275,7 @@ define([
module.handleImagePaste(editor);
var setMode = exp.setMode = function (mode, cb) {
if (!mode) { return; }
exp.highlightMode = mode;
if (mode === 'markdown') { mode = 'gfm'; }
if (/text\/x/.test(mode)) {
@ -293,9 +295,11 @@ define([
editor.setOption('mode', mode);
}
if (exp.$language) {
var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined;
var name = exp.$language.$menu.find('a[data-value="' + mode + '"]').text() || undefined;
name = name ? Messages.languageButton + ' ('+name+')' : Messages.languageButton;
exp.$language.setValue(mode, name);
exp.$language.find('span.cp-language-text').text(name);
exp.$language.find('span.cp-language-text').prepend('<i class="fa fa-chevron-right"></i>');
}
if (mode === "orgmode") {
@ -340,6 +344,8 @@ define([
var name = theme || undefined;
name = name ? Messages.themeButton + ' ('+theme+')' : Messages.themeButton;
$select.setValue(theme, name);
$select.find('span.cp-theme-text').text(name);
$select.find('span.cp-theme-text').prepend('<i class="fa fa-chevron-right"></i>');
}
};
}());
@ -364,31 +370,45 @@ define([
text: Messages.languageButton, // Button initial text
options: options, // Entries displayed in the menu
isSelect: true,
isSubmenuOf: $drawer,
feedback: 'CODE_LANGUAGE',
common: Common
};
var $block = exp.$language = UIElements.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.languageButtonTitle);
$block.find('button').attr('title', Messages.languageButtonTitle).hide();
$block.prepend(h('span.cp-language-text', Messages.languageButton));
var isHovering = false;
var $aLanguages = $block.find('li');
var $aLanguages = $block.$menu.find('li');
$aLanguages.mouseenter(function () {
isHovering = true;
setMode($(this).find('a').attr('data-value'));
});
$aLanguages.mouseleave(function () {
if (isHovering) {
setMode($block.find(".cp-dropdown-element-active").attr('data-value'));
setMode($block.$menu.find(".cp-dropdown-element-active").attr('data-value'));
}
});
$aLanguages.click(function () {
//$aLanguages.click(function () {
$block.onChange.reg(() => {
isHovering = false;
var mode = $(this).find('a').attr('data-value');
var mode = $block.getValue();
setMode(mode, onModeChanged);
$block.close();
onLocal();
});
if ($drawer) { $drawer.append($block); }
if ($drawer) {
var $blockButton = UIElements.createDropdownEntry({
tag: 'a',
content: $block[0],
action: function () {
$block.find('button').click();
},
});
dropdownConfig.$parentButton = $blockButton;
$drawer.append($blockButton);
}
if (exp.highlightMode) { exp.setMode(exp.highlightMode); }
if (cb) { cb(); }
};
@ -416,24 +436,27 @@ define([
text: Messages.code_editorTheme, // Button initial text
options: options, // Entries displayed in the menu
isSelect: true,
isSubmenuOf: $drawer,
initialValue: lastTheme,
feedback: 'CODE_THEME',
common: Common
};
var $block = exp.$theme = UIElements.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.themeButtonTitle).click(function () {
/*$block.find('button').attr('title', Messages.themeButtonTitle).click(function () {
var state = $block.find('.cp-dropdown-content').is(':visible');
var $c = $block.closest('.cp-toolbar-drawer-content');
$c.removeClass('cp-dropdown-visible');
if (!state) {
$c.addClass('cp-dropdown-visible');
}
});
});*/
$block.find('button').hide();
$block.prepend(h('span.cp-theme-text', Messages.languageButton));
setTheme(lastTheme, $block);
var isHovering = false;
var $aThemes = $block.find('li');
var $aThemes = $block.$menu.find('li');
$aThemes.mouseenter(function () {
isHovering = true;
var theme = $(this).find('a').attr('data-value');
@ -445,14 +468,24 @@ define([
Common.setAttribute(themeKey, lastTheme);
}
});
$aThemes.click(function () {
$block.onChange.reg(() => {
isHovering = false;
var theme = $(this).find('a').attr('data-value');
var theme = $block.getValue();
setTheme(theme, $block);
Common.setAttribute(themeKey, theme);
});
if ($drawer) { $drawer.append($block); }
if ($drawer) {
const $blockButton = UIElements.createDropdownEntry({
tag: 'a',
content: $block[0],
action: function () {
$block.find('button').click();
},
});
dropdownConfig.$parentButton = $blockButton;
$drawer.append($blockButton);
}
if (cb) { cb(); }
};
Common.getAttribute(themeKey, todo);

View File

@ -64,7 +64,7 @@ MessengerUI, Messages, Pages, PadTypes) {
return 'cp-toolbar-uid-' + String(Math.random()).substring(2);
};
var observeChildren = function ($content) {
var observeChildren = function ($content, isDrawer) {
var reorderDOM = Util.throttle(function ($content, observer) {
if (!$content.length) { return; }
@ -73,7 +73,15 @@ MessengerUI, Messages, Pages, PadTypes) {
$content[0].childNodes.forEach((node) => {
try {
if (!node.attributes) { return; }
var order = getComputedStyle(node).getPropertyValue("order");
let nodeWithOrder;
if (isDrawer) { // HACK: the order is set on their inner "a" tag
let $n = $(node);
if (!$n.attr('class') &&
($n.find('.fa').length || $n.find('.cptools').length)) {
nodeWithOrder = $n.find('.fa')[0] || $n.find('.cptools')[0];
}
}
var order = getComputedStyle(nodeWithOrder || node).getPropertyValue("order");
var a = map[order] = map[order] || [];
a.push(node);
} catch (e) { console.error(e, node); }
@ -102,6 +110,7 @@ MessengerUI, Messages, Pages, PadTypes) {
});
});
observer.start = function () {
if (!$content.length) { return; }
observer.observe($content[0], {
childList: true
});
@ -146,13 +155,16 @@ MessengerUI, Messages, Pages, PadTypes) {
var $file = $toolbar.find('.'+BOTTOM_LEFT_CLS);
if (!config.hideDrawer) {
var $drawer = $(h('button.' + FILE_CLS, [
h('i.fa.fa-file-o'),
h('span.cp-button-name', Messages.toolbar_file)
])).appendTo($file).hide();
var $drawerContent = $(h('div.'+ DRAWER_CLS, {tabindex: 1})).hide();
UI.createDrawer($drawer, $drawerContent);
if (config.addFileMenu) {
var $drawer = UIElements.createDropdown({
text: Messages.toolbar_file,
options: [],
common: Common,
iconCls: 'fa fa-file-o'
}).hide();
$drawer.addClass(FILE_CLS).appendTo($file);
$drawer.find('.cp-dropdown-content').addClass(DRAWER_CLS);
$drawer.find('span').addClass('cp-button-name');
}
// The 'notitle' class removes the line added for the title with a small screen
@ -283,7 +295,7 @@ MessengerUI, Messages, Pages, PadTypes) {
var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = numberOfViewUsers === '' ? '' : '<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);
$userButtons.find('.cp-toolbar-userlist-button').html('').append($spansmall);
if (!online || toolbar.isDeleted) { return; }
@ -462,7 +474,7 @@ MessengerUI, Messages, Pages, PadTypes) {
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);
$('<span>',{'class': 'cp-toolbar-userlist-button'}).appendTo($button);
toolbar.$bottomR.prepend($container);
@ -998,8 +1010,9 @@ MessengerUI, Messages, Pages, PadTypes) {
var createNewPad = function (toolbar) {
var $button = Common.createButton('newpad', true);
toolbar.$drawer.append($button);
return $button;
var $newPad = UIElements.getEntryFromButton($button);
toolbar.$drawer.append($newPad);
return $newPad;
};
var createUserAdmin = function (toolbar, config) {
@ -1353,6 +1366,10 @@ MessengerUI, Messages, Pages, PadTypes) {
toolbar.connected = false;
toolbar.firstConnection = true;
if (Array.isArray(cfg.displayed) && cfg.displayed.includes('pad')) {
cfg.addFileMenu = true;
}
var $toolbar = toolbar.$toolbar = createRealtimeToolbar(config);
toolbar.$bottom = $toolbar.find('.'+Bar.constants.bottom);
toolbar.$bottomL = $toolbar.find('.'+Bar.constants.bottomL);
@ -1366,7 +1383,7 @@ MessengerUI, Messages, Pages, PadTypes) {
toolbar.$history = $toolbar.find('.'+Bar.constants.history);
toolbar.$user = $toolbar.find('.'+Bar.constants.userAdmin);
observeChildren(toolbar.$drawer);
observeChildren(toolbar.$drawer, true);
observeChildren(toolbar.$bottomL);
observeChildren(toolbar.$bottomM);
observeChildren(toolbar.$bottomR);

View File

@ -4645,8 +4645,9 @@ define([
var $toolbarContainer = $('#cp-toolbar');
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'pad']);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
$toolbarContainer.after(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
framework._.toolbar.$drawer.append($helpMenuButton);
if (!APP.isEditor && !priv.form_auditorKey) {
$(helpMenu.menu).hide();
}

View File

@ -1118,11 +1118,13 @@ define([
var $toolbarContainer = $('#cp-app-kanban-container');
var helpMenu = framework._.sfCommon.createHelpMenu(['kanban']);
$toolbarContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
$toolbarContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append($helpMenuButton);
};
// Start of the main loop
var andThen2 = function (framework) {

View File

@ -345,15 +345,19 @@ define([
}, function () {
UI.alert(getSettings());
});
framework._.toolbar.$drawer.append($settingsButton);
var $settings = UIElements.getEntryFromButton($settingsButton);
framework._.toolbar.$drawer.append($settings);
};
var mkHelpMenu = function(framework) {
var $toolbarContainer = $('.cke_toolbox_main');
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'pad']);
$toolbarContainer.before(helpMenu.menu);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
framework._.toolbar.$drawer.append(helpMenu.button);
$toolbarContainer.before(helpMenu.menu);
framework._.toolbar.$drawer.append($helpMenuButton);
};
var mkDiffOptions = function(cursor, readOnly) {
@ -632,7 +636,8 @@ define([
editor.execCommand('print');
framework.feedback('PRINT_PAD');
});
framework._.toolbar.$drawer.append($printButton);
var $print = UIElements.getEntryFromButton($printButton);
framework._.toolbar.$drawer.append($print);
};
var andThen2 = function(editor, Ckeditor, framework) {

View File

@ -16,6 +16,7 @@ define([
'/common/hyperscript.js',
'/customize/messages.js',
'cm/lib/codemirror',
'/common/common-ui-elements.js',
'css!/components/bootstrap/dist/css/bootstrap.min.css',
'css!/components/components-font-awesome/css/font-awesome.min.css',
@ -61,7 +62,8 @@ define([
UI,
h,
Messages,
CMeditor)
CMeditor,
UIElements)
{
window.CodeMirror = CMeditor;
@ -106,19 +108,15 @@ define([
};
var mkThemeButton = function (framework) {
var $theme = $(h('button.cp-toolbar-appmenu', [
h('i.cptools.cptools-palette'),
h('span.cp-button-name', Messages.toolbar_theme)
]));
var $content = $(h('div.cp-toolbar-drawer-content', {
tabindex: 1
})).hide();
// set up all the necessary events
UI.createDrawer($theme, $content);
framework._.toolbar.$theme = $content;
framework._.toolbar.$bottomL.append($theme);
const $drawer = UIElements.createDropdown({
text: Messages.toolbar_theme,
options: [],
common: framework._.sfCommon,
iconCls: 'cptools cptools-palette'
});
framework._.toolbar.$theme = $drawer.find('ul.cp-dropdown-content');
framework._.toolbar.$bottomL.append($drawer);
$drawer.addClass('cp-toolbar-appmenu');
};
var mkPrintButton = function (framework, editor, $content, $print) {
@ -130,7 +128,8 @@ define([
window.print();
framework.feedback('PRINT_SLIDES');
});
framework._.toolbar.$drawer.append($printButton);
var $printEntry = UIElements.getEntryFromButton($printButton);
framework._.toolbar.$drawer.append($printEntry);
};
// Flag to check if a file from the filepicker is a mediatag for the slides or a background image
@ -325,12 +324,13 @@ define([
title: Messages.slideOptionsTitle,
hiddenReadOnly: true,
text: Messages.slideOptionsText,
name: 'options'
name: 'options',
callback: function () {
$('body').append(createPrintDialog());
}
});
$optionsButton.click(function () {
$('body').append(createPrintDialog());
});
framework._.toolbar.$theme.append($optionsButton);
var $options = UIElements.getEntryFromButton($optionsButton);
framework._.toolbar.$theme.append($options);
metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata();
@ -385,6 +385,7 @@ define([
$backgroundPicker.val(backColor);
$backgroundPicker.click();
});
var $backButton = UIElements.getEntryFromButton($back);
var $foregroundPicker = $('<input>', { type: 'color', value: textColor })
.css({ display: 'none', })
@ -400,13 +401,15 @@ define([
$foregroundPicker.val(textColor);
$foregroundPicker.click();
});
var $textButton = UIElements.getEntryFromButton($text);
var $testColor = $('<input>', { type: 'color', value: '!' });
if ($testColor.attr('type') !== "color" || $testColor.val() === '!') { return; }
$check.append($backgroundPicker);
$check.append($foregroundPicker);
framework._.toolbar.$theme.append($text).append($back);
framework._.toolbar.$theme.append($textButton).append($backButton);
metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata();
@ -436,9 +439,9 @@ define([
var mkHelpMenu = function (framework) {
var $codeMirrorContainer = $('#cp-app-slide-editor-container');
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'slide']);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
$codeMirrorContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
framework._.toolbar.$drawer.append($helpMenuButton);
};
var activateLinks = function ($content, framework) {

View File

@ -17,6 +17,7 @@ define([
'/whiteboard/colors.js',
'/customize/application_config.js',
'/components/chainpad/chainpad.dist.js',
'/common/common-ui-elements.js',
'/lib/fabric.min.js',
'less!/whiteboard/app-whiteboard.less'
@ -34,7 +35,8 @@ define([
Messages,
Colors,
AppConfig,
ChainPad)
ChainPad,
UIElements)
{
var APP = window.APP = {
@ -295,8 +297,10 @@ define([
var mkHelpMenu = function (framework) {
var $appContainer = $('#cp-app-whiteboard-container');
var helpMenu = framework._.sfCommon.createHelpMenu(['whiteboard']);
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
$appContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
framework._.toolbar.$drawer.append($helpMenuButton);
};
// Start of the main loop
@ -431,13 +435,19 @@ define([
});
// Export to drive as PNG
framework._.sfCommon.createButton('savetodrive', true, {}).click(function () {
var defaultName = framework._.title.getTitle();
UI.prompt(Messages.exportPrompt, defaultName + '.png', function (name) {
if (name === null || !name.trim()) { return; }
APP.upload(name);
});
}).appendTo($drawer);
var $saveToDriveButton = framework._.sfCommon.createButton('savetodrive', true, {
callback: function () {
var defaultName = framework._.title.getTitle()
|| framework._.title.defaultTitle;
UI.prompt(Messages.exportPrompt, defaultName + '.png', function (name) {
if (name === null || !name.trim()) { return; }
APP.upload(name);
});
}
});
var $saveToDrive = UIElements.getEntryFromButton($saveToDriveButton);
$saveToDrive.appendTo($drawer);
} else {
framework._.sfCommon.createButton('', true, {
title: Messages.canvas_imageEmbed,