0001-Add-keyboard-shortcuts-for-bold-italic-and-underline_v2.patch

Marius BALTEANU, 2021-01-25 01:22

Download (8.67 KB)

View differences:

lib/redmine/platform.rb
24 24
        (/(:?mswin|mingw)/.match?(RUBY_PLATFORM)) ||
25 25
           (RUBY_PLATFORM == 'java' && /windows/i.match?(ENV['OS'] || ENV['os']))
26 26
      end
27

  
28
      def osx?
29
        (/(:?darwin)/.match?(RUBY_PLATFORM))
30
      end
27 31
    end
28 32
  end
29 33
end
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([modifier_key, 'b'])
78
      assert_equal '**', find('#issue_description').value
79

  
80
      # Clear textarea value
81
      fill_in 'Description', :with => ''
82
      find('#issue_description').send_keys([modifier_key, 'u'])
83
      assert_equal '++', find('#issue_description').value
84

  
85
      # Clear textarea value
86
      fill_in 'Description', :with => ''
87
      find('#issue_description').send_keys([modifier_key, '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([modifier_key, 'b'])
98
      assert_equal '****', find('#issue_description').value
99

  
100
      # Clear textarea value
101
      fill_in 'Description', :with => ''
102
      find('#issue_description').send_keys([modifier_key, 'u'])
103
      assert_equal '__', find('#issue_description').value
104

  
105
      # Clear textarea value
106
      fill_in 'Description', :with => ''
107
      find('#issue_description').send_keys([modifier_key, '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 (#{modifier_key_title}B)", find('button.jstb_strong')['title']
118
      assert_equal "Italic (#{modifier_key_title}I)", find('button.jstb_em')['title']
119
      assert_equal "Underline (#{modifier_key_title}U)", find('button.jstb_ins')['title']
120
    end
121
  end
122

  
123
  private
124

  
125
  def modifier_key
126
    modifier = osx? ? "command" : "control"
127
    modifier.to_sym
128
  end
129

  
130
  def modifier_key_title
131
    osx? ? "⌘" : "Ctrl+"
132
  end
71 133
end
72
-