Feature #34549 » 0001-Add-keyboard-shortcuts-for-bold-italic-and-underline.patch
public/javascripts/jstoolbar/jstoolbar.js | ||
---|---|---|
22 | 22 | |
23 | 23 |
/* Modified by JP LANG for textile formatting */ |
24 | 24 |
let lastJstPreviewed = null; |
25 |
const isMac = Boolean(navigator.platform.toLowerCase().match(/mac/)); |
|
25 | 26 | |
26 | 27 |
function jsToolBar(textarea) { |
27 | 28 |
if (!document.createElement) { return; } |
... | ... | |
208 | 209 |
mode: 'wiki', |
209 | 210 |
elements: {}, |
210 | 211 |
help_link: '', |
212 |
shortcuts: {}, |
|
211 | 213 | |
212 | 214 |
getMode: function() { |
213 | 215 |
return this.mode; |
... | ... | |
233 | 235 |
button: function(toolName) { |
234 | 236 |
var tool = this.elements[toolName]; |
235 | 237 |
if (typeof tool.fn[this.mode] != 'function') return null; |
236 |
var b = new jsButton(tool.title, tool.fn[this.mode], this, 'jstb_'+toolName); |
|
238 | ||
239 |
const className = 'jstb_' + toolName; |
|
240 |
let title = tool.title |
|
241 | ||
242 |
if (tool.hasOwnProperty('shortcut')) { |
|
243 |
this.shortcuts[tool.shortcut] = className; |
|
244 |
title = this.buttonTitleWithShortcut(tool.title, tool.shortcut) |
|
245 |
} |
|
246 | ||
247 |
var b = new jsButton(title, tool.fn[this.mode], this, className); |
|
237 | 248 |
if (tool.icon != undefined) b.icon = tool.icon; |
249 | ||
238 | 250 |
return b; |
239 | 251 |
}, |
252 |
buttonTitleWithShortcut: function(title, shortcutKey) { |
|
253 |
if (isMac) { |
|
254 |
return title + " (⌘" + shortcutKey.toUpperCase() + ")"; |
|
255 |
} else { |
|
256 |
return title + " (Ctrl+" + shortcutKey.toUpperCase() + ")"; |
|
257 |
} |
|
258 |
}, |
|
240 | 259 |
space: function(toolName) { |
241 | 260 |
var tool = new jsSpace(toolName) |
242 | 261 |
if (this.elements[toolName].width !== undefined) |
... | ... | |
409 | 428 |
this.toolbar.classList.add('hidden'); |
410 | 429 |
this.textarea.classList.add('hidden'); |
411 | 430 |
this.preview.classList.remove('hidden'); |
412 |
this.tabsBlock.getElementsByClassName('tab-edit')[0].classList.remove('selected');
|
|
431 |
this.tabsBlock.querySelector('.tab-edit').classList.remove('selected');
|
|
413 | 432 |
event.target.classList.add('selected'); |
414 | 433 |
}, |
415 | 434 |
hidePreview: function(event) { |
... | ... | |
418 | 437 |
this.textarea.classList.remove('hidden'); |
419 | 438 |
this.textarea.focus(); |
420 | 439 |
this.preview.classList.add('hidden'); |
421 |
this.tabsBlock.getElementsByClassName('tab-preview')[0].classList.remove('selected');
|
|
440 |
this.tabsBlock.querySelector('.tab-preview').classList.remove('selected');
|
|
422 | 441 |
event.target.classList.add('selected'); |
423 | 442 |
}, |
424 | 443 |
keyboardShortcuts: function(e) { |
444 |
let stop = false; |
|
425 | 445 |
if (isToogleEditPreviewShortcut(e)) { |
426 |
// Switch to preview only if tab edit is selected when the event triggered.
|
|
446 |
// Switch to preview only if Edit tab is selected when the event triggers.
|
|
427 | 447 |
if (this.tabsBlock.querySelector('.tab-edit.selected')) { |
428 |
e.stopPropagation(); |
|
429 |
e.preventDefault(); |
|
430 |
this.tabsBlock.getElementsByClassName('tab-preview')[0].click(); |
|
448 |
stop = true |
|
449 |
this.tabsBlock.querySelector('.tab-preview').click(); |
|
431 | 450 |
} |
432 | 451 |
} |
452 |
if (isModifierKey(e) && this.shortcuts.hasOwnProperty(e.key.toLowerCase())) { |
|
453 |
stop = true |
|
454 |
this.toolbar.querySelector("." + this.shortcuts[e.key.toLowerCase()]).click(); |
|
455 |
} |
|
456 |
if (stop) { |
|
457 |
e.stopPropagation(); |
|
458 |
e.preventDefault(); |
|
459 |
} |
|
433 | 460 |
}, |
434 | 461 |
stripBaseURL: function(url) { |
435 | 462 |
if (this.base_url != '') { |
... | ... | |
539 | 566 |
} else { |
540 | 567 |
return false; |
541 | 568 |
} |
569 |
} |
|
570 |
function isModifierKey(e) { |
|
571 |
if (isMac && e.metaKey) { |
|
572 |
return true; |
|
573 |
} else if (!isMac && e.ctrlKey) { |
|
574 |
return true; |
|
575 |
} else { |
|
576 |
return false; |
|
577 |
} |
|
542 | 578 |
} |
public/javascripts/jstoolbar/markdown.js | ||
---|---|---|
26 | 26 |
jsToolBar.prototype.elements.strong = { |
27 | 27 |
type: 'button', |
28 | 28 |
title: 'Strong', |
29 |
shortcut: 'b', |
|
29 | 30 |
fn: { |
30 | 31 |
wiki: function() { this.singleTag('**') } |
31 | 32 |
} |
... | ... | |
35 | 36 |
jsToolBar.prototype.elements.em = { |
36 | 37 |
type: 'button', |
37 | 38 |
title: 'Italic', |
39 |
shortcut: 'i', |
|
38 | 40 |
fn: { |
39 | 41 |
wiki: function() { this.singleTag("*") } |
40 | 42 |
} |
... | ... | |
44 | 46 |
jsToolBar.prototype.elements.ins = { |
45 | 47 |
type: 'button', |
46 | 48 |
title: 'Underline', |
49 |
shortcut: 'u', |
|
47 | 50 |
fn: { |
48 | 51 |
wiki: function() { this.singleTag('_') } |
49 | 52 |
} |
public/javascripts/jstoolbar/textile.js | ||
---|---|---|
26 | 26 |
jsToolBar.prototype.elements.strong = { |
27 | 27 |
type: 'button', |
28 | 28 |
title: 'Strong', |
29 |
shortcut: 'b', |
|
29 | 30 |
fn: { |
30 | 31 |
wiki: function() { this.singleTag('*') } |
31 | 32 |
} |
... | ... | |
35 | 36 |
jsToolBar.prototype.elements.em = { |
36 | 37 |
type: 'button', |
37 | 38 |
title: 'Italic', |
39 |
shortcut: 'i', |
|
38 | 40 |
fn: { |
39 | 41 |
wiki: function() { this.singleTag("_") } |
40 | 42 |
} |
... | ... | |
44 | 46 |
jsToolBar.prototype.elements.ins = { |
45 | 47 |
type: 'button', |
46 | 48 |
title: 'Underline', |
49 |
shortcut: 'u', |
|
47 | 50 |
fn: { |
48 | 51 |
wiki: function() { this.singleTag('+') } |
49 | 52 |
} |
test/system/keyboard_shortcuts_test.rb | ||
---|---|---|
68 | 68 |
find 'textarea#issue_notes', :visible => true |
69 | 69 |
find 'div#preview_issue_notes', :visible => false |
70 | 70 |
end |
71 | ||
72 |
def test_keyboard_shortcuts_for_wiki_toolbar_buttons_using_textile |
|
73 |
with_settings :text_formatting => 'textile' do |
|
74 |
log_user('jsmith', 'jsmith') |
|
75 |
visit 'issues/new' |
|
76 | ||
77 |
find('#issue_description').click.send_keys([:control, 'b']) |
|
78 |
assert_equal '**', find('#issue_description').value |
|
79 | ||
80 |
# Clear textarea value |
|
81 |
fill_in 'Description', :with => '' |
|
82 |
find('#issue_description').send_keys([:control, 'u']) |
|
83 |
assert_equal '++', find('#issue_description').value |
|
84 | ||
85 |
# Clear textarea value |
|
86 |
fill_in 'Description', :with => '' |
|
87 |
find('#issue_description').send_keys([:control, 'i']) |
|
88 |
assert_equal '__', find('#issue_description').value |
|
89 |
end |
|
90 |
end |
|
91 | ||
92 |
def test_keyboard_shortcuts_for_wiki_toolbar_buttons_using_markdown |
|
93 |
with_settings :text_formatting => 'markdown' do |
|
94 |
log_user('jsmith', 'jsmith') |
|
95 |
visit 'issues/new' |
|
96 | ||
97 |
find('#issue_description').click.send_keys([:control, 'b']) |
|
98 |
assert_equal '****', find('#issue_description').value |
|
99 | ||
100 |
# Clear textarea value |
|
101 |
fill_in 'Description', :with => '' |
|
102 |
find('#issue_description').send_keys([:control, 'u']) |
|
103 |
assert_equal '__', find('#issue_description').value |
|
104 | ||
105 |
# Clear textarea value |
|
106 |
fill_in 'Description', :with => '' |
|
107 |
find('#issue_description').send_keys([:control, 'i']) |
|
108 |
assert_equal '**', find('#issue_description').value |
|
109 |
end |
|
110 |
end |
|
111 | ||
112 |
def test_keyboard_shortcuts_keys_should_be_shown_in_button_title |
|
113 |
log_user('jsmith', 'jsmith') |
|
114 |
visit 'issues/new' |
|
115 | ||
116 |
within('.jstBlock .jstElements') do |
|
117 |
assert_equal 'Strong (Ctrl+B)', find('button.jstb_strong')['title'] |
|
118 |
assert_equal 'Italic (Ctrl+I)', find('button.jstb_em')['title'] |
|
119 |
assert_equal 'Underline (Ctrl+U)', find('button.jstb_ins')['title'] |
|
120 |
end |
|
121 |
end |
|
71 | 122 |
end |