您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Changeset reference utilities
当前为
// ==UserScript== // @name TFS Changeset History Helper // @namespace http://jonas.ninja // @version 1.1.0 // @description Changeset reference utilities // @author @_jnblog // @match http://*/tfs/DefaultCollection/*/_versionControl/* // @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js // @grant GM_addStyle // ==/UserScript== /* jshint -W097 */ /* global $ */ /* jshint asi: true, multistr: true */ 'use strict'; waitForKeyElements('.history-result', doEverything, false) waitForKeyElements(".vc-page-title[title^=Changeset]", addChangesetIdCopyUtilities, true) $(document).on('mouseenter', '.history-result, .ijg-changeset-id', highlightHistoryResult) .on('mouseleave', '.history-result, .ijg-changeset-id', unhighlightHistoryResult) $(document).on('click', 'input.ijg-copy-changeset-id', function clickToCopy(e) { // if shift is held down, copy a commit message var optMessage if (e.ctrlKey) { optMessage = $(this).closest('.history-result').find('.change-link').text() optMessage += ' (merge c' + this.value + ' to QA)' } copy(this, $(this).next('span.ijg-copy-message'), optMessage) }) $(document).on('click', 'input.ijg-copy-changeset-page-link', function clickToCopy(e) { // if shift is held down, copy a commit message var optMessage if (e.ctrlKey) { optMessage = $('.vc-change-summary-comment').text() + ' (merge c' + this.value + ' to QA)' } copy(this, $(this).next('span.ijg-copy-message'), optMessage) }) function doEverything(historyResult) { historyResult = $(historyResult) spanifyText(historyResult) addCopyUtilities(historyResult) createTaskContainers(historyResult) //fetchTaskLinks(historyResult) } function createTaskContainers(historyResult) { // makes a positioned div in the right place to hold Task info var tasks = historyResult.find('.ijg-task-id') if (tasks.size()) { // make a container and append rows var container = $('<div class="ijg-tasks-container">') historyResult.find('.change-link-container').append(container) tasks.each(function() { var tasknum = $(this).data('ijgTaskId') var task = $('<div class=ijg-task-link>').data('ijgTaskId', tasknum) var link = $('<a target="_blank">') .text(tasknum) .prop('href', 'http://tfs.sqlsentry.com:8080/tfs/DefaultCollection/SQLSentryWebsite/_workitems/edit/'+tasknum) task.append(link) container.append(task) }) } } function fetchTaskLinks(historyResult) { var base = window.location.origin + window.location.pathname.match(/^\/(.*?)\/(.*?)\//)[0] var urls = { changesetLinkedWorkItems: '_apis/tfvc/changesets/{}/workItems', changesetInfo: '_apis/tfvc/changesets/{}', apiVersion: '?api-version=1.0' } } function spanifyText(historyResult) { // wraps changeset/task IDs with spans so they can be targeted individually // adds data to the newly-created spans historyResult.find('.change-link').each(function() { // commit messages may have either Tasks or Changesets $(this).html($(this).text().replace(/[ct]\d{3,}/gi, function(match) { var id = match.replace(/[ct]/gi, '') if (match.startsWith('t')) { return '<span class="ijg-task-id" data-ijg-task-id="' + id + '">' + match + '</span>' } return '<span class="ijg-changeset-id" data-ijg-changeset-id="' + id + '">' + match + '</span>' })) }) historyResult.find('.change-info').each(function() { // '.history-result's will only have changesets, and they will not be prefixed with 'c' $(this).html($(this).text().replace(/\d{3,}/gi, function(match) { var changesetId = match.replace(/c/i, '') return '<span class="ijg-changeset-id" data-ijg-changeset-id="' + changesetId + '">' + match + '</span>' })) }) } function addCopyUtilities(historyResult) { // adds a text field for each changeset for easy copying of the changeset id var changesetId = historyResult.find('.change-info').prop('title').match(/^\d{3,6}/)[0] historyResult.find('.result-details') .before($('<td class=ijg-copy-changeset-id-container><input type=text class=ijg-copy-changeset-id data-ijg-changeset-id="'+changesetId+'" value="'+changesetId+'"><span class="ijg-copy-message"></td>')) } function addChangesetIdCopyUtilities (pageTitle) { var $pageTitle = $(pageTitle) if ($pageTitle.hasClass('added')) { return } $pageTitle.addClass('added') var id = $pageTitle.text().replace('Changeset ', '') var $copyLinkInput = $('<input value="' + id + '">').addClass('ijg-copy-changeset-page-link') $pageTitle.after($('<span>').addClass('copy-message')).after($copyLinkInput) } function highlightHistoryResult(e) { var changesetId = $(this).data('ijgChangesetId') changesetId = changesetId || $(this).find('.ijg-copy-changeset-id').data('ijgChangesetId') var mainHistoryResult = $('.result-details .change-info .ijg-changeset-id[data-ijg-changeset-id='+changesetId+']').closest('.history-result') var matchingChangesets = $('span.ijg-changeset-id[data-ijg-changeset-id="'+changesetId+'"]') if (matchingChangesets.size() > 1) { matchingChangesets.each(function() { var matchingChangesetId = $(this) matchingChangesetId.css('color', 'red').closest('.history-result').css('background-color', 'beige') }) mainHistoryResult.css('background-color', '#D1D1A9') } } function unhighlightHistoryResult(e) { $('span.ijg-changeset-id').css('color', '').closest('.history-result').css('background-color', '') } var styles = '\ span.ijg-changeset-id { \ border-bottom: 1px dotted #ccc; \ } \ div > span.ijg-changeset-id { \ cursor: default; \ } \ td.ijg-copy-changeset-id-container { \ width: 52px; \ vertical-align: top; \ padding: 5px 7px 0 0; \ } \ input.ijg-copy-changeset-id { \ cursor: pointer; \ width: 50px; \ text-align: center; \ border: 1px solid #ddd; \ padding: 3px 0; \ } \ input.ijg-copy-changeset-page-link {\ cursor: pointer;\ text-align: center;\ width: 80px;\ margin: 0 16px;\ border: 1px solid #ccc;\ vertical-align: middle; \ }\ span.ijg-copy-message { \ font-size: .75em; \ display: block; \ text-align: center; \ }\ .change-link-container { \ position: relative;\ display: inline-block; \ }\ .ijg-tasks-container {\ position: absolute; \ top: 0; \ right: 0;\ transform: translateX(100%);\ padding-left: 20px;\ }' GM_addStyle(styles) function copy(elToCopy, $messageContainer, optMessage) { var $fakeElem = $('<textarea>'); var succeeded var message = optMessage || 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/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAA7klEQVRIS7WUaw0CQQwG5xSAA5CAEyyAAywgAQfgAAeAAyQghXyXLGlgHy132+R+NTvT22470DmGznx6CDbAMxU+p2AJ3AAJzsBekrkEFp6KHyVzCHLwj2SqoAYfJVMETThwtAI1Zws8gHvj+XrgF2CXBILrBeigQi9ATcqFG56u6BueoDlJCJ4EL2BVqNZKwnCPIF3X1QxRqT3jnX8n1QNdkZq6qDRWf7mu5LNw+0w9klDluV30j6RYeWnZRSRNeGmSPRIXvLYqahI3vLWLcpIQvCVQ3kpOwKGxo37Snm2qCdanWQiHRxCG2gPdBW+zDC83C5E8rQAAAABJRU5ErkJggg==), auto') } 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) } 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或关注我们的公众号极客氢云获取最新地址