您需要先安装一个扩展,例如 篡改猴、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.5.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* // @require https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js // @grant GM_addStyle // ==/UserScript== /* jshint -W097 */ /* global $, GM_addStyle */ /* jshint asi: true, multistr: true */ 'use strict' var topClass = "makeTfsNotAwful" $('body').addClass(topClass) function doEverything(linksPane) { if ($(linksPane).data('moved')) { return } showLinksPane(linksPane) stackAllTabs(linksPane) window.setTimeout(function() { addTaskIdCopyUtilities(linksPane) changeDialogBorderColor(linksPane) }, 250) } $(document).on('click', 'input.task-identifier', function clickToCopy(e) { copy(this, $(this).next('span.copy-message')) if (e.ctrlKey) { // if CTRL is held down, copy both the commit ID and a commit message (useful if you have a clipboard manager like Ditto) var optMessage = 't' + (this.value) + ' ' + $(this).siblings('.info-text').text().replace(/^dev: /i, "") var that = this window.setTimeout(function() { copy(that, $(that).next('span.copy-message'), optMessage) }, 250) } }) 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', '30%') 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 { // Task dialog.css('border-color', '#E0C252') // yellow } } function stackAllTabs(linksPane) { var column2 = $(linksPane).closest('table.content').parent('.column') if (Math.max(window.innerWidth, document.documentElement.clientWidth) >= 1600) return; column2.add(column2.prev()).css({width: '100%', display: 'block'}) window.dispatchEvent(new Event('resize')); } function addTaskIdCopyUtilities(linksPane) { $('.workitem-info-bar').find('.info-text-wrapper').each(function() { var $header = $(this) if ($header.hasClass('added')) { return } $header.addClass('added') var id = $header.find('a.caption').text().match(/\d+/)[0] var $input = $('<input value="' + id + '"/>').addClass('task-identifier') $header.find('span.info-text').after($('<span>').addClass('copy-message')).after($input) }); } function copy(elToCopy, $messageContainer, optionalMessage) { var $fakeElem = $('<textarea>'); var succeeded var message = optionalMessage || elToCopy.value $fakeElem .css({ position: 'absolute', left: '-9999px', top: (window.pageYOffset || document.documentElement.scrollTop) + 'px' }) .attr('readonly', '') .val(message) .appendTo(document.body) select($fakeElem[0]) try { succeeded = document.execCommand('copy'); } catch (err) { succeeded = false; select(elToCopy) } if (succeeded) { $messageContainer.text('Copied!') $(elToCopy).css('cursor', 'url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="415.582" height="415.582" viewBox="0 0 415.582 415.582"><path d="M411.47 96.426l-46.32-46.32c-5.48-5.482-14.37-5.482-19.852 0l-192.95 192.952-82.066-82.064c-5.48-5.482-14.37-5.482-19.85 0l-46.32 46.32c-5.482 5.48-5.482 14.37 0 19.852l138.31 138.31a13.99 13.99 0 0 0 9.927 4.112c3.592 0 7.185-1.37 9.925-4.112l249.195-249.2a14.034 14.034 0 0 0 0-19.85z"/></svg>\')') } else { $messageContainer.text('Press Ctrl+C to copy!') $(elToCopy).css('cursor', 'text') } $fakeElem.remove() $messageContainer.show() window.setTimeout(function() { $messageContainer.fadeOut(500) if (succeeded) { $(elToCopy).css('cursor', 'pointer') } }, 1200) } waitForKeyElements("div.tab-page[rawtitle=Links]", doEverything, false) 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;\ }' var uiDialogContentStyle = '.ui-dialog-content {height: calc(100% - 59px) !important}' var otherStyles = '\ .linksPanel {\ display: block !important;\ }\ .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;\ }\ input.task-identifier {\ cursor: pointer;\ text-align: center;\ width: 80px;\ margin: 0 16px;\ border: 1px solid #ccc;\ }\ .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.changeset-identifier {\ vertical-align: top;\ line-height: 0;\ padding: 0px 12px;\ height: 22px;\ margin-left: 8px;\ }\ .copy-message {\ font-weight: normal;\ }\ .agile-content-container div.board-tile.ui-draggable:focus {\ box-shadow: 0px 0px 10px 2px; \ }\ .ui-tabs-panel[rawtitle="Description"] .workitemcontrol > div, .ui-tabs-panel[rawtitle="Description"] .workitemcontrol > div > .richeditor-container {\ height: auto !important;\ }\ .ui-tabs-panel[rawtitle="Description"] .richeditor-editarea {\ position: relative;\ }\ .ui-tabs-panel[rawtitle="Description"] .richeditor-editarea iframe {\ resize: vertical;\ }\ .witform-layout > tbody > tr.group {\ width: calc(100% - 4px);\ }' GM_addStyle("." + topClass + " " + modalStyle) GM_addStyle("." + topClass + " " + innerModalStyle) GM_addStyle("." + topClass + " " + uiDialogContentStyle) GM_addStyle(otherStyles) function select(element) { // MIT licensed. Author: @zenorocha var selectedText; if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { element.focus(); element.setSelectionRange(0, element.value.length); selectedText = element.value; } else { if (element.hasAttribute('contenteditable')) { element.focus(); } var selection = window.getSelection(); var range = document.createRange(); range.selectNodeContents(element); selection.removeAllRanges(); selection.addRange(range); selectedText = selection.toString(); } return selectedText; } 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; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址