austindh / Redmine

// ==UserScript==
// @name       Redmine
// @namespace  
// @version    2.1.1
// @description  Add some extra info and styling to my redmine page
// @match      http://redmine.byu.edu/my/page
// @match      http://redmine.byu.edu/issues/*
// @copyright  2012+, Austin Hughes
// @require http://code.jquery.com/jquery-latest.js
// ==/UserScript==
	
	//At start, get name and username of person logged in, store name in localStorage
	var USERNAME = $( '#loggedas a' ).text();
	//GET to get full name
	var userURL = $( '#loggedas a' ).attr( 'href' );
	//console.log(userURL);
    $.get( "http://redmine.byu.edu" + userURL, function ( data ) {
        var pageContents = $( "<html>" ).html( data );
    	var fullName = pageContents.find( 'h2' );
        fullName = $( fullName ).text().trim();
        var firstName = fullName.split( " " )[0];
        localStorage.RedmineFirstName = firstName;
        localStorage.RedmineFullName = fullName;
    });

	//MY PAGE
	if ( location.href.match( /redmine.byu.edu\/my\/page/ ) ) {
	
        //Section of assigned issues
        var assignedIssues = $( ".issues" );
        var assignedNumbers = $( assignedIssues ).find( ".id" );
    
        //Array to hold bug numbers
        var assignedBugNumbers = [];
        $( assignedNumbers ).each( function () {
            var bugNum = $(this).text().trim();
            assignedBugNumbers.push( bugNum );
        });
        
        localStorage.pageBugs = JSON.stringify( assignedBugNumbers );
        
        //Loop through seenBugs array, and remove any that are not on "my page" anymore
        var pageBugs = assignedBugNumbers;
        if ( !localStorage.seenBugs ) {
        	localStorage.seenBugs = JSON.stringify( [] );
        }
        var seenBugs = JSON.parse( localStorage.seenBugs );
  
        //New columns for Seen checkboxes and Priority
        $( ".issues thead " ).each ( function () {
            $( this ).children().children().eq(0).before( "<th>Seen</th>" );	
            $( this ).children().children().eq(2).after( "<th>Priority</th>" );	
        });
        
        //Priority level classes
        var priorityLevels = [ "OnHold", "Low", "Normal", "High", "Urgent" ];
        					//   1        2        3        4        5
        
        //Status level classes
        var statusLevels = [ "New","Assigned","","PendingReview","StageReady","OnStage","","","ProductionReady","OnProduction","Closed","Feedback","","","","OnHold" ];
        				   //  1       2      3        4               5          6     7  8          9               10          11        12	   13 14 15    16
        
    	//Insert Priority level, status, and unseen class, as needed
        $( '.issue' ).each( function () {
            var priority;
            var bugNum = $(this).attr( 'id' ).split( '-' )[1];
            
            //Duplicate ID as a unique class to each, to check for duplicates (watched bug also assigned to me, for example)
            $( this ).addClass( $(this).attr('id') );
            
            //Unseen class
            var seen = true;
            if ( seenBugs.indexOf( bugNum ) < 0 ) {
            	$( this ).addClass( 'unseen' );
                seen = false;
            }
            
            //Priority level
            for ( var j = 0; j <= 4; j++ ) {
            	var level = j + 1;
                if ( $(this).hasClass( 'priority-' + level ) ) {
                	priority = priorityLevels[j];
                    break;
                }    
            }
            //Insert cells with checkmarks
            if ( seen ) {
            	$( this ).children().eq(0).before( "<td><div align=\"center\" class=\"seenCheck " + bugNum + "\" id=\"" + bugNum + "\">✓</div></td>" );
            } else {
            	$( this ).children().eq(0).before( "<td><div align=\"center\" class=\"seenCheck " + bugNum + "\" id=\"" + bugNum + "\"></div></td>" );
            }
            
            
            //Insert cells for priority
            $( this ).children().eq(2).after( "<td><div class=\"level " + priority + "\">" + priority + "</div></td>" );
           
            //Status level
            for ( var j = 0; j < statusLevels.length; j++ ) {
                var level = j + 1;
                if ( $(this).hasClass( "status-" + level ) ) {
                    status = statusLevels[j];
                    break;
                }
            }
            //Change subject HTML to wrap status in <span>
            var id = $( this ).attr( 'id' );
            var subjectHtml = $( '#' + id + " .subject" ).html();
            var statusText = subjectHtml.split( "</a>" )[1];
            var newText = "<span class = \"statusLevel " + status + "\">" + statusText + "</span>";
           	$( '#' + id + " .subject" ).html( subjectHtml.replace( statusText, newText ) );
            
        });
        
        //Click event for "Seen" checkboxes
        $( document.body ).on( 'click', '.seenCheck', function ( e ) {
            e.stopPropagation();
            
            var seenBugs = JSON.parse( localStorage.seenBugs );
            var bugNumber =  $( e.currentTarget ).attr( 'id' );
            var index = seenBugs.indexOf( bugNumber );

            if ( index === -1 ) { //need to add
                seenBugs.push( bugNumber );
                $( '.' + bugNumber ).each( function () {
                    $( this ).html( '✓' );
                });
                $( '.issue-' + bugNumber ).each( function () {
                    $( this ).removeClass( 'unseen' );
                });
            } else { //need to remove
                seenBugs.splice( index, 1 );
                $( '.' + bugNumber ).each( function () {
                    $( this ).html( '' );
                });
                $( '.issue-' + bugNumber ).each( function () {
                    $( this ).addClass( 'unseen' );
                });
            }
            localStorage.seenBugs = JSON.stringify( seenBugs );

        });  
        
        //Check for duplicates
        $( '.issue' ).each( function () {
        	var id = $(this).attr( 'id' );
            var length = $( '.' + id ).length;
            if ( length > 1 ) {
                for ( var i = 0; i < length; i++ ) {
                	var issue = $( '.' + id )[i];
                    var edge = $( issue ).children()[0];
                    var number = $( issue ).children()[1];
                    //console.log(issue, number);
                    $( edge ).addClass( 'duplicate' );
                    $( number ).addClass( 'duplicate2' );
                }
            }
        });
        
        $( ".mypage-box h3" ).each( function () {
            var text = $( this ).text();
            //Hide watched or assigned sections if sections are empty
        	var numIssues = parseFloat( text.match( /[0-9]/ ) ); //note that this will only get the first digit, but we're only checking for a zero, so it doesn't matter
            if ( !numIssues ) {
            	$(this).parent().addClass( "nodata" );
            }
            
            var numBugsAssigned = 0;
            if ( text.match(/issues assigned to me/gi) ) {
            	numBugsAssigned = parseInt( text.split("(")[1] );
            }
            
            ////////////
            //If more than 10 assigned bugs, load rest and show on my page
            //////////
            if ( numBugsAssigned > 10 ) {
            	//Grab all issues URL
                var URL = $( this ).parent().find( ".small a" ).attr( "href" );
                //Get all issues through ajax call
                $.ajax({
                    url: URL,
                    success: function ( data ) {
                        var pageContents = $( "<html>" ).html( data );
                        //Organize data into object, then add info for missing bugs to my page
                        var assignedBugs = {};
                        
                        var issues = pageContents.find( '.issue' );
                        $( issues ).each( function () {
                        	var bugNum = $( this ).find( '.id a').text();
                            var bugNumCell = $( this ).find( '.id' ).html();
                            var projectCell = $( this ).find( '.project' ).html();
                            var trackerCell = $( this ).find( '.tracker' ).html();
                            var priority = $( this ).find( '.priority' ).text();
                            var subjectCell = $( this ).find( '.subject' ).html();
                            var status = $( this ).find( '.status' ).text();
                            
                            assignedBugs[bugNum] = {};
                            assignedBugs[bugNum].bugNumCell = bugNumCell;
                            assignedBugs[bugNum].projectCell = projectCell;
                            assignedBugs[bugNum].trackerCell = trackerCell;            
                            assignedBugs[bugNum].priority = priority;
                            assignedBugs[bugNum].subjectCell = subjectCell;
                            assignedBugs[bugNum].status = status;
                        });
                        
                        //Loop through bugs on page to see which ones need to be added
                        var bugsToAdd = [];
                        for ( var bugNum in assignedBugs ) {
                            if ( assignedBugNumbers.indexOf( bugNum ) === -1 ) {
                            	bugsToAdd.push( bugNum );
                            }
                        }
                        
                        //Add needed bugs to page
                        for ( var i = 0; i < bugsToAdd.length; i++ ) {
                        	var num = bugsToAdd[i];
                            var row = 'even';
                            if ( i % 2 === 0 ) {
                            	row = 'odd';
                            }
                            var seen = "";
                            var seenClass = " unseen";
                            if ( seenBugs.indexOf( num ) !== -1 ) {
                            	seen = "✓";
                                seenClass = "";
                            }
                            //Construct row HTML, then insert into table
                            var HTML = '<tr id="issue-' + num + '" class="hascontextmenu ' + row + ' issue assigned-to-me issue-' + num + '' + seenClass + '">' +
                               '<td><div align="center" class="seenCheck ' + num + '" id="' + num + '">' + seen + '</div></td>' +
                               '<td class="id">' + assignedBugs[num].bugNumCell + '</td>' +
                                '<td class="project">' + assignedBugs[num].projectCell + '</td>' +
                                '<td><div class="level ' + assignedBugs[num].priority + '">' + assignedBugs[num].priority + '</div></td>' +
                                '<td class="tracker">' + assignedBugs[num].trackerCell + '</td>' +
                                '<td class="subject">' + assignedBugs[num].subjectCell + '<span class="statusLevel ' + assignedBugs[num].status + '"> (' + assignedBugs[num].status + ')</span></td>' +
                                '</tr>';
                            
                            //Refind our issues box to get last row to insert after
                            var issuesBox;
                            $( ".mypage-box h3" ).each( function () {
                                if ( text.match(/issues assigned to me/gi) ) {
                                    issuesBox = $( this ).parent().find( 'tbody tr:last' );
                                }
                            });
                            issuesBox.after( HTML );
                        }
                        
                    },
                    error: function () {
                        //Nothing for now  
                    }
                });
            }
            
        });
        
    }
	

	//INDIVIDUAL BUG PAGE
    if ( location.href.match( /redmine.byu.edu\/issues/ ) ) {
		var bugNumber = location.href.split( "/" );
        bugNumber = bugNumber[ bugNumber.length - 1 ];
        
        //Add checkbox for seen/unseen/etc.
        var checkBoxHtml = "<span class=\"check\"><input type=\"checkbox\"><span id=\"seenText\">Mark as Seen</span></span>";
        $( "h2" ).append( checkBoxHtml );
        //Check if bug is seen or not
        var pageBugs = JSON.parse( localStorage.pageBugs );
        var seenBugs = JSON.parse( localStorage.seenBugs );
        
        var seen = seenBugs.indexOf( bugNumber ) > -1;
        if ( seen ) {
            $( ":checkbox" ).attr( "checked", true );
            $( "#seenText" ).text( "Seen" );
            $( "#seenText" ).addClass( "seen" );
        }
        
        //Click event for "Seen" checkbox
        $( ":checkbox" ).on( "click", function () {
            setTimeout( function () {
            	if ( $( ":checkbox" ).is( ":checked" ) ) {
                    if ( seenBugs.indexOf( bugNumber ) < 0 ) {
                        $( "#seenText" ).text( "Seen" );
                        $( "#seenText" ).addClass( "seen" );
                    	seenBugs.push( bugNumber );
                    	localStorage.seenBugs = JSON.stringify( seenBugs );
                    } 
                } else { //remove from seen list
                	var index = seenBugs.indexOf( bugNumber );
                    if ( index > -1 ) {
                        $( "#seenText" ).text( "Mark as Seen" );
                        $( "#seenText" ).removeClass( "seen" );
                    	seenBugs.splice( index, 1 );
                        localStorage.seenBugs = JSON.stringify( seenBugs );
                    }
                }
            }, 100);
            
        });
        
        //Priority classes
        priority = $( "td.priority" ).text().replace( " ", "" );
		$( "td.priority" ).wrapInner( "<div class=\'bugPageLevel " + priority + "'</div>" );
        
        //Status classes
        var status = $( "td.status" )[0];
        var status1 = $( status )[0];
        status1 = $( status1 ).text().replace( " ", "" );
		$( status ).wrapInner( "<div class=\'bugPageLevel " + status1 + "'</div>" );
        
        //This is for any child-bugs that show up the same page
        var otherStatus = $( "tr td:nth-child(3)" );
        $( otherStatus ).each( function () { 
        	var status1 = $(this).text().replace( " ", "" );
            $(this).wrapInner( "<div class=\'bugPageLevel " + status1 + "'</div>" );
        });
        
        //Change any URLs into hyperlinks
        $( "tbody tr td" ).each( function () {
            var text = $( this ).html();
            //console.log(text);
            if ( text.match( /http:\/\/.*/ ) ) {
                //Add word wrapping to parent
                $( this ).parent().attr("style", "word-wrap: break-word");
                var array;
                var text1;
                if ( array = /\(?(http:\/\/.*?)\)/gi.exec( text ) ) { //For the case of a URL in parentheses
                	text1 = array[1];
                } else {
                	text1 = text.split("<")[0];
                }
                text = text.replace( text1, "<a class=\"redHover\" href=\"" + text1 + "\">" + text1 + "</a>" )
            	$( this ).html( text );
            }
        });

        //Style steps to reproduce and description box, and Change environment URL into hyperlink
        var tableRows = $( "table.attributes tr" );
        var desc = $( '.issue div.wiki' );
        var html = $( desc ).html();
        var DESC_WIDTH = 0;
        $( desc ).html( '<div class=\"descriptionBox\">' + html + "</div>" );
        $( tableRows ).each( function () {
            if ( $(this).find( 'th' ).text().match( /steps to reproduce/gi ) ) {
                //console.log("hey");
            	var details = $( this ).find( 'td' )[0];
                var detailText = $( details ).text();
                if ( detailText.length > 0 ) {
                    $( details ).wrapInner( '<div class=\"reproduce descriptionBox\"></div>' );
                    var width1 = $( '.reproduce' ).parent().siblings().width(); // grabs witdh of "Steps To Reproduce"
                    var width2 = $( '.reproduce' ).parent().width(); // grabs width of parent cell
                    DESC_WIDTH = width1 + width2;
                    if ( DESC_WIDTH > 350 ){ DESC_WIDTH = 350; }
                }
            }
            else if ( $(this).find( 'th' ).text().match( /environment/gi ) ) {
            	var environment = $( this ).find( 'td' )[1];
                //console.log(environment);
                var enviroURL = $( environment ).text();
                //console.log( enviroURL );
                if ( enviroURL.length > 0 ) {
                    var URL = /\((.*?)\)/gi.exec( enviroURL );
                    enviroURL = enviroURL.replace( URL[1], "<a href=\"http://" + URL[1] + "\">" + URL[1] + "</a>" )
                    $( environment ).html( enviroURL );
                }    
            }
        });
        //console.log($( '.issue div.wiki' ).html());
        
        //Look for instructor netID, student netID, course ID, and add classes to make them bold
        $( '.descriptionBox' ).each( function () {
        	var text = $( this ).html();
            var reg = />instructor(?: id)?(?: |:)(.[^<]*)/gi;
            var regg = />co(?:-| )instructor(?: id)?:(?: )?(.[^<]*)/gi;
            var reg2 = />student(?:s)?(?: id)?(?: |:)(.[^<]*)/gi;
            var reg3 = />(?:course|class[^=])(?: name| id)?:?(?: )?(.[^<]*)/gi;
            if ( !text.match(reg) ) {
                reg = /(?:net )?id(?:s)?:(?: )?(.[^<]*)/gi;
            }
            if ( text.match(reg) ) {
            	var netID = reg.exec( text )[1];
                if ( !netID.match( /\bn(?:\/)?a\b/gi ) ) { //Don't match "n/a" or N/A"
                    $( this ).html( text.replace( netID, "<span class=\"netID\">" + netID.toLowerCase() + "</span>" ) );
                    text = $( this ).html();
                }
            }
            if ( text.match(regg) ) {
            	var netID = regg.exec( text )[1];
                if ( !netID.match( /\bn(?:\/)?a\b/gi ) ) { //Don't match "n/a" or N/A"
                    $( this ).html( text.replace( netID, "<span class=\"netID\">" + netID.toLowerCase() + "</span>" ) );
                    text = $( this ).html();
                }
            }
            //console.log($( '.issue div.wiki' ).html());
            if ( text.match(reg2) ) {
            	var netID = reg2.exec( text )[1];
                if ( !netID.match( /\bn(?:\/)?a\b/gi ) ) { //Don't match "n/a" or N/A"
                	$( this ).html( text.replace( netID, "<span class=\"netID\">" + netID.toLowerCase() + "</span>" ) );
                    text = $( this ).html();
                }
            }
            if ( text.match(reg3) ) {
            	var courseID = reg3.exec( text )[1];
                if ( !courseID.match( /must be/gi ) && !courseID.match( /\bn(?:\/)?a\b/gi ) && !courseID.match( /copied/gi ) ){
                	$( this ).html( text.replace( courseID, "<span class=\"courseID\">" + courseID + "</span>" ) );
                    text = $( this ).html();
                } 
            }
        });
        
        //Add dates for bug added and most recent update
        //$( '.author' ).children().each( function () {
        //    if ( $(this).attr( 'title' ) ) {
        //    	var tDate = $(this).attr( 'title' );
        //        //Insert date and time after
        //        $(this).after( "<div class='dateAndTime dateTime2'>(" +tDate + ")</div>" );
        //    }
        //});
        
        //Add buttons for random, previous and next bugs
        $( "#content h2" ).append( "<div class=\"button disabled\" id=\"next\">Next</div>" );
        $( "#content h2" ).append( "<div class=\"button disabled\" id = \"prev\">Prev.</div>" );
        $( "#content h2" ).append( "<div class=\"button\" id = \"rand\">Random Bug</div>" );
        
        //Click events for previous and next (go to previous or next bug)
        var nextBug = parseInt( bugNumber ) + 1;
        var prevBug = parseInt( bugNumber ) - 1;
        var BUGS_TO_CHECK = 20; //How many bugs to check if next number isn't used
        
        //Keep track of bugs that we know exists (i.e. that we've visited before)
        if ( !localStorage.existingBugs ) {
            localStorage.existingBugs = JSON.stringify( [] );
        }
        var aaa = JSON.parse( localStorage.existingBugs );
        var bbb = parseInt( bugNumber );
        if ( aaa.indexOf( bbb ) === -1 ) {
        	aaa.push( bbb );
        }
        localStorage.existingBugs = JSON.stringify( aaa );
        
        //Check if previous and/or next bug exists, hide button if it doesn't exist
        //Checker function
        function bugExists ( bugNum, numToCheck, next ) {
            var visitedBugs = JSON.parse( localStorage.existingBugs );
            if ( visitedBugs.indexOf( bugNum ) !== -1 ) { // we know it exists, so just add it
            	 if ( next ) {
                     nextBug = bugNum;
                     $( "#next" ).removeClass( 'disabled' );
                     $( "#next" ).on( "click", function () {
                         location.href = "http://redmine.byu.edu/issues/" + nextBug; 
                     });
                     $( "#next" ).mouseenter( function() {
                         $( "#next" ).text( nextBug );
                     });
                     $( "#next" ).mouseleave( function() {
                         $( "#next" ).text( 'Next' );
                     });
                 } else {
                     prevBug = bugNum;
                     $( "#prev" ).removeClass( 'disabled' );
                     $( "#prev" ).on( "click", function () {
                         location.href = "http://redmine.byu.edu/issues/" + prevBug; 
                     });
                     $( "#prev" ).mouseenter( function() {
                         $( "#prev" ).text( prevBug );
                     });
                     $( "#prev" ).mouseleave( function() {
                         $( "#prev" ).text( 'Prev.' );
                     });
                 }
            } else { //Check if it exists
            	$.ajax({
                    url: "http://redmine.byu.edu/issues/" + bugNum,
                    success: function () {
                        //console.log("yes");
                        var aaa = JSON.parse( localStorage.existingBugs );
                        if ( aaa.indexOf( bugNum ) === -1 ) {
                            aaa.push( bugNum );
                        }	
                        localStorage.existingBugs = JSON.stringify( aaa );
                        if ( next ) {
                            nextBug = bugNum;
                            $( "#next" ).removeClass( 'disabled' );
                            $( "#next" ).on( "click", function () {
                                location.href = "http://redmine.byu.edu/issues/" + nextBug; 
                            });
                            $( "#next" ).mouseenter( function() {
                                $( "#next" ).text( nextBug );
                            });
                            $( "#next" ).mouseleave( function() {
                                $( "#next" ).text( 'Next' );
                            });
                        } else {
                            prevBug = bugNum;
                            $( "#prev" ).removeClass( 'disabled' );
                            $( "#prev" ).on( "click", function () {
                                location.href = "http://redmine.byu.edu/issues/" + prevBug; 
                            });
                            $( "#prev" ).mouseenter( function() {
                                $( "#prev" ).text( prevBug );
                            });
                            $( "#prev" ).mouseleave( function() {
                                $( "#prev" ).text( 'Prev.' );
                            });
                        }
                    },
                    error: function () {
                        //console.log("err");
                        if ( numToCheck > 0 ) {
                            if ( next ) {
                                bugExists( parseInt( bugNum ) + 1, numToCheck - 1, next);
                            } else {
                                bugExists( parseInt( bugNum ) - 1, numToCheck - 1, next);
                            }
                        }
                    }
                });
            }
            
            
        }
        
        //Check next and previous bugs
        bugExists( prevBug, BUGS_TO_CHECK, false );
        bugExists( nextBug, BUGS_TO_CHECK, true );
        
        function randomBug() {
            var min = 1;
            var max = 8000;
            var random = Math.floor( Math.random() * ( max - min + 1 ) + min );
        	$.ajax({
                url: "http://redmine.byu.edu/issues/" + random,
                success: function () {
                    location.href = "http://redmine.byu.edu/issues/" + random;
                },
                error: function () {
                    randomBug();
                }
            });
        }
        $( "#rand" ).on( "click", function () {
            //Go to random bug
            randomBug();
        });
        
        /**
        $( "#rand" ).mouseenter( function() {
            //Flash through random numbers
            changeNumbers = setInterval( function(){
                var min = 1;
                var max = 7000;
            	var random = Math.floor( Math.random() * ( max - min + 1 ) + min );
                $( "#rand" ).text( random );
            }, 150 );
        });
        $( "#rand" ).mouseleave( function() {
            clearInterval( changeNumbers );
            $( "#rand" ).text( 'Random Bug' );
        }); 
        */
        
        //Highlight your name wherever it is on the page
        var nameLoaded = false;
        if ( localStorage.RedmineFullName ) {
            var fullName = localStorage.RedmineFullName;
            var firstName = localStorage.RedmineFirstName;
            nameLoaded = true;
        }
        $( 'td a' ).each( function () {
        	if ( $( this ).text() == fullName && nameLoaded ) {
                 $( this ).wrapInner( "<span class=\"me redHover\"></span>" );
            } else {
            	 $( this ).wrapInner( "<span class=\"redHover\"></span>" );
            }
        });
        $( 'h4 a' ).each( function () {
            if ( $(this).text() == fullName && nameLoaded ) {
            	$(this).wrapInner( "<span class='me'></span>" );
            }
            //Add dates to updates
            if ( $(this).attr( 'title' ) ) {
            	var titleDate = $(this).attr( 'title' );
                //Append to end of parent HTML
                var parent = $(this).parent();
                var parentHtml = $( parent ).html();
                $( parent ).html( parentHtml + " <span class='dateAndTime'>(" +titleDate + ")</span>" );
            }
        });
        $( 'i ').each( function () {
        	if ( $(this).text() == fullName && nameLoaded ) {
            	$(this).wrapInner( "<span class='me me2'></span>" );
            }
        });
        $( 'p' ).each( function () {
        	var html = $(this).html();
            if ( nameLoaded  ) {
            	if ( html.match( fullName ) ) {
                    html = html.replace( fullName, "<span class='me me2'>" + fullName + "</span>" );
                } else {
                    html = html.replace( firstName, "<span class='me me2'>" + firstName + "</span>" );
                }
            }
            html = html.replace( USERNAME, "<span class='me'>" + USERNAME + "</span>" );
            $(this).html( html );
        }); 
 
        //Add class for subversion commit messages
        $( '.editable' ).each( function ( i, val ) {
            var p = $( val ).find( "p" );
            var html = '';
            var commit = false;
            var iNum;
            var title = false;
            for ( var i = 0; i < p.length; i++ ) {
            	var par = $( p )[i];
                if ( $( par ).text().match( /---------------/ ) ) { //start of new redmine message
                    html += "</div>"
                    commit = false;
                }
                
                if ( $( par ).text().match( /r.*[0-9]{4}-[0-9]{2}-[0-9]{2}/gi ) ) {
                	html += "<div class = 'commitTitle'>";
                    title = true;
                }
                
                if ( $( par ).text().match( /rm.*title:.*desc.*/gi ) ) {
                    html += "<div class = 'commitMessage'>";
                    commit = true;
                    iNum = i;
                }
                
                html += $( par ).html();
                
                if ( title ) {
                    html += "</div>"; //closing tag for title
                    title = false;
                }
                
                if ( i === ( p.length - 1 ) && commit ) { //closing tag
                	html += "</div>"
                }
            }
            if ( commit ) {
            	$(this).html( html );
            }
            
        });
        
        //Calculate how many days since the bug started
        var today = new Date();
        var s = "s";
        var bugDate = new Date( Date.parse( $( 'td.start-date' ).text() ) );
        var bugDateHtml = $( 'td.start-date' ).html();
        var numDays = Math.floor( ( today - bugDate ) / 86400000 );
        if ( numDays === 1 ) {
        	s = "";
        }
        bugDateHtml += " <span class='daysSince'>(" + numDays + " day" + s + " ago)</span>";
        $( 'td.start-date' ).html( bugDateHtml );
        
        //Auto expand update window whenever updating a bug
        $( ".icon-edit" ).on( 'click', function(){
        	$( "#issue_descr_fields" ).show();
            
            //Also highlight name of person who originally submitted the bug in the dropdown and put their name to the side of it as well
            var bugSubmitter = $( 'p.author a')[0];
            bugSubmitter = $( bugSubmitter ).text();
            $("label[for=issue_assigned_to_id]").before("<div class='submitterText'>Originally submitted by " + bugSubmitter + "</div>");
            
            $( '#issue_assigned_to_id' ).children().each( function () {
            	var name = $( this ).text();
                if ( name === bugSubmitter ) {
                	$( this ).addClass( 'submitter' );
                }
            });
            
        } );
        	
    }
	
	

	/////
	//CSS stuff
	/////

	//BUG PAGE
	
    GM_addStyle('.button				{display: inline;font-size: 14px;padding: 2px;float: right;font-weight: normal;border-radius: 2px;margin-right: 11px;color: #2A5685;padding-top: 3px;}');
    GM_addStyle('.button:hover			{cursor: pointer; text-decoration: underline; color: rgb(221, 22, 45)}');
    GM_addStyle('.disabled				{color: rgb(163, 163, 163)!important;cursor: default!important;text-decoration: none!important;}');
    GM_addStyle('#next					{width:34px; text-align:center}');
    GM_addStyle('#prev					{width:35px; text-align:center; padding-left: 5px;}');
    GM_addStyle('#rand					{width:82px; text-align:center}');
	GM_addStyle('.seen 					{color: rgb(33, 175, 33);}');
    GM_addStyle('td						{max-width: 400px}');
    GM_addStyle('.bugPageLevel 			{border-radius: 5px;padding-top: 2px;padding-bottom: 2px;text-align: center;width: 130px}');
	GM_addStyle('.check 				{margin-left: 5px;font-size: 15px;}');
	GM_addStyle('input[type="checkbox"]	{margin-bottom: 3px;}');
    GM_addStyle('div.issue 				{background: #EEEEEE}');
    GM_addStyle('#header 				{padding: 4px 8px 20px 6px;}');
    GM_addStyle('td.closed				{background-image: linear-gradient(to bottom, rgb(74, 223, 74), rgb(0, 99, 0))!important;}');
    GM_addStyle('.me 					{font-weight: bold;color: #507AAA;padding-right: 5px;padding-left: 5px;background-image: linear-gradient(to bottom,rgb(255, 255, 255),rgb(217, 217, 247));border-radius: 6px;}}');
	GM_addStyle('.me2 					{font-style: normal;font-weight: normal;}');
    GM_addStyle('.daysSince				{font-size: 11px;font-style: italic;}');
    GM_addStyle('.commitMessage 		{background-color: rgba(182, 204, 230, 0.5);margin-right: 100px;margin-left: 100px;border-bottom-left-radius: 5px;border-bottom-right-radius: 5px;padding: 7px; margin-top: 10px}');
    GM_addStyle('.commitTitle 			{background-color: #EEEEEE;margin-top: 15px;margin-right: 100px;padding: 2px;padding-bottom: 6px;margin-left: 100px;margin-bottom: -10px;padding-left: 8px;padding-right: 6px;font-weight: bold;font-size: 11px;border-top-left-radius: 5px;border-top-right-radius: 5px;border: solid rgb(232, 232, 232);border-width: 2px;}');
    GM_addStyle('ins					{display: block}');
    GM_addStyle('.dateAndTime			{font-size: 12px;color: rgb(122, 122, 122);}');
    GM_addStyle('h4 					{padding: 4px 10px 4px 5px;border-bottom: 1px solid #E3E3E3;background-image: linear-gradient(to top,#E3E3E3, white);}');
    GM_addStyle('.editable 				{padding-left: 25px;}');
    GM_addStyle('.journal				{padding-bottom: 5px;}');
    GM_addStyle('#issue_descr_fields	{padding: 10px;margin-top: 12px;margin-left: -9px; padding-right: 19px}');
    GM_addStyle('.descriptionBox		{background-color: rgb(241, 241, 241);padding-left: 10px; line-height: 20px;padding-right: 12px;max-height: 350px; overflow-x: auto;border: 1px solid rgb(197, 197, 197);margin-left: 10px; margin-right: 10px;margin-bottom: 15px}');
    GM_addStyle('.reproduce 			{width:' + DESC_WIDTH + 'px; padding-top: 10px;padding-bottom: 10px;margin-right: 5px;margin-left: -130px;margin-top: 24px;margin-bottom: 11px;');
    GM_addStyle('.step 					{padding: 2px}');
    GM_addStyle('.netID					{font-weight: bold; color: #507AAA}');
    GM_addStyle('.courseID				{font-weight: bold; color: #507AAA}');
    GM_addStyle('.redHover:hover		{color: rgb(203, 20, 41)!important; text-decoration: none;}');
    GM_addStyle('.submitter				{color: white; background: rgb(80, 122, 170);}');
    GM_addStyle('.submitterText			{color: rgb(106, 106, 106);margin-left: -100px;margin-right: 73px;text-align: center;margin-bottom: 3px;font-style: italic;}');
    //GM_addStyle('#rand					{float: none;margin-left: 60px;}');


    if ( location.href.match( /redmine.byu.edu\/issues/ ) ) {
        GM_addStyle('h1                 {font-size: 22px; width: 500px;}');
    } else {
    	GM_addStyle('h1                 {font-size: 26px;width: 154px;}');
        GM_addStyle('tbody tr td 		{padding-top: 6px;padding-bottom: 6px;}');
    }


	//MYPAGE

    GM_addStyle('.nodata 							{display: none}');
	GM_addStyle('.unseen 							{background-image: linear-gradient(to top, rgb(255, 255, 162), rgba(255, 255, 202, 1));}'); //yellow
    GM_addStyle('.unseen.context-menu-selection 	{background: rgb(80, 122, 170)!important}');
	GM_addStyle('.unseen:hover						{background:#B6CCE6!important;}');
    GM_addStyle('.seenCheck							{background: rgb(242, 242, 242);margin: 0 15px 0 15px;border: solid 1px rgb(209, 209, 209);height: 17px;padding-bottom: 2px; color:black;min-width: 9px;}');
    GM_addStyle('.seenCheck:hover					{cursor: pointer}');
	GM_addStyle('table.list th 						{background-image: linear-gradient(to top, rgb(81, 81, 134), #507AAA); color: #EAECFF}');
    GM_addStyle('h1 								{margin-top: -4px;padding: 9px;margin-left: -6px;height: 69px;border-radius: 0px;}');
    GM_addStyle('h1 								{background-image: linear-gradient(to right, rgb(86, 86, 100), #507AAA);}');
	GM_addStyle('#header.h2							{color: #EAECFF}');
    GM_addStyle('tr th a 							{color: white!important;}');
	
    var colPadding = 40;
    GM_addStyle('thead tr th:nth-child(1)			{width: 3%;}');
    GM_addStyle('thead tr th:nth-child(2)			{width: 3%;}');
    GM_addStyle('thead tr th:nth-child(3)			{width: 17%;}');
    GM_addStyle('thead tr th:nth-child(4)			{width: 7%;}');
    GM_addStyle('thead tr th:nth-child(5)			{width: 9%; padding-left: ' + colPadding + 'px;}');
	GM_addStyle('td.tracker 						{padding-left: ' + colPadding + 'px;}');
    GM_addStyle('thead tr th:nth-child(6)			{width: 40%;}');
    GM_addStyle('td.subject							{padding-left: ' + colPadding + 'px;}');
    GM_addStyle('tr a:hover 						{color: white!important;}');
	GM_addStyle('tr.issue:hover 					{background-color: #B6CCE6!important;}');
	GM_addStyle('.duplicate							{border-left: solid 4px rgb(134, 205, 249);}');
    GM_addStyle('.duplicate2						{font-weight: bold;}');
    
    
	//Priority Levels
	GM_addStyle('.level 				{margin: 2px; border-radius: 5px;}');
	GM_addStyle('.High 					{background-color: red;color: white; font-weight: bold}');
    GM_addStyle('.Urgent 				{background-color: red;color: white; font-weight: bold}');	
	GM_addStyle('.Normal 				{background-color: rgba(152, 171, 255, 0.72);}'); //blue
	GM_addStyle('.Low 					{background-color: lightgray;}');
	GM_addStyle('.OnHold 				{background-color: rgb(255, 255, 124)!important;}'); //yellow
	//Status Levels
    GM_addStyle('.statusLevel 			{color: #2A5685; background-color: lightcyan;margin-left: 5px;padding-right: 5px;border-radius: 10px;padding-top: 3px;padding-bottom: 3px;}');
	GM_addStyle('.New 					{background-color: lightskyblue;}');
	GM_addStyle('.Assigned 				{background-color: lightskyblue;}');
    GM_addStyle('.InProgress 			{background-color: lightskyblue;}');
	GM_addStyle('.PendingReview 		{background-color: rgb(243, 208, 91);}');  //orange
	GM_addStyle('.StageReady 			{background-color: rgb(126, 233, 126);}'); //green
    GM_addStyle('.OnStage 				{background-color: rgb(126, 233, 126);}'); // ''
	GM_addStyle('.ProductionReady 		{background-color: rgb(126, 233, 126);}'); // ''
    GM_addStyle('.OnProduction 			{background-color: rgb(126, 233, 126);}'); // ''
	GM_addStyle('.Feedback 				{background-color: rgb(246, 247, 158);}'); //yellow
    GM_addStyle('.Closed 				{background-color: lightgray;}');