diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js
index da9813106..f25418e80 100644
--- a/src/static/js/pad_editbar.js
+++ b/src/static/js/pad_editbar.js
@@ -77,411 +77,406 @@ ToolbarItem.prototype.bind = function (callback) {
}
};
-
-const padeditbar = (() => {
- const syncAnimation = (() => {
- const SYNCING = -100;
- const DONE = 100;
- let state = DONE;
- const fps = 25;
- const step = 1 / fps;
- const T_START = -0.5;
- const T_FADE = 1.0;
- const T_GONE = 1.5;
- const animator = padutils.makeAnimationScheduler(() => {
- if (state === SYNCING || state === DONE) {
- return false;
- } else if (state >= T_GONE) {
- state = DONE;
+const syncAnimation = (() => {
+ const SYNCING = -100;
+ const DONE = 100;
+ let state = DONE;
+ const fps = 25;
+ const step = 1 / fps;
+ const T_START = -0.5;
+ const T_FADE = 1.0;
+ const T_GONE = 1.5;
+ const animator = padutils.makeAnimationScheduler(() => {
+ if (state === SYNCING || state === DONE) {
+ return false;
+ } else if (state >= T_GONE) {
+ state = DONE;
+ $('#syncstatussyncing').css('display', 'none');
+ $('#syncstatusdone').css('display', 'none');
+ return false;
+ } else if (state < 0) {
+ state += step;
+ if (state >= 0) {
$('#syncstatussyncing').css('display', 'none');
- $('#syncstatusdone').css('display', 'none');
- return false;
- } else if (state < 0) {
- state += step;
- if (state >= 0) {
- $('#syncstatussyncing').css('display', 'none');
- $('#syncstatusdone').css('display', 'block').css('opacity', 1);
- }
- return true;
- } else {
- state += step;
- if (state >= T_FADE) {
- $('#syncstatusdone').css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
- }
- return true;
+ $('#syncstatusdone').css('display', 'block').css('opacity', 1);
}
- }, step * 1000);
- return {
- syncing: () => {
- state = SYNCING;
- $('#syncstatussyncing').css('display', 'block');
- $('#syncstatusdone').css('display', 'none');
- },
- done: () => {
- state = T_START;
- animator.scheduleAnimation();
- },
- };
- })();
-
+ return true;
+ } else {
+ state += step;
+ if (state >= T_FADE) {
+ $('#syncstatusdone').css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
+ }
+ return true;
+ }
+ }, step * 1000);
return {
- _editbarPosition: 0,
-
- init() {
- this.dropdowns = [];
-
- $('#editbar .editbarbutton').attr('unselectable', 'on'); // for IE
- this.enable();
- $('#editbar [data-key]').each((i, elt) => {
- $(elt).unbind('click');
- new ToolbarItem($(elt)).bind((command, item) => {
- this.triggerCommand(command, item);
- });
- });
-
- $('body:not(#editorcontainerbox)').on('keydown', (evt) => {
- this._bodyKeyEvent(evt);
- });
-
- $('.show-more-icon-btn').click(() => {
- $('.toolbar').toggleClass('full-icons');
- });
- this.checkAllIconsAreDisplayedInToolbar();
- $(window).resize(_.debounce(() => this.checkAllIconsAreDisplayedInToolbar(), 100));
-
- this._registerDefaultCommands();
-
- hooks.callAll('postToolbarInit', {
- toolbar: this,
- ace: padeditor.ace,
- });
-
- /*
- * On safari, the dropdown in the toolbar gets hidden because of toolbar
- * overflow:hidden property. This is a bug from Safari: any children with
- * position:fixed (like the dropdown) should be displayed no matter
- * overflow:hidden on parent
- */
- if (!browser.safari) {
- $('select').niceSelect();
- }
-
- // When editor is scrolled, we add a class to style the editbar differently
- $('iframe[name="ace_outer"]').contents().scroll((ev) => {
- $('#editbar').toggleClass('editor-scrolled', $(ev.currentTarget).scrollTop() > 2);
- });
+ syncing: () => {
+ state = SYNCING;
+ $('#syncstatussyncing').css('display', 'block');
+ $('#syncstatusdone').css('display', 'none');
},
- isEnabled: () => true,
- disable: () => {
- $('#editbar').addClass('disabledtoolbar').removeClass('enabledtoolbar');
- },
- enable: () => {
- $('#editbar').addClass('enabledtoolbar').removeClass('disabledtoolbar');
- },
- commands: {},
- registerCommand(cmd, callback) {
- this.commands[cmd] = callback;
- return this;
- },
- registerDropdownCommand(cmd, dropdown) {
- dropdown = dropdown || cmd;
- this.dropdowns.push(dropdown);
- this.registerCommand(cmd, () => {
- this.toggleDropDown(dropdown);
- });
- },
- registerAceCommand(cmd, callback) {
- this.registerCommand(cmd, (cmd, ace, item) => {
- ace.callWithAce((ace) => {
- callback(cmd, ace, item);
- }, cmd, true);
- });
- },
- triggerCommand(cmd, item) {
- if (this.isEnabled() && this.commands[cmd]) {
- this.commands[cmd](cmd, padeditor.ace, item);
- }
- if (padeditor.ace) padeditor.ace.focus();
- },
- toggleDropDown(moduleName, cb) {
- // do nothing if users are sticked
- if (moduleName === 'users' && $('#users').hasClass('stickyUsers')) {
- return;
- }
-
- $('.nice-select').removeClass('open');
- $('.toolbar-popup').removeClass('popup-show');
-
- // hide all modules and remove highlighting of all buttons
- if (moduleName === 'none') {
- const returned = false;
- for (const thisModuleName of this.dropdowns) {
- // skip the userlist
- if (thisModuleName === 'users') continue;
-
- const module = $(`#${thisModuleName}`);
-
- // skip any "force reconnect" message
- const isAForceReconnectMessage = module.find('button#forcereconnect:visible').length > 0;
- if (isAForceReconnectMessage) continue;
- if (module.hasClass('popup-show')) {
- $(`li[data-key=${thisModuleName}] > a`).removeClass('selected');
- module.removeClass('popup-show');
- }
- }
-
- if (!returned && cb) return cb();
- } else {
- // hide all modules that are not selected and remove highlighting
- // respectively add highlighting to the corresponding button
- for (const thisModuleName of this.dropdowns) {
- const module = $(`#${thisModuleName}`);
-
- if (module.hasClass('popup-show')) {
- $(`li[data-key=${thisModuleName}] > a`).removeClass('selected');
- module.removeClass('popup-show');
- } else if (thisModuleName === moduleName) {
- $(`li[data-key=${thisModuleName}] > a`).addClass('selected');
- module.addClass('popup-show');
- if (cb) {
- cb();
- }
- }
- }
- }
- },
- setSyncStatus: (status) => {
- if (status === 'syncing') {
- syncAnimation.syncing();
- } else if (status === 'done') {
- syncAnimation.done();
- }
- },
- setEmbedLinks: () => {
- const padUrl = window.location.href.split('?')[0];
- const params = '?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false';
- const props = 'width="100%" height="600" frameborder="0"';
-
- if ($('#readonlyinput').is(':checked')) {
- const urlParts = padUrl.split('/');
- urlParts.pop();
- const readonlyLink = `${urlParts.join('/')}/${clientVars.readOnlyId}`;
- $('#embedinput')
- .val(``);
- $('#linkinput').val(readonlyLink);
- } else {
- $('#embedinput')
- .val(``);
- $('#linkinput').val(padUrl);
- }
- },
- checkAllIconsAreDisplayedInToolbar: () => {
- // reset style
- $('.toolbar').removeClass('cropped');
- $('body').removeClass('mobile-layout');
- const menu_left = $('.toolbar .menu_left')[0];
-
- // this is approximate, we cannot measure it because on mobile
- // Layout it takes the full width on the bottom of the page
- const menuRightWidth = 280;
- if (menu_left && menu_left.scrollWidth > $('.toolbar').width() - menuRightWidth ||
- $('.toolbar').width() < 1000) {
- $('body').addClass('mobile-layout');
- }
- if (menu_left && menu_left.scrollWidth > $('.toolbar').width()) {
- $('.toolbar').addClass('cropped');
- }
- },
-
- _bodyKeyEvent(evt) {
- // If the event is Alt F9 or Escape & we're already in the editbar menu
- // Send the users focus back to the pad
- if ((evt.keyCode === 120 && evt.altKey) || evt.keyCode === 27) {
- if ($(':focus').parents('.toolbar').length === 1) {
- // If we're in the editbar already..
- // Close any dropdowns we have open..
- this.toggleDropDown('none');
- // Check we're on a pad and not on the timeslider
- // Or some other window I haven't thought about!
- if (typeof pad === 'undefined') {
- // Timeslider probably..
- // Shift focus away from any drop downs
- $(':focus').blur(); // required to do not try to remove!
- $('#editorcontainerbox').focus(); // Focus back onto the pad
- } else {
- // Shift focus away from any drop downs
- $(':focus').blur(); // required to do not try to remove!
- padeditor.ace.focus(); // Sends focus back to pad
- // The above focus doesn't always work in FF, you have to hit enter afterwards
- evt.preventDefault();
- }
- } else {
- // Focus on the editbar :)
- const firstEditbarElement = parent.parent.$('#editbar button').first();
-
- $(evt.currentTarget).blur();
- firstEditbarElement.focus();
- evt.preventDefault();
- }
- }
- // Are we in the toolbar??
- if ($(':focus').parents('.toolbar').length === 1) {
- // On arrow keys go to next/previous button item in editbar
- if (evt.keyCode !== 39 && evt.keyCode !== 37) return;
-
- // Get all the focusable items in the editbar
- const focusItems = $('#editbar').find('button, select');
-
- // On left arrow move to next button in editbar
- if (evt.keyCode === 37) {
- // If a dropdown is visible or we're in an input don't move to the next button
- if ($('.popup').is(':visible') || evt.target.localName === 'input') return;
-
- this._editbarPosition--;
- // Allow focus to shift back to end of row and start of row
- if (this._editbarPosition === -1) this._editbarPosition = focusItems.length - 1;
- $(focusItems[this._editbarPosition]).focus();
- }
-
- // On right arrow move to next button in editbar
- if (evt.keyCode === 39) {
- // If a dropdown is visible or we're in an input don't move to the next button
- if ($('.popup').is(':visible') || evt.target.localName === 'input') return;
-
- this._editbarPosition++;
- // Allow focus to shift back to end of row and start of row
- if (this._editbarPosition >= focusItems.length) this._editbarPosition = 0;
- $(focusItems[this._editbarPosition]).focus();
- }
- }
- },
-
- _registerDefaultCommands() {
- this.registerDropdownCommand('showusers', 'users');
- this.registerDropdownCommand('settings');
- this.registerDropdownCommand('connectivity');
- this.registerDropdownCommand('import_export');
- this.registerDropdownCommand('embed');
-
- this.registerCommand('settings', () => {
- this.toggleDropDown('settings', () => {
- $('#options-stickychat').focus();
- });
- });
-
- this.registerCommand('import_export', () => {
- this.toggleDropDown('import_export', () => {
- // If Import file input exists then focus on it..
- if ($('#importfileinput').length !== 0) {
- setTimeout(() => {
- $('#importfileinput').focus();
- }, 100);
- } else {
- $('.exportlink').first().focus();
- }
- });
- });
-
- this.registerCommand('showusers', () => {
- this.toggleDropDown('users', () => {
- $('#myusernameedit').focus();
- });
- });
-
- this.registerCommand('embed', () => {
- this.setEmbedLinks();
- this.toggleDropDown('embed', () => {
- $('#linkinput').focus().select();
- });
- });
-
- this.registerCommand('savedRevision', () => {
- padsavedrevs.saveNow();
- });
-
- this.registerCommand('showTimeSlider', () => {
- document.location = `${document.location.pathname}/timeslider`;
- });
-
- const aceAttributeCommand = (cmd, ace) => {
- ace.ace_toggleAttributeOnSelection(cmd);
- };
- this.registerAceCommand('bold', aceAttributeCommand);
- this.registerAceCommand('italic', aceAttributeCommand);
- this.registerAceCommand('underline', aceAttributeCommand);
- this.registerAceCommand('strikethrough', aceAttributeCommand);
-
- this.registerAceCommand('undo', (cmd, ace) => {
- ace.ace_doUndoRedo(cmd);
- });
-
- this.registerAceCommand('redo', (cmd, ace) => {
- ace.ace_doUndoRedo(cmd);
- });
-
- this.registerAceCommand('insertunorderedlist', (cmd, ace) => {
- ace.ace_doInsertUnorderedList();
- });
-
- this.registerAceCommand('insertorderedlist', (cmd, ace) => {
- ace.ace_doInsertOrderedList();
- });
-
- this.registerAceCommand('indent', (cmd, ace) => {
- if (!ace.ace_doIndentOutdent(false)) {
- ace.ace_doInsertUnorderedList();
- }
- });
-
- this.registerAceCommand('outdent', (cmd, ace) => {
- ace.ace_doIndentOutdent(true);
- });
-
- this.registerAceCommand('clearauthorship', (cmd, ace) => {
- // If we have the whole document selected IE control A has been hit
- const rep = ace.ace_getRep();
- let doPrompt = false;
- const lastChar = rep.lines.atIndex(rep.lines.length() - 1).width - 1;
- const lastLineIndex = rep.lines.length() - 1;
- if (rep.selStart[0] === 0 && rep.selStart[1] === 0) {
- // nesting intentionally here to make things readable
- if (rep.selEnd[0] === lastLineIndex && rep.selEnd[1] === lastChar) {
- doPrompt = true;
- }
- }
- /*
- * NOTICE: This command isn't fired on Control Shift C.
- * I intentionally didn't create duplicate code because if you are hitting
- * Control Shift C we make the assumption you are a "power user"
- * and as such we assume you don't need the prompt to bug you each time!
- * This does make wonder if it's worth having a checkbox to avoid being
- * prompted again but that's probably overkill for this contribution.
- */
-
- // if we don't have any text selected, we have a caret or we have already said to prompt
- if ((!(rep.selStart && rep.selEnd)) || ace.ace_isCaret() || doPrompt) {
- if (window.confirm(html10n.get('pad.editbar.clearcolors'))) {
- ace.ace_performDocumentApplyAttributesToCharRange(0, ace.ace_getRep().alltext.length, [
- ['author', ''],
- ]);
- }
- } else {
- ace.ace_setAttributeOnSelection('author', '');
- }
- });
-
- this.registerCommand('timeslider_returnToPad', (cmd) => {
- if (document.referrer.length > 0 &&
- document.referrer.substring(document.referrer.lastIndexOf('/') - 1,
- document.referrer.lastIndexOf('/')) === 'p') {
- document.location = document.referrer;
- } else {
- document.location = document.location.href
- .substring(0, document.location.href.lastIndexOf('/'));
- }
- });
+ done: () => {
+ state = T_START;
+ animator.scheduleAnimation();
},
};
})();
-exports.padeditbar = padeditbar;
+exports.padeditbar = {
+ _editbarPosition: 0,
+
+ init() {
+ this.dropdowns = [];
+
+ $('#editbar .editbarbutton').attr('unselectable', 'on'); // for IE
+ this.enable();
+ $('#editbar [data-key]').each((i, elt) => {
+ $(elt).unbind('click');
+ new ToolbarItem($(elt)).bind((command, item) => {
+ this.triggerCommand(command, item);
+ });
+ });
+
+ $('body:not(#editorcontainerbox)').on('keydown', (evt) => {
+ this._bodyKeyEvent(evt);
+ });
+
+ $('.show-more-icon-btn').click(() => {
+ $('.toolbar').toggleClass('full-icons');
+ });
+ this.checkAllIconsAreDisplayedInToolbar();
+ $(window).resize(_.debounce(() => this.checkAllIconsAreDisplayedInToolbar(), 100));
+
+ this._registerDefaultCommands();
+
+ hooks.callAll('postToolbarInit', {
+ toolbar: this,
+ ace: padeditor.ace,
+ });
+
+ /*
+ * On safari, the dropdown in the toolbar gets hidden because of toolbar
+ * overflow:hidden property. This is a bug from Safari: any children with
+ * position:fixed (like the dropdown) should be displayed no matter
+ * overflow:hidden on parent
+ */
+ if (!browser.safari) {
+ $('select').niceSelect();
+ }
+
+ // When editor is scrolled, we add a class to style the editbar differently
+ $('iframe[name="ace_outer"]').contents().scroll((ev) => {
+ $('#editbar').toggleClass('editor-scrolled', $(ev.currentTarget).scrollTop() > 2);
+ });
+ },
+ isEnabled: () => true,
+ disable: () => {
+ $('#editbar').addClass('disabledtoolbar').removeClass('enabledtoolbar');
+ },
+ enable: () => {
+ $('#editbar').addClass('enabledtoolbar').removeClass('disabledtoolbar');
+ },
+ commands: {},
+ registerCommand(cmd, callback) {
+ this.commands[cmd] = callback;
+ return this;
+ },
+ registerDropdownCommand(cmd, dropdown) {
+ dropdown = dropdown || cmd;
+ this.dropdowns.push(dropdown);
+ this.registerCommand(cmd, () => {
+ this.toggleDropDown(dropdown);
+ });
+ },
+ registerAceCommand(cmd, callback) {
+ this.registerCommand(cmd, (cmd, ace, item) => {
+ ace.callWithAce((ace) => {
+ callback(cmd, ace, item);
+ }, cmd, true);
+ });
+ },
+ triggerCommand(cmd, item) {
+ if (this.isEnabled() && this.commands[cmd]) {
+ this.commands[cmd](cmd, padeditor.ace, item);
+ }
+ if (padeditor.ace) padeditor.ace.focus();
+ },
+ toggleDropDown(moduleName, cb) {
+ // do nothing if users are sticked
+ if (moduleName === 'users' && $('#users').hasClass('stickyUsers')) {
+ return;
+ }
+
+ $('.nice-select').removeClass('open');
+ $('.toolbar-popup').removeClass('popup-show');
+
+ // hide all modules and remove highlighting of all buttons
+ if (moduleName === 'none') {
+ const returned = false;
+ for (const thisModuleName of this.dropdowns) {
+ // skip the userlist
+ if (thisModuleName === 'users') continue;
+
+ const module = $(`#${thisModuleName}`);
+
+ // skip any "force reconnect" message
+ const isAForceReconnectMessage = module.find('button#forcereconnect:visible').length > 0;
+ if (isAForceReconnectMessage) continue;
+ if (module.hasClass('popup-show')) {
+ $(`li[data-key=${thisModuleName}] > a`).removeClass('selected');
+ module.removeClass('popup-show');
+ }
+ }
+
+ if (!returned && cb) return cb();
+ } else {
+ // hide all modules that are not selected and remove highlighting
+ // respectively add highlighting to the corresponding button
+ for (const thisModuleName of this.dropdowns) {
+ const module = $(`#${thisModuleName}`);
+
+ if (module.hasClass('popup-show')) {
+ $(`li[data-key=${thisModuleName}] > a`).removeClass('selected');
+ module.removeClass('popup-show');
+ } else if (thisModuleName === moduleName) {
+ $(`li[data-key=${thisModuleName}] > a`).addClass('selected');
+ module.addClass('popup-show');
+ if (cb) {
+ cb();
+ }
+ }
+ }
+ }
+ },
+ setSyncStatus: (status) => {
+ if (status === 'syncing') {
+ syncAnimation.syncing();
+ } else if (status === 'done') {
+ syncAnimation.done();
+ }
+ },
+ setEmbedLinks: () => {
+ const padUrl = window.location.href.split('?')[0];
+ const params = '?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false';
+ const props = 'width="100%" height="600" frameborder="0"';
+
+ if ($('#readonlyinput').is(':checked')) {
+ const urlParts = padUrl.split('/');
+ urlParts.pop();
+ const readonlyLink = `${urlParts.join('/')}/${clientVars.readOnlyId}`;
+ $('#embedinput')
+ .val(``);
+ $('#linkinput').val(readonlyLink);
+ } else {
+ $('#embedinput')
+ .val(``);
+ $('#linkinput').val(padUrl);
+ }
+ },
+ checkAllIconsAreDisplayedInToolbar: () => {
+ // reset style
+ $('.toolbar').removeClass('cropped');
+ $('body').removeClass('mobile-layout');
+ const menu_left = $('.toolbar .menu_left')[0];
+
+ // this is approximate, we cannot measure it because on mobile
+ // Layout it takes the full width on the bottom of the page
+ const menuRightWidth = 280;
+ if (menu_left && menu_left.scrollWidth > $('.toolbar').width() - menuRightWidth ||
+ $('.toolbar').width() < 1000) {
+ $('body').addClass('mobile-layout');
+ }
+ if (menu_left && menu_left.scrollWidth > $('.toolbar').width()) {
+ $('.toolbar').addClass('cropped');
+ }
+ },
+
+ _bodyKeyEvent(evt) {
+ // If the event is Alt F9 or Escape & we're already in the editbar menu
+ // Send the users focus back to the pad
+ if ((evt.keyCode === 120 && evt.altKey) || evt.keyCode === 27) {
+ if ($(':focus').parents('.toolbar').length === 1) {
+ // If we're in the editbar already..
+ // Close any dropdowns we have open..
+ this.toggleDropDown('none');
+ // Check we're on a pad and not on the timeslider
+ // Or some other window I haven't thought about!
+ if (typeof pad === 'undefined') {
+ // Timeslider probably..
+ // Shift focus away from any drop downs
+ $(':focus').blur(); // required to do not try to remove!
+ $('#editorcontainerbox').focus(); // Focus back onto the pad
+ } else {
+ // Shift focus away from any drop downs
+ $(':focus').blur(); // required to do not try to remove!
+ padeditor.ace.focus(); // Sends focus back to pad
+ // The above focus doesn't always work in FF, you have to hit enter afterwards
+ evt.preventDefault();
+ }
+ } else {
+ // Focus on the editbar :)
+ const firstEditbarElement = parent.parent.$('#editbar button').first();
+
+ $(evt.currentTarget).blur();
+ firstEditbarElement.focus();
+ evt.preventDefault();
+ }
+ }
+ // Are we in the toolbar??
+ if ($(':focus').parents('.toolbar').length === 1) {
+ // On arrow keys go to next/previous button item in editbar
+ if (evt.keyCode !== 39 && evt.keyCode !== 37) return;
+
+ // Get all the focusable items in the editbar
+ const focusItems = $('#editbar').find('button, select');
+
+ // On left arrow move to next button in editbar
+ if (evt.keyCode === 37) {
+ // If a dropdown is visible or we're in an input don't move to the next button
+ if ($('.popup').is(':visible') || evt.target.localName === 'input') return;
+
+ this._editbarPosition--;
+ // Allow focus to shift back to end of row and start of row
+ if (this._editbarPosition === -1) this._editbarPosition = focusItems.length - 1;
+ $(focusItems[this._editbarPosition]).focus();
+ }
+
+ // On right arrow move to next button in editbar
+ if (evt.keyCode === 39) {
+ // If a dropdown is visible or we're in an input don't move to the next button
+ if ($('.popup').is(':visible') || evt.target.localName === 'input') return;
+
+ this._editbarPosition++;
+ // Allow focus to shift back to end of row and start of row
+ if (this._editbarPosition >= focusItems.length) this._editbarPosition = 0;
+ $(focusItems[this._editbarPosition]).focus();
+ }
+ }
+ },
+
+ _registerDefaultCommands() {
+ this.registerDropdownCommand('showusers', 'users');
+ this.registerDropdownCommand('settings');
+ this.registerDropdownCommand('connectivity');
+ this.registerDropdownCommand('import_export');
+ this.registerDropdownCommand('embed');
+
+ this.registerCommand('settings', () => {
+ this.toggleDropDown('settings', () => {
+ $('#options-stickychat').focus();
+ });
+ });
+
+ this.registerCommand('import_export', () => {
+ this.toggleDropDown('import_export', () => {
+ // If Import file input exists then focus on it..
+ if ($('#importfileinput').length !== 0) {
+ setTimeout(() => {
+ $('#importfileinput').focus();
+ }, 100);
+ } else {
+ $('.exportlink').first().focus();
+ }
+ });
+ });
+
+ this.registerCommand('showusers', () => {
+ this.toggleDropDown('users', () => {
+ $('#myusernameedit').focus();
+ });
+ });
+
+ this.registerCommand('embed', () => {
+ this.setEmbedLinks();
+ this.toggleDropDown('embed', () => {
+ $('#linkinput').focus().select();
+ });
+ });
+
+ this.registerCommand('savedRevision', () => {
+ padsavedrevs.saveNow();
+ });
+
+ this.registerCommand('showTimeSlider', () => {
+ document.location = `${document.location.pathname}/timeslider`;
+ });
+
+ const aceAttributeCommand = (cmd, ace) => {
+ ace.ace_toggleAttributeOnSelection(cmd);
+ };
+ this.registerAceCommand('bold', aceAttributeCommand);
+ this.registerAceCommand('italic', aceAttributeCommand);
+ this.registerAceCommand('underline', aceAttributeCommand);
+ this.registerAceCommand('strikethrough', aceAttributeCommand);
+
+ this.registerAceCommand('undo', (cmd, ace) => {
+ ace.ace_doUndoRedo(cmd);
+ });
+
+ this.registerAceCommand('redo', (cmd, ace) => {
+ ace.ace_doUndoRedo(cmd);
+ });
+
+ this.registerAceCommand('insertunorderedlist', (cmd, ace) => {
+ ace.ace_doInsertUnorderedList();
+ });
+
+ this.registerAceCommand('insertorderedlist', (cmd, ace) => {
+ ace.ace_doInsertOrderedList();
+ });
+
+ this.registerAceCommand('indent', (cmd, ace) => {
+ if (!ace.ace_doIndentOutdent(false)) {
+ ace.ace_doInsertUnorderedList();
+ }
+ });
+
+ this.registerAceCommand('outdent', (cmd, ace) => {
+ ace.ace_doIndentOutdent(true);
+ });
+
+ this.registerAceCommand('clearauthorship', (cmd, ace) => {
+ // If we have the whole document selected IE control A has been hit
+ const rep = ace.ace_getRep();
+ let doPrompt = false;
+ const lastChar = rep.lines.atIndex(rep.lines.length() - 1).width - 1;
+ const lastLineIndex = rep.lines.length() - 1;
+ if (rep.selStart[0] === 0 && rep.selStart[1] === 0) {
+ // nesting intentionally here to make things readable
+ if (rep.selEnd[0] === lastLineIndex && rep.selEnd[1] === lastChar) {
+ doPrompt = true;
+ }
+ }
+ /*
+ * NOTICE: This command isn't fired on Control Shift C.
+ * I intentionally didn't create duplicate code because if you are hitting
+ * Control Shift C we make the assumption you are a "power user"
+ * and as such we assume you don't need the prompt to bug you each time!
+ * This does make wonder if it's worth having a checkbox to avoid being
+ * prompted again but that's probably overkill for this contribution.
+ */
+
+ // if we don't have any text selected, we have a caret or we have already said to prompt
+ if ((!(rep.selStart && rep.selEnd)) || ace.ace_isCaret() || doPrompt) {
+ if (window.confirm(html10n.get('pad.editbar.clearcolors'))) {
+ ace.ace_performDocumentApplyAttributesToCharRange(0, ace.ace_getRep().alltext.length, [
+ ['author', ''],
+ ]);
+ }
+ } else {
+ ace.ace_setAttributeOnSelection('author', '');
+ }
+ });
+
+ this.registerCommand('timeslider_returnToPad', (cmd) => {
+ if (document.referrer.length > 0 &&
+ document.referrer.substring(document.referrer.lastIndexOf('/') - 1,
+ document.referrer.lastIndexOf('/')) === 'p') {
+ document.location = document.referrer;
+ } else {
+ document.location = document.location.href
+ .substring(0, document.location.href.lastIndexOf('/'));
+ }
+ });
+ },
+};