Jira Annoying Guy

Jira custom options

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name       Jira Annoying Guy
// @namespace  http://ciandt.com/
// @version    0.5.2
// @description  Jira custom options
// @match      http*://jiracloud.cit.com.br/secure/RapidBoard.jspa?rapidView=7215*
// @copyright  rafaelh
// ==/UserScript==

/*Change log
0.5.2 - 08/08/2014
Match url fix

0.5.1 - 28/07/2014
Menu bar fix

0.5.0 - 21/07/2014
Warning messages
Review story warning
Already assigned tasks warning

0.4.4 - 18/07/2014
Another refresh screen fix try

0.4.3 - 11/07/2014
Refresh screen fix

0.4.1 - 07/07/2014
Refactors, Constants
Bug when trying to attach file fixed

0.4.0 - 03/07/2014
When moving tasks to IN PROGRESS or IN CODE REVIEW, check if there is already a task assigned for the user

0.3.2 - 03/07/2014
Refresh work view was improved; refresh fails are going to happen less frequently

0.3.1 - 01/07/2014
Backlog column missing fix

0.3.0 - 30/06/2014
Task grab events - refreshing is still improvised

0.2 - 30/06/2014
ScrollTo error message

0.1 - 28/06/2014
Create Issue - Bug - Environment
Not working if issue is created in a new tab
*/

//Constants
var Constants = {
    ISSUE_TYPE_BUG: ["1", "8"],
    COLUMN_INDEX_IN_PROGRESS: 2,
    COLUMN_INDEX_IN_CODE_REVIEW: 4
}

//Utils
var Utils = {
    closeMessage: function() {
        $("#message").fadeOut("slow");
        return false;
    },
    obtainNthIndex: function(str, pat, n){
        var L= str.length, i= -1;
        while(n-- && i++<L){
            i= str.indexOf(pat, i);
        }
        return i;
    },
    parseFieldsBody: function(body, splitter) {
        var obj = {};
        var text = body.split(splitter);
        var field;
        var fieldName;
        var fieldVal;
        text.forEach(function(attr) {
            field = attr.split('=');
            fieldName = field[0];
            fieldVal = field[1];
            obj[fieldName] = fieldVal;
        });
        return obj;
    },
    
    parseReturnUrlFromDocumentUrl: function(documentUrl) {
        var returnUrl = document.URL.substring(this.obtainNthIndex(document.URL, '/', 3), document.URL.length);
        returnUrl = returnUrl.replace(/\?/g,'%3F');
        returnUrl = returnUrl.replace(/\=/g,'%3D');
        returnUrl = returnUrl.replace(/\&/g,'%26');
        return returnUrl;
    },
    
    refreshView: function() {
        setTimeout(function(){ 
        	GH.RefreshHandler.Poller.startPoller(true);
        }, 3500);
    },
    
    removeInsertedErrorMessages: function() {
        $('#inserted-error-message').remove();
    },
    
    addErrorAndScrollTo: function(fieldText, fieldId) {
        var errorMsg = '<div id="inserted-error-message" style="color:red; font-weight: bold;">You must specify '+fieldText+' of the issue.</div>';
        $(fieldId+'-wiki-edit').append(errorMsg);
        var scrollPosition = 0;
        $('.form-body')[0].scrollTop = scrollPosition;
        scrollPosition = $(fieldId).position().top - 5;
        $('.form-body')[0].scrollTop = scrollPosition;  
    },
    
};

//Top Message
var TopMessage = {
    addGlobalStyle: function(css) {
        var head, style;
        head = document.getElementsByTagName('head')[0];
        if (!head) { return; }
        style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = css;
        head.appendChild(style);
    },
    
    closeMessage: function() {
        $("#message").fadeOut("slow");
        return false;
    },
    
    init: function() {
     	this.addGlobalStyle(
            '#message {'
            +'    font-family:Arial,Helvetica,sans-serif;'
            +'    position:fixed;'
            +'    top:0px;'
            +'    left:0px;'
            +'    width:100%;'
            +'    z-index:105;'
            +'    text-align:center;'
            +'    font-weight:bold;'
            +'    font-size:100%;'
            +'    color:white;'
            +'    padding:10px 0px 10px 0px;'
            +'    background-color:#8E1609;'
            +'}'
            +'#message span {'
            +'    text-align: center;'
            +'    width: 95%;'
            +'    float:left;'
            +'}'
            +'.close-notify {'
            +'    white-space: nowrap;'
            +'    float:right;'
            +'    margin-right:10px;'
            +'    color:#fff;'
            +'    text-decoration:none;'
            +'    border:2px #fff solid;'
            +'    padding-left:3px;'
            +'    padding-right:3px'
            +'} '
        );   
        var messageDiv = document.createElement('div');
        messageDiv.id = 'message';
        messageDiv.style.cssText = 'display:none;';
        messageDiv.appendChild(document.createElement('span'));
        var button = document.createElement('button');
        button.classList.add('close-notify');
        button.id = 'close-notify-button';
        button.textContent = 'X';
        messageDiv.appendChild(button);
        document.body.appendChild(messageDiv);
        document.querySelector('#close-notify-button').addEventListener("click", this.closeMessage, false);
    }     
}
TopMessage.init();

//Overwriting .send
XMLHttpRequest.prototype.reallySend = XMLHttpRequest.prototype.send; 
XMLHttpRequest.prototype.send = function(body) { 
    if (body != null) { 
        //If .send body is a string (i.e. not a file)
        if (typeof body == 'string' || body instanceof String) {
            var obj = Utils.parseFieldsBody(body, '&');
            //Create-issue
            if (obj.isCreateIssue) {
                Utils.removeInsertedErrorMessages();
                //Bug
                if (Constants.ISSUE_TYPE_BUG.indexOf(obj.issuetype)!=-1) {
                    //Environment
                    if (obj.environment == '') {
                        Utils.addErrorAndScrollTo('an environment', '#environment');
                        this.abort();
                    }
                }
            }
        }
    }
    this.reallySend(body);
};

function checkIfColumnHasAssignedTasks(args, column, index, task) {
    var taskAssignee = $(task).find('.ghx-avatar img').attr('alt');
    var user = $('meta[name="ajs-remote-user-fullname"]').attr('content');
    if (taskAssignee == user) {
        args.alreadyAssignedToTask = true;
        args.alreadyAssignedTaskColumn = column;
        args.alreadyAssignedTaskIndex = index;
    }
    return args;
}

function checkIfUserHasAssignedTasks() {    
    var $allTasksInProgress = $('ul li:nth-child('+Constants.COLUMN_INDEX_IN_PROGRESS+') .ghx-has-avatar');
    var $allTasksInCodeReview = $('ul li:nth-child('+Constants.COLUMN_INDEX_IN_CODE_REVIEW+') .ghx-has-avatar');
    var args = {
        alreadyAssignedTaskColumn: null,
        alreadyAssignedTaskIndex: null,
        alreadyAssignedToTask: false
    };
    $allTasksInProgress.each( function(index, task) {
        args = checkIfColumnHasAssignedTasks(args, Constants.COLUMN_INDEX_IN_PROGRESS, index, task);
    });
    $allTasksInCodeReview.each( function(index, task) {;
        args = checkIfColumnHasAssignedTasks(args, Constants.COLUMN_INDEX_IN_CODE_REVIEW, index, task);
    });
    if (args.alreadyAssignedToTask) {
        $('#message span').text('Warning: You already have a task assigned to you!');
        $('#message').fadeIn('slow');
        window.scrollTo(0, $($('ul li:nth-child('+args.alreadyAssignedTaskColumn+') .ghx-has-avatar')[args.alreadyAssignedTaskIndex]).offset().top - 200);
    }
}
//Overwriting .open
XMLHttpRequest.prototype.reallyOpen = XMLHttpRequest.prototype.open; 
XMLHttpRequest.prototype.open = function() {
    var abortedRequest = false;
    var method = this.open.arguments[0];
    var url = this.open.arguments[1];
    var async = this.open.arguments[2];
    //GET
    if (method=="GET") {
        //Parsing url
        var parsedUrl = url.split('?');
        var file = parsedUrl[0];
        var body = parsedUrl[1];
        var fileFormat = file.substring(file.lastIndexOf('.')+1);
        var fileType = file.substring(file.lastIndexOf('/')+1, file.lastIndexOf('.'));
        var obj = Utils.parseFieldsBody(body, '&');
        //JSPA
        if (fileFormat == "jspa") {
            //WorkflowUIDispatcher
            if (fileType == "WorkflowUIDispatcher") {     
                //When moved to IN PROGRESS(41) and IN CODE REVIEW(61) columns
                if ((obj.action==41)||(obj.action==61)) {
                    checkIfUserHasAssignedTasks();
                    //Assign
                    this.onload = function() {
                        xmlHttp = new XMLHttpRequest();
                        xmlHttp.open("POST", "/secure/AssignIssue.jspa", true);
                        //Refresh
                        xmlHttp.onload = function(){
                            Utils.refreshView();
                        };
                        xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                        var returnUrl = Utils.parseReturnUrlFromDocumentUrl(document.URL);
                        xmlHttp.send("inline=true&decorator=dialog&returnUrl="+returnUrl+"&id="+obj.id+"&assignee="+$("meta[name='ajs-remote-user']").attr('content')+"&comment=&commentLevel=&atl_token="+$("meta[name='atlassian-token']").attr('content'));
                    };    
                }
                //When moved to TO DO(backlog (81), development(31)) and AWAITING CODE REVIEW(51) columns
                if ((obj.action==81)||(obj.action==31)||(obj.action==51)) {
                    //Unassign
                    this.onload = function() { 
                        setTimeout( function() {
                            $('[data-issue-id = '+obj.id+']').attr('class', $('[data-issue-id = '+obj.id+']').attr('class').replace('ghx-has-avatar ', ''));
                        }, 1000);
                        xmlHttp = new XMLHttpRequest();
                        xmlHttp.open("POST", "/secure/AssignIssue.jspa", true);
                        //Refresh
                        xmlHttp.onload = function(){
                            Utils.refreshView();
                        };
                        xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                        var returnUrl = Utils.parseReturnUrlFromDocumentUrl(document.URL);
                        xmlHttp.send("inline=true&decorator=dialog&returnUrl="+returnUrl+"&id="+obj.id+"&assignee=&comment=&commentLevel=&atl_token="+$("meta[name='atlassian-token']").attr('content'));
                    };
                }
            }
        }
        //JSON
        if (fileFormat == "json") {
            //subtasksInFinalColumn
            if (fileType == "subtasksInFinalColumn") {
                this.onload = function() {
                    var parsedResponse = JSON.parse(this.response);
                    if (parsedResponse.subtasksInFinalColumn) {
                        $('#message span').text('You have just closed a story, please remember to review it!');
                    	$('#message').fadeIn('slow');
                    }
                }
            }
        }
    }
    this.reallyOpen(method, url, async);
};