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  |