TFS Helper

Adds styles and moves things around so that oft-used functions are easier

目前為 2016-03-15 提交的版本,檢視 最新版本

// ==UserScript==
// @name         TFS Helper
// @namespace    http://jonas.ninja
// @version      1.4.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.11.3/jquery.min.js
// @grant        GM_addStyle
// ==/UserScript==
/* jshint -W097 */
/* global $ */
/* 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)
    }, 1000)
  }
})



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 column2table = $(linksPane).closest('table.content')
  if (column2table.width() < 882) {
    // put att tabPanels in a single column
    column2table.parent().siblings().append(column2table).css('width', '100%').children().css('overflow', 'hidden')
    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 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;\
}'

GM_addStyle("." + topClass + " " + modalStyle)
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或关注我们的公众号极客氢云获取最新地址