您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds styles and moves things around so that oft-used functions are easier
当前为
// ==UserScript== // @name TFS Helper // @namespace http://jonas.ninja // @version 1.9.3 // @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 // ==/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($("<h3>").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() + '*\n' + url var message = makeMessage($header) var $container = $('<div class="ijg-copyButtons">') var button = $('<button class="ijg-copyButton">') $container.append(button.clone().text('ID') .addClass('ijg-js-copyButton').data('ijgCopyText', id)) .append(button.clone().text('Link') .addClass('ijg-js-copyButton').data('ijgCopyText', formattedUrl)) .append(button.clone().text('Commit Message').addClass('ijg-js-copyButton').data('ijgCopyText', message)) $header.find('span.info-text').after($container) }) } function makeMessage(el) { var parent = $(el).closest('.info-text-wrapper') if (parent.length === 0) { console.error("The userscript _TFS Helper_ could not identify this task's name") } return 't' + parent.children('a.caption').text().match(/\d{5,}/)[0] + ' ' + parent.children('.info-text').text().replace(/^dev: /i, "") } function addBlockedButton(linksPane) { var blockedInput = linksPane.find('[aria-label=Blocked]'); blockedInput.closest('.control-cell').css({width: 'calc(100% - 60px)', 'min-width': '50px'}) } function copy(e) { $target = $(this) GM_setClipboard($target.data('ijgCopyText')) displayResult($target) } function displayResult($button) { var cursorClass = 'ijg-check' var highlightClass = 'isHighlighted' $button.addClass(cursorClass).addClass(highlightClass) setGreenCheckCursor() window.setTimeout(function() { $button.removeClass(highlightClass) }, 50) window.setTimeout(function() { $button.removeClass(cursorClass) }, 1500) } function refreshNowAndLater() { window.dispatchEvent(new Event('resize')); window.setTimeout(function() { window.dispatchEvent(new Event('resize')); }, 500); } ;(function addStyles () { var modalStyle = '.workitem-dialog { \ left: 10px !important;\ top: 10px !important;\ width: calc(100% - 20px) !important;\ height: calc(100% - 20px) !important;\ border: 4px solid grey;\ box-shadow: gray 0 0 30px 8px;\ box-sizing: border-box;\ }' var innerModalStyle = '.work-item-view {\ width: calc(100% - 20px);\ margin: 0 10px;\ top: 68px;\ }' var uiDialogContentStyle = '.ui-dialog-content:not(.modal-dialog) {height: calc(100% - 51px) !important}' var otherStyles = '.' + topClass + ' table.witform-layout {\ width: calc(100% - 4px);\ }\ .linksPanel {\ display: block !important;\ margin-bottom: 5px;\ }\ .linksPanelHeader {\ background-color: #e6e6e6;\ font-size: 11px;\ text-transform: uppercase;\ margin: 0;\ padding: 0 4px 0;\ border: 0;\ white-space: nowrap;\ height: 25px;\ line-height: 2.1;\ }\ .tbTile {\ width: 100%;\ margin: 0px 0px 3px;\ }\ .subColumn { \ width: calc(50% - 5px); \ margin-right: 5px; \ }\ .linksPanel .grid-cell:not(:only-child) {\ text-indent: 0 !important;\ }\ button {\ transition: box-shadow 100ms;\ }\ button:focus {\ background-color: #f8f8f8;\ box-shadow: 0px 0px 0px 3px rgba(128, 128, 128, 0.4);\ }\ button:hover {\ background-color: #fefefe;\ }\ button:active {\ background-color: #e6e6e6;\ }\ button.changeset-identifier {\ vertical-align: top;\ line-height: 0;\ padding: 0px 12px;\ height: 22px;\ margin-left: 8px;\ }\ .agile-content-container div.board-tile.ui-draggable,\ #taskboard-table-body .ui-draggable {\ transition: box-shadow 250ms;\ }\ .agile-content-container div.board-tile.ui-draggable:focus,\ #taskboard-table-body .ui-draggable:focus {\ box-shadow: 0px 0px 8px 2px rgb(25, 22, 6);\ transition-delay: 50ms;\ outline: none;\ }\ .ui-tabs-panel[rawtitle="Description"] .workitemcontrol > div, \ .ui-tabs-panel[rawtitle="Description"] .workitemcontrol > div > .richeditor-container, \ [rawtitle="Steps to Reproduce"] .workitemcontrol > div, \ [rawtitle="Steps to Reproduce"] .workitemcontrol > div > .richeditor-container {\ height: auto !important;\ }\ .ui-tabs-panel .richeditor-editarea {\ position: relative;\ }\ .ui-tabs-panel .richeditor-editarea iframe {\ resize: vertical;\ }\ .witform-layout > tbody > tr.group {\ width: calc(100% - 4px);\ }\ .link-dialog-form .text {\ text-shadow: 0px 0px 6px black;\ height: auto;\ }\ .workitem-control-maximized-dialog {\ height: calc(100% - 20px) !important;\ width: calc(100% - 20px) !important;\ }\ .taskboard-parent {\ min-width: 154px;\ width: 154px;\ }\ .taskboardTableHeaderScrollContainer .taskboard-parent {\ min-width: 164px;\ }\ .ijg-check {\ cursor: ' + cursorUrl + ';\ }\ .workitem-info-bar .info-text-wrapper{\ overflow: visible !important;\ }\ .workitem-dialog .ui-dialog-titlebar-progress-container {\ margin: 0 !important;\ }\ .workitem-dialog .ui-resizable-handle {\ display: none !important;\ }\ \ .ui-dialog .ui-dialog-titlebar-close {\ height: calc(100% + 1px);\ transition: background-color 150ms;\ }\ .ui-dialog .ui-dialog-titlebar-close:hover {\ background-color: rgba(232, 129, 129, 0.5) !important;\ }\ span.ui-button-icon-primary.ui-icon.ui-icon-closethick {\ font-size: 20px;\ top: -2px;\ height: auto;\ background-image: initial !important;\ text-indent: initial;\ }\ span.ui-button-icon-primary.ui-icon.ui-icon-closethick:after {\ content: "×";\ }\ \ .ijg-copyButtons {\ display: inline-block;\ font-size: 14px;\ margin-top: -2px;\ margin-bottom: -5px;\ }\ button.ijg-copyButton {\ margin-left: 16px;\ transition: box-shadow 100ms, background-color 250ms 100ms linear;\ transform: translateY(-2px);\ }\ .ijg-copyButton.isHighlighted {\ transition-delay: 0s;\ transition-duration: 0s;\ background-color: rgba(160, 232, 151, 0.6);\ }' var verticalCompactionStyles = '\ .work-item-view legend {\ display: none;\ }\ div.tfs-tags {\ margin-bottom: -4px !important;\ margin-top: -8px;\ }\ .toolbar {\ border: none;\ margin-top: -8px;\ }\ .toolbar .menu-bar > .menu-item, .workitemcontrol .menu-bar > .menu-item {\ padding: 6px 8px;\ }\ .toolbar {\ height: 83px;\ }\ .linksPanel .grid {\ height: 250px !important;\ margin-top: -12px;\ }\ .ui-dialog .ui-dialog-buttonpane button {\ margin: 0.3em .4em 0.3em 0;\ }' GM_addStyle("." + topClass + " " + modalStyle) GM_addStyle("." + topClass + " " + innerModalStyle) GM_addStyle("." + topClass + " " + uiDialogContentStyle) GM_addStyle(otherStyles) GM_addStyle(verticalCompactionStyles) })() function waitForKeyElements( // CC BY-NC-SA 4.0. Author: BrockA selectorTxt, // Required: The jQuery selector string that specifies the desired element(s). actionFunction, // Required: The code to run when elements are found. It is passed a jNode to the matched element. bWaitOnce, // Optional: If false, will continue to scan for new elements even after the first match is found. iframeSelector // Optional: If set, identifies the iframe to search. ) { var targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents() .find(selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; /*--- Found target node(s). Go through each and act if they are new. */ targetNodes.each(function() { var jThis = $(this); var alreadyFound = jThis.data('alreadyFound') || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction(jThis); if (cancelFound) btargetsFound = false; else jThis.data('alreadyFound', true); } }); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace(/[^\w]/g, "_"); var timeControl = controlObj[controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval(timeControl); delete controlObj[controlKey] } else { //--- Set a timer, if needed. if (!timeControl) { timeControl = setInterval(function() { waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector ); }, 300 ); controlObj[controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; } function setGreenCheckCursor() { /// from https://bugs.chromium.org/p/chromium/issues/detail?id=26723#c87 if (document.body.style.cursor != cursorUrl) { var wkch = document.createElement("div"); wkch.style.overflow = "hidden"; wkch.style.position = "absolute"; wkch.style.left = "0px"; wkch.style.top = "0px"; wkch.style.width = "100%"; wkch.style.height = "100%"; var wkch2 = document.createElement("div"); wkch2.style.width = "200%"; wkch2.style.height = "200%"; wkch.appendChild(wkch2); document.body.appendChild(wkch); document.body.style.cursor = cursorUrl; wkch.scrollLeft = 1; wkch.scrollLeft = 0; document.body.removeChild(wkch); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址