// ==UserScript==
// @name TFS Helper
// @namespace http://jonas.ninja
// @version 1.9.1
// @description Adds styles and moves things around so that oft-used functions are easier
// @author @_jnblog
// @match http://*/tfs/DefaultCollection/*/_backlogs*
// @match http://*/tfs/DefaultCollection/*/_versionControl*
// @match http://*/tfs/DefaultCollection/*/_workitems*
// @grant GM_addStyle
// @grant GM_setClipboard
// @downloadURL none
// ==/UserScript==
/* jshint -W097 */
/* global GM_addStyle */
/* jshint asi: true, multistr: true */
var $ = unsafeWindow.jQuery;
var topClass = "makeTfsNotAwful"
var cursorUrl = 'url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDQxNS41ODIgNDE1LjU4MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDE1LjU4MiA0MTUuNTgyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggZD0iTTQxMS40Nyw5Ni40MjZsLTQ2LjMxOS00Ni4zMmMtNS40ODItNS40ODItMTQuMzcxLTUuNDgyLTE5Ljg1MywwTDE1Mi4zNDgsMjQzLjA1OGwtODIuMDY2LTgyLjA2NCAgIGMtNS40OC01LjQ4Mi0xNC4zNy01LjQ4Mi0xOS44NTEsMGwtNDYuMzE5LDQ2LjMyYy01LjQ4Miw1LjQ4MS01LjQ4MiwxNC4zNywwLDE5Ljg1MmwxMzguMzExLDEzOC4zMSAgIGMyLjc0MSwyLjc0Miw2LjMzNCw0LjExMiw5LjkyNiw0LjExMmMzLjU5MywwLDcuMTg2LTEuMzcsOS45MjYtNC4xMTJMNDExLjQ3LDExNi4yNzdjMi42MzMtMi42MzIsNC4xMTEtNi4yMDMsNC4xMTEtOS45MjUgICBDNDE1LjU4MiwxMDIuNjI4LDQxNC4xMDMsOTkuMDU5LDQxMS40Nyw5Ni40MjZ6IiBmaWxsPSIjMmQ5ZTFlIi8+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==), auto !important'
$('body').addClass(topClass)
waitForKeyElements("div.tab-page[rawtitle=Links]", doEverything, false)
waitForKeyElements(".workitem-info-bar > .info-text-wrapper", addTaskIdCopyUtilities, false)
$(document).on('click', '.ijg-js-copyButton', copy)
function doEverything(linksPane) {
if ($(linksPane).data('moved')) {
return
}
showLinksPane(linksPane)
stackAllTabs($(linksPane))
addBlockedButton(linksPane)
window.setTimeout(function() {
addTaskIdCopyUtilities(linksPane)
changeDialogBorderColor(linksPane)
}, 250)
refreshNowAndLater();
}
function showLinksPane(linksPane) {
$(linksPane).data('moved', true)
.addClass('linksPanel')
.prepend($("
").addClass('linksPanelHeader')
.text($(linksPane).attr('rawtitle')))
var link = $('a[rawtitle=Links]')
link.closest('td').parent().closest('td').prev().css({'width': '35%' /*, 'padding-right': '12px'*/})
link.closest('td').prepend(linksPane)
link.parent().remove()
}
function changeDialogBorderColor(linksPane) {
// depending on the type of this work item, color the border differently
var dialog = $(linksPane).closest('.workitem-dialog')
var caption = dialog.find('a.caption').text()
if (caption.indexOf('Product Backlog Item ') !== -1) {
dialog.css('border-color', '#009CCC') // blue
} else if (caption.indexOf('Bug ') !== -1) {
dialog.css('border-color', '#CC293D') // red
} else if (caption.indexOf('Feature ') !== -1) {
dialog.css('border-color', '#773B93') // purple
} else { // Task
dialog.css('border-color', '#E0C252') // yellow
}
}
function stackAllTabs($linksPane) {
if (Math.max(window.innerWidth, document.documentElement.clientWidth) >= 1600) {
// not stackable
} else {
// stackable
var column2 = $linksPane.closest('table.content').parent('.column')
var column1 = column2.prev()
column2.add(column1).css({width: '100%', display: 'block'})
}
fillVerticalHeight($linksPane)
}
function fillVerticalHeight($linksPane) {
var column2 = $linksPane.closest('table.content').parent('.column')
var column1 = column2.prev()
var modalRect = $linksPane.closest('.work-item-view')[0].getBoundingClientRect()
var editorContainer = column1.find('.richeditor-editarea')
if (Math.max(window.innerWidth, document.documentElement.clientWidth) >= 1600) {
// not stackable
var editorRect = editorContainer[0].getBoundingClientRect()
var newheight = modalRect.bottom - editorRect.bottom + editorRect.height - 10
var col2height = column2.height() - 58
editorContainer.css('min-height', Math.max(newheight, col2height))
.children('iframe').css('height', Math.max(newheight, col2height))
} else {
// stackable
var contentHeight = $linksPane.closest('.work-item-view').children('.witform-layout')[0].getBoundingClientRect().bottom
var newHeight = editorContainer.height() + modalRect.bottom - contentHeight - 8
editorContainer.css('min-height', Math.max(editorContainer.height(), newHeight))
.children('iframe').css('height', Math.max(editorContainer.height(), newHeight))
}
}
function addTaskIdCopyUtilities() {
$('.workitem-info-bar').find('.info-text-wrapper').each(function(idx, header) {
var $header = $(header)
if ($header.hasClass('ijg-isProcessed')) {
return
}
$header.addClass('ijg-isProcessed')
var id = $header.find('a.caption').text().match(/\d+/)[0]
var url = $header.find('a.caption').prop('href')
var formattedUrl = '[' + $header.find('.info-text').text() + '](' + url + ')'
var message = makeMessage($header)
var $container = $('
')
var button = $('