Patch by Marius BALTEANU. git-svn-id: http://svn.redmine.org/redmine/trunk@20729 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/4.2.0
@@ -24,6 +24,10 @@ module Redmine | |||
(/(:?mswin|mingw)/.match?(RUBY_PLATFORM)) || | |||
(RUBY_PLATFORM == 'java' && /windows/i.match?(ENV['OS'] || ENV['os'])) | |||
end | |||
def osx? | |||
(/(:?darwin)/.match?(RUBY_PLATFORM)) | |||
end | |||
end | |||
end | |||
end |
@@ -22,6 +22,7 @@ | |||
/* Modified by JP LANG for textile formatting */ | |||
let lastJstPreviewed = null; | |||
const isMac = Boolean(navigator.platform.toLowerCase().match(/mac/)); | |||
function jsToolBar(textarea) { | |||
if (!document.createElement) { return; } | |||
@@ -208,6 +209,7 @@ jsToolBar.prototype = { | |||
mode: 'wiki', | |||
elements: {}, | |||
help_link: '', | |||
shortcuts: {}, | |||
getMode: function() { | |||
return this.mode; | |||
@@ -233,10 +235,27 @@ jsToolBar.prototype = { | |||
button: function(toolName) { | |||
var tool = this.elements[toolName]; | |||
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; | |||
return b; | |||
}, | |||
buttonTitleWithShortcut: function(title, shortcutKey) { | |||
if (isMac) { | |||
return title + " (⌘" + shortcutKey.toUpperCase() + ")"; | |||
} else { | |||
return title + " (Ctrl+" + shortcutKey.toUpperCase() + ")"; | |||
} | |||
}, | |||
space: function(toolName) { | |||
var tool = new jsSpace(toolName) | |||
if (this.elements[toolName].width !== undefined) | |||
@@ -409,7 +428,7 @@ jsToolBar.prototype = { | |||
this.toolbar.classList.add('hidden'); | |||
this.textarea.classList.add('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'); | |||
}, | |||
hidePreview: function(event) { | |||
@@ -418,18 +437,26 @@ jsToolBar.prototype = { | |||
this.textarea.classList.remove('hidden'); | |||
this.textarea.focus(); | |||
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'); | |||
}, | |||
keyboardShortcuts: function(e) { | |||
let stop = false; | |||
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')) { | |||
e.stopPropagation(); | |||
e.preventDefault(); | |||
this.tabsBlock.getElementsByClassName('tab-preview')[0].click(); | |||
stop = true | |||
this.tabsBlock.querySelector('.tab-preview').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) { | |||
if (this.base_url != '') { | |||
@@ -539,4 +566,13 @@ function isToogleEditPreviewShortcut(e) { | |||
} else { | |||
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 = { | |||
type: 'button', | |||
title: 'Strong', | |||
shortcut: 'b', | |||
fn: { | |||
wiki: function() { this.singleTag('**') } | |||
} | |||
@@ -35,6 +36,7 @@ jsToolBar.prototype.elements.strong = { | |||
jsToolBar.prototype.elements.em = { | |||
type: 'button', | |||
title: 'Italic', | |||
shortcut: 'i', | |||
fn: { | |||
wiki: function() { this.singleTag("*") } | |||
} | |||
@@ -44,6 +46,7 @@ jsToolBar.prototype.elements.em = { | |||
jsToolBar.prototype.elements.ins = { | |||
type: 'button', | |||
title: 'Underline', | |||
shortcut: 'u', | |||
fn: { | |||
wiki: function() { this.singleTag('_') } | |||
} |
@@ -26,6 +26,7 @@ | |||
jsToolBar.prototype.elements.strong = { | |||
type: 'button', | |||
title: 'Strong', | |||
shortcut: 'b', | |||
fn: { | |||
wiki: function() { this.singleTag('*') } | |||
} | |||
@@ -35,6 +36,7 @@ jsToolBar.prototype.elements.strong = { | |||
jsToolBar.prototype.elements.em = { | |||
type: 'button', | |||
title: 'Italic', | |||
shortcut: 'i', | |||
fn: { | |||
wiki: function() { this.singleTag("_") } | |||
} | |||
@@ -44,6 +46,7 @@ jsToolBar.prototype.elements.em = { | |||
jsToolBar.prototype.elements.ins = { | |||
type: 'button', | |||
title: 'Underline', | |||
shortcut: 'u', | |||
fn: { | |||
wiki: function() { this.singleTag('+') } | |||
} |
@@ -68,4 +68,66 @@ class InlineAutocompleteSystemTest < ApplicationSystemTestCase | |||
find 'textarea#issue_notes', :visible => true | |||
find 'div#preview_issue_notes', :visible => false | |||
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 |