Custom GTT Shortcuts (Merge, Apply to All, etc.) Through Bookmarklets


FlixFont+
GTT+

/*  

Usage: Drag to Bookmark Bar, click once when you open a project in GTT,
and use predefined shortcuts until you close or reload the page.

Implemented by Dan Biktashev (katzurki@gmail.com) v1.5 30 Jan 2019

Currently defined are: 
    Alt+A               Apply to All
    Alt+S               Merge Down
    Alt+Z               Insert Current Property Name (only for Booking.com projects)
    Ctrl+Down,          Replace double and trailing spaces 
    Ctrl/Cmd+J          (use Alt+Down if you need to retain them on purpose) 

As an added bonus, it prettifies GTT layout making it easier on the eyes.
    
*/

var iframe = document.querySelectorAll('iframe.gtc-document-frame.jfk-scrollbar')[1]
var iframe2 = document.querySelectorAll('iframe.gtc-document-frame.jfk-scrollbar')[0]
var base = iframe.contentWindow.document.querySelector('#transEditor')
var _document = iframe.contentWindow.document
var __document = iframe2.contentWindow.document
var el = _document.querySelector('#transEditor')
const MERGE_SELECTOR = '#gtc-merge-parent > div.modal-dialog.gtc-merge > div.modal-dialog-buttons > button.goog-buttonset-default'

/* First, let's get fancy with our layout in Target/Source columns */
const H1_CSS = 'h1.notranslate { font-size: 1em; color: MidnightBlue; transition: all 1s; text-decoration:underline;}'
const HR_CSS = 'hr{border:0;height:1px;position:relative;margin:.5em 0}|hr:before{top:-.5em;height:1em}|hr:after{height:.5em;top:1px}|hr:after,hr:before{content:\'\';display:inline;position:absolute;width:100%}|hr,hr:before{bq,0,0,.1) 0,rgba(0,0,0,0) 75%);background:-o-radial-gradient(center,ellipse cover,rgba(0,0,0,.1) 0,rgba(0,0,0,0) 75%);background:-ms-radial-gradient(center,ellipse cover,rgba(0,0,0,.1) 0,rgba(0,0,0,0) 75%);background:radial-gradient(ellipse at center,rgba(0,0,0,.1) 0,rgba(0,0,0,0) 75%)}|body,hr:after{background:#f4f4f4}'
function insertCSS(doc, rules) {
    for (var rule of rules.split('|')) {
        var h = document.createElement('style'),styleSheet
        doc.head.appendChild(h)
        styleSheet = h.sheet
        styleSheet.insertRule(rule)
    }
}

insertCSS(_document, H1_CSS)
insertCSS(__document, H1_CSS)
insertCSS(_document, HR_CSS)
insertCSS(__document, HR_CSS)

/* Attaching to el, instead of document, to get the highest priority,
otherwise GTT does its own stuff first (which breaks our Ctrl+Down, for example) */
el.onkeydown = function(event) {
    var current_name = _document.querySelector('#current-hotel-name')
    var el = _document.querySelector('#transEditor > div')
    if (current_name) { current_name.remove() }
    /* keyCode 40 = ArrowDown, keyCode 74 = j */
    if ((event.ctrlKey || event.metaKey) && (event.keyCode == 40 || event.keyCode == 74)) { eliminateDoubleSpaces(el) }
    if (event.altKey) { current_name = hotelName() }
    /* keyCode 90 = z */
    if (event.altKey && event.keyCode == 90) {
        event.preventDefault()
        var sel = iframe.contentWindow.getSelection()
        var offset = sel.anchorOffset
        var after_paste_offset = current_name.length
        var text_before = el.innerText.substring(0, offset)
        var text_after = el.innerText.substring(offset)
        el.innerText = text_before + current_name + text_after
        var range = _document.createRange()
        range.setStart(el.childNodes[0], offset + after_paste_offset)
        range.collapse(true)
        sel.removeAllRanges()
        sel.addRange(range)
        el.focus()
    }
    /* keyCode 65 = a */
    if (event.altKey && event.keyCode == 65) {
        event.preventDefault()
        applyToAll()
    }
    /* keyCode 83 = s */
    if (event.altKey && event.keyCode == 83) {
        event.preventDefault()
        mergeDown()
    }
}

_document.onkeyup = function() { fadeOut() }

function eliminateDoubleSpaces(el) {
    el.innerText = el.innerText.replace(/ +(?= )/g, '').trim()
}

/* It's easiest to imitate a click and let GTT figure out the Merge Segment logic */
var mergeDown = (function() { document.querySelector(MERGE_SELECTOR).click() })

/* Ditto, but the clicks are more complex, and we have to bubble up from the iframe */
var applyToAll = (function() {
    const a = f => new MouseEvent(f, { bubbles: !0 }),
        b = f => () => document.querySelector(f).click(),
        c = f => `#fnrDialogParent .gtc-rep-modal-dialog .modal-dialog-buttons button[name=${f}]`,
        d = {
            imgSel: (f => () => {
                const g = a('mousedown'),
                    h = a('mouseup'),
                    i = document.querySelector(f)
                i.dispatchEvent(g), i.dispatchEvent(h)
            })('img.jfk-button-img.gtc-img-rep'),
            applyToAll: b(c('repDialogReplaceAll')),
            exit: b(c('repDialogExit'))
        }
    d.imgSel(), d.applyToAll(), d.exit()
})

function hotelName() {
    var fresh_name = base.parentNode.parentElement.parentElement.parentElement.parentElement.querySelector('a').textContent
    if (!_document.querySelector('#current-hotel-name')) {
        var hotel_name = _document.createElement('span')
        hotel_name.id = 'current-hotel-name'
        hotel_name.style.color = 'green'
        hotel_name.textContent = ' ' + fresh_name
        _document.querySelector('.gtc-editor-info').insertAdjacentElement('beforeEnd', hotel_name)
    }
    /* In the unlikely but possible case our focus has shifted, let's try for both cases */
    return (typeof fresh_name == 'undefined' ? document.querySelector('#current-hotel-name') : fresh_name)
}


function fadeOut() {
    var current_name = _document.querySelector('#current-hotel-name')
    if (current_name) { fade('out', 500, current_name, true) }

}

/* Gratefully stolen from somewhere on Stackoverflow */
function fade(type, ms, el, remove = false) {
    var isIn = type === 'in',
        opacity = isIn ? 0 : 1,
        interval = 50,
        duration = ms,
        gap = interval / duration

    if (isIn) {
        el.style.display = 'inline'
        el.style.opacity = opacity
    }

    function func() {
        opacity = isIn ? opacity + gap : opacity - gap
        el.style.opacity = opacity

        if (opacity <= 0) { remove ? el.remove() : el.style.display = 'none' }
        if (opacity <= 0 || opacity >= 1) window.clearInterval(fading)
    }

    var fading = window.setInterval(func, interval)
}

Native GTT Shortcuts


Navigation

Ctrl+Home 

Select first unit

Ctrl+End 

Select last unit

Ctrl+K 

Select previous unit

Ctrl+J 

Select next unit

Alt+K 

Select previous untranslated unit

Alt+J 

Select next untranslated unit

Alt+L 

Select next invalid unit

Editing

 

Ctrl+F 

Find and replace

Ctrl+S 

Save

Ctrl+Z 

Undo

Ctrl+Y 

Redo

Ctrl+M 

Add comment

Translation

Ctrl+Shift+K 

Open/close toolkit

Ctrl+Shift+S 

Replace translation with source

Ctrl+Shift+M 

Replace translation with machine translation

Ctrl+Shift+L 

Replace translation with translation memory

Ctrl+Shift+A 

Start automatic translation search

Ctrl+Shift+C 

Start custom translation search for highlighted text

Ctrl+Shift+I 

Automatically insert placeholders

Ctrl+Shift+U 

Clear all placeholders

Ctrl+Shift+Space 

Show placeholder menu



CHANGELOG

v0.2, changed Copy Hotel logic to fit the new GTT
v1.0: completely reworked the code, eliminating the need for other programs v1.2: Got rid of all the unnecessary functions, including Copy to Clipboard