mirror of
https://github.com/redmine/redmine.git
synced 2025-11-18 03:00:52 +01:00
Add keyboard shortcuts for bold, italic and underline buttons (#34549).
Patch by Marius BALTEANU. git-svn-id: http://svn.redmine.org/redmine/trunk@20729 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -24,6 +24,10 @@ module Redmine
|
|||||||
(/(:?mswin|mingw)/.match?(RUBY_PLATFORM)) ||
|
(/(:?mswin|mingw)/.match?(RUBY_PLATFORM)) ||
|
||||||
(RUBY_PLATFORM == 'java' && /windows/i.match?(ENV['OS'] || ENV['os']))
|
(RUBY_PLATFORM == 'java' && /windows/i.match?(ENV['OS'] || ENV['os']))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def osx?
|
||||||
|
(/(:?darwin)/.match?(RUBY_PLATFORM))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
/* Modified by JP LANG for textile formatting */
|
/* Modified by JP LANG for textile formatting */
|
||||||
let lastJstPreviewed = null;
|
let lastJstPreviewed = null;
|
||||||
|
const isMac = Boolean(navigator.platform.toLowerCase().match(/mac/));
|
||||||
|
|
||||||
function jsToolBar(textarea) {
|
function jsToolBar(textarea) {
|
||||||
if (!document.createElement) { return; }
|
if (!document.createElement) { return; }
|
||||||
@@ -208,6 +209,7 @@ jsToolBar.prototype = {
|
|||||||
mode: 'wiki',
|
mode: 'wiki',
|
||||||
elements: {},
|
elements: {},
|
||||||
help_link: '',
|
help_link: '',
|
||||||
|
shortcuts: {},
|
||||||
|
|
||||||
getMode: function() {
|
getMode: function() {
|
||||||
return this.mode;
|
return this.mode;
|
||||||
@@ -233,10 +235,27 @@ jsToolBar.prototype = {
|
|||||||
button: function(toolName) {
|
button: function(toolName) {
|
||||||
var tool = this.elements[toolName];
|
var tool = this.elements[toolName];
|
||||||
if (typeof tool.fn[this.mode] != 'function') return null;
|
if (typeof tool.fn[this.mode] != 'function') return null;
|
||||||
var b = new jsButton(tool.title, tool.fn[this.mode], this, 'jstb_'+toolName);
|
|
||||||
|
const className = 'jstb_' + toolName;
|
||||||
|
let title = tool.title
|
||||||
|
|
||||||
|
if (tool.hasOwnProperty('shortcut')) {
|
||||||
|
this.shortcuts[tool.shortcut] = className;
|
||||||
|
title = this.buttonTitleWithShortcut(tool.title, tool.shortcut)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = new jsButton(title, tool.fn[this.mode], this, className);
|
||||||
if (tool.icon != undefined) b.icon = tool.icon;
|
if (tool.icon != undefined) b.icon = tool.icon;
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
},
|
},
|
||||||
|
buttonTitleWithShortcut: function(title, shortcutKey) {
|
||||||
|
if (isMac) {
|
||||||
|
return title + " (⌘" + shortcutKey.toUpperCase() + ")";
|
||||||
|
} else {
|
||||||
|
return title + " (Ctrl+" + shortcutKey.toUpperCase() + ")";
|
||||||
|
}
|
||||||
|
},
|
||||||
space: function(toolName) {
|
space: function(toolName) {
|
||||||
var tool = new jsSpace(toolName)
|
var tool = new jsSpace(toolName)
|
||||||
if (this.elements[toolName].width !== undefined)
|
if (this.elements[toolName].width !== undefined)
|
||||||
@@ -409,7 +428,7 @@ jsToolBar.prototype = {
|
|||||||
this.toolbar.classList.add('hidden');
|
this.toolbar.classList.add('hidden');
|
||||||
this.textarea.classList.add('hidden');
|
this.textarea.classList.add('hidden');
|
||||||
this.preview.classList.remove('hidden');
|
this.preview.classList.remove('hidden');
|
||||||
this.tabsBlock.getElementsByClassName('tab-edit')[0].classList.remove('selected');
|
this.tabsBlock.querySelector('.tab-edit').classList.remove('selected');
|
||||||
event.target.classList.add('selected');
|
event.target.classList.add('selected');
|
||||||
},
|
},
|
||||||
hidePreview: function(event) {
|
hidePreview: function(event) {
|
||||||
@@ -418,18 +437,26 @@ jsToolBar.prototype = {
|
|||||||
this.textarea.classList.remove('hidden');
|
this.textarea.classList.remove('hidden');
|
||||||
this.textarea.focus();
|
this.textarea.focus();
|
||||||
this.preview.classList.add('hidden');
|
this.preview.classList.add('hidden');
|
||||||
this.tabsBlock.getElementsByClassName('tab-preview')[0].classList.remove('selected');
|
this.tabsBlock.querySelector('.tab-preview').classList.remove('selected');
|
||||||
event.target.classList.add('selected');
|
event.target.classList.add('selected');
|
||||||
},
|
},
|
||||||
keyboardShortcuts: function(e) {
|
keyboardShortcuts: function(e) {
|
||||||
|
let stop = false;
|
||||||
if (isToogleEditPreviewShortcut(e)) {
|
if (isToogleEditPreviewShortcut(e)) {
|
||||||
// Switch to preview only if tab edit is selected when the event triggered.
|
// Switch to preview only if Edit tab is selected when the event triggers.
|
||||||
if (this.tabsBlock.querySelector('.tab-edit.selected')) {
|
if (this.tabsBlock.querySelector('.tab-edit.selected')) {
|
||||||
e.stopPropagation();
|
stop = true
|
||||||
e.preventDefault();
|
this.tabsBlock.querySelector('.tab-preview').click();
|
||||||
this.tabsBlock.getElementsByClassName('tab-preview')[0].click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isModifierKey(e) && this.shortcuts.hasOwnProperty(e.key.toLowerCase())) {
|
||||||
|
stop = true
|
||||||
|
this.toolbar.querySelector("." + this.shortcuts[e.key.toLowerCase()]).click();
|
||||||
|
}
|
||||||
|
if (stop) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
stripBaseURL: function(url) {
|
stripBaseURL: function(url) {
|
||||||
if (this.base_url != '') {
|
if (this.base_url != '') {
|
||||||
@@ -539,4 +566,13 @@ function isToogleEditPreviewShortcut(e) {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
function isModifierKey(e) {
|
||||||
|
if (isMac && e.metaKey) {
|
||||||
|
return true;
|
||||||
|
} else if (!isMac && e.ctrlKey) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
jsToolBar.prototype.elements.strong = {
|
jsToolBar.prototype.elements.strong = {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
title: 'Strong',
|
title: 'Strong',
|
||||||
|
shortcut: 'b',
|
||||||
fn: {
|
fn: {
|
||||||
wiki: function() { this.singleTag('**') }
|
wiki: function() { this.singleTag('**') }
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,7 @@ jsToolBar.prototype.elements.strong = {
|
|||||||
jsToolBar.prototype.elements.em = {
|
jsToolBar.prototype.elements.em = {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
title: 'Italic',
|
title: 'Italic',
|
||||||
|
shortcut: 'i',
|
||||||
fn: {
|
fn: {
|
||||||
wiki: function() { this.singleTag("*") }
|
wiki: function() { this.singleTag("*") }
|
||||||
}
|
}
|
||||||
@@ -44,6 +46,7 @@ jsToolBar.prototype.elements.em = {
|
|||||||
jsToolBar.prototype.elements.ins = {
|
jsToolBar.prototype.elements.ins = {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
title: 'Underline',
|
title: 'Underline',
|
||||||
|
shortcut: 'u',
|
||||||
fn: {
|
fn: {
|
||||||
wiki: function() { this.singleTag('_') }
|
wiki: function() { this.singleTag('_') }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
jsToolBar.prototype.elements.strong = {
|
jsToolBar.prototype.elements.strong = {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
title: 'Strong',
|
title: 'Strong',
|
||||||
|
shortcut: 'b',
|
||||||
fn: {
|
fn: {
|
||||||
wiki: function() { this.singleTag('*') }
|
wiki: function() { this.singleTag('*') }
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,7 @@ jsToolBar.prototype.elements.strong = {
|
|||||||
jsToolBar.prototype.elements.em = {
|
jsToolBar.prototype.elements.em = {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
title: 'Italic',
|
title: 'Italic',
|
||||||
|
shortcut: 'i',
|
||||||
fn: {
|
fn: {
|
||||||
wiki: function() { this.singleTag("_") }
|
wiki: function() { this.singleTag("_") }
|
||||||
}
|
}
|
||||||
@@ -44,6 +46,7 @@ jsToolBar.prototype.elements.em = {
|
|||||||
jsToolBar.prototype.elements.ins = {
|
jsToolBar.prototype.elements.ins = {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
title: 'Underline',
|
title: 'Underline',
|
||||||
|
shortcut: 'u',
|
||||||
fn: {
|
fn: {
|
||||||
wiki: function() { this.singleTag('+') }
|
wiki: function() { this.singleTag('+') }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,4 +68,66 @@ class InlineAutocompleteSystemTest < ApplicationSystemTestCase
|
|||||||
find 'textarea#issue_notes', :visible => true
|
find 'textarea#issue_notes', :visible => true
|
||||||
find 'div#preview_issue_notes', :visible => false
|
find 'div#preview_issue_notes', :visible => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_keyboard_shortcuts_for_wiki_toolbar_buttons_using_textile
|
||||||
|
with_settings :text_formatting => 'textile' do
|
||||||
|
log_user('jsmith', 'jsmith')
|
||||||
|
visit 'issues/new'
|
||||||
|
|
||||||
|
find('#issue_description').click.send_keys([modifier_key, 'b'])
|
||||||
|
assert_equal '**', find('#issue_description').value
|
||||||
|
|
||||||
|
# Clear textarea value
|
||||||
|
fill_in 'Description', :with => ''
|
||||||
|
find('#issue_description').send_keys([modifier_key, 'u'])
|
||||||
|
assert_equal '++', find('#issue_description').value
|
||||||
|
|
||||||
|
# Clear textarea value
|
||||||
|
fill_in 'Description', :with => ''
|
||||||
|
find('#issue_description').send_keys([modifier_key, 'i'])
|
||||||
|
assert_equal '__', find('#issue_description').value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_keyboard_shortcuts_for_wiki_toolbar_buttons_using_markdown
|
||||||
|
with_settings :text_formatting => 'markdown' do
|
||||||
|
log_user('jsmith', 'jsmith')
|
||||||
|
visit 'issues/new'
|
||||||
|
|
||||||
|
find('#issue_description').click.send_keys([modifier_key, 'b'])
|
||||||
|
assert_equal '****', find('#issue_description').value
|
||||||
|
|
||||||
|
# Clear textarea value
|
||||||
|
fill_in 'Description', :with => ''
|
||||||
|
find('#issue_description').send_keys([modifier_key, 'u'])
|
||||||
|
assert_equal '__', find('#issue_description').value
|
||||||
|
|
||||||
|
# Clear textarea value
|
||||||
|
fill_in 'Description', :with => ''
|
||||||
|
find('#issue_description').send_keys([modifier_key, 'i'])
|
||||||
|
assert_equal '**', find('#issue_description').value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_keyboard_shortcuts_keys_should_be_shown_in_button_title
|
||||||
|
log_user('jsmith', 'jsmith')
|
||||||
|
visit 'issues/new'
|
||||||
|
|
||||||
|
within('.jstBlock .jstElements') do
|
||||||
|
assert_equal "Strong (#{modifier_key_title}B)", find('button.jstb_strong')['title']
|
||||||
|
assert_equal "Italic (#{modifier_key_title}I)", find('button.jstb_em')['title']
|
||||||
|
assert_equal "Underline (#{modifier_key_title}U)", find('button.jstb_ins')['title']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def modifier_key
|
||||||
|
modifier = osx? ? "command" : "control"
|
||||||
|
modifier.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def modifier_key_title
|
||||||
|
osx? ? "⌘" : "Ctrl+"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user