Last active
March 14, 2022 23:28
-
-
Save roylez/daf9924aa1bdd759378a429472fd9e48 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name SFreadability | |
// @namespace http://tampeGonkey.net/ | |
// @version 0.96 | |
// @history 0.96 add back "close case" button | |
// @history 0.95 some UI adjustments | |
// @history 0.94 some UI adjustments | |
// @history 0.93 make word breaking less aggressive | |
// @history 0.92 more UI adjustments | |
// @history 0.91 many inline image display fixes | |
// @history 0.90 fix search completion | |
// @history 0.89 use tailwindcss | |
// @history 0.88 display survey monkey | |
// @history 0.87 remove Athena bot comments | |
// @history 0.86 restore time card link | |
// @history 0.85 fix tachyons.css dead unpkg link | |
// @history 0.84 exclude more pages | |
// @history 0.83 fix case closing page | |
// @history 0.82 remove mw9 from comment/time card boxes | |
// @history 0.81 load time card box on top of time card list | |
// @history 0.80 load comment box on top of comments | |
// @history 0.75 adjust comments section max-width | |
// @history 0.74 Hightlight last update date fields in case listings | |
// @history 0.73 Color code last updated time in case listing | |
// @history 0.72 Add call history tab 2020-03-06 (zhouqt) | |
// @history 0.71 Handle files without extensions 2020-03-03 | |
// @history 0.7 Handle attachments with same names 2020-02-14 (zhouqt) | |
// @history 0.6 Adapt to changes in SF UI change in 2020-02 | |
// @description SalesForce readability | |
// @author roylez | |
// @include https://*.my.salesforce.com/500* | |
// @exclude https://*.my.salesforce.com/500*/* | |
// @license MIT | |
// @noframes | |
// @grant GM_addStyle | |
// @require https://code.jquery.com/jquery-2.2.4.min.js | |
// @require https://code.jquery.com/ui/1.12.1/jquery-ui.min.js | |
// @require https://unpkg.com/dayjs | |
// @updateURL https://gist.github.com/roylez/daf9924aa1bdd759378a429472fd9e48/raw/SFreadability.user.js | |
// @downloadURL https://gist.github.com/roylez/daf9924aa1bdd759378a429472fd9e48/raw/SFreadability.user.js | |
// @run-at document-end | |
// ==/UserScript== | |
GM_addStyle ( ` | |
.efpPanelSelect.efpsTopTabs.efpViewSelect { display: none; } | |
.pbBottomButtons { display: none; } | |
#head_1_ep { display: none; } | |
#ep > .pbBody > div:nth-child(3) { display: none; } | |
select { | |
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); | |
-webkit-appearance: none; | |
-moz-appearance: none; | |
appearance: none; | |
-webkit-print-color-adjust: exact; | |
color-adjust: exact; | |
background-repeat: no-repeat; | |
background-color: #fff; | |
border-color: #d1d5db; | |
border-width: 1px; | |
border-radius: 0.375rem; | |
padding-top: 0.5rem; | |
padding-right: 2.5rem; | |
padding-bottom: 0.5rem; | |
padding-left: 0.75rem; | |
font-size: 1rem; | |
line-height: 1.5rem; | |
background-position: right 0.5rem center; | |
background-size: 1.5em 1.5em; | |
} | |
a>img, | |
a>img:hover, | |
span>img, | |
.pbSubheader>img, | |
img.infoIcon | |
{ | |
display: inline-block; | |
} | |
` ); | |
this.$ = this.jQuery = jQuery.noConflict(true) | |
// https://gist.github.com/BrockA/2625891 | |
function waitForKeyElements ( | |
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; | |
} | |
function always_show_details() { | |
$(".lowerMainSection").remove() | |
$(".eflDetails").attr("style", "") | |
} | |
function remake_header() { | |
let header = $(".headerContent") | |
let title = header.find(".efhpTitle").first().text() | |
let customer = header.find(".efhpContainer > table > tbody > tr > td.efhpLeftContent > div:nth-child(2) > span > div a") | |
let number = header.find(".efhpContainer > table > tbody > tr > td.efhpCenterContent > div > div.efhpCenterTopRow > span:nth-child(3) > span") | |
let created = header.find(".efhpContainer > table > tbody > tr > td.efhpCenterContent > div > div.efhpCenterTopRow > span:nth-child(6)") | |
let status = header.find(".efhpContainer > table > tbody > tr > td.efhpRightContent > table > tbody > tr:nth-child(1) > td.efhpValue.efhpLargeRow > div > span") | |
let severity = header.find(".efhpContainer > table > tbody > tr > td.efhpRightContent > table > tbody > tr:nth-child(2) > td.efhpValue.efhpLargeRow > div > span") | |
let owner = header.find(".efhpContainer > table > tbody > tr > td.efhpRightContent > table > tbody > tr:nth-child(3) > td.efhpValue.efhpLargeRow > div > a") | |
header.children().remove() | |
header.append($(`<h1 class="f1 fw2 black-90 mv2">${title}</h1>`)) | |
} | |
function inject_css() { | |
$("head").append('<meta name="viewport" content="width=device-width, initial-scale=1">') | |
$("head").append('<link ' + 'href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css" ' + 'rel="stylesheet" type="text/css">') | |
$("head").append('<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">') | |
} | |
function move_sections() { | |
// move related lists to tabs | |
var list = $(".efdRelatedLists").length > 0 ? $(".efdRelatedLists") : $(".bRelatedList.first").wrap("<div/>").parent() | |
list.children(".fewerMore").remove() | |
let comments = $(".bRelatedList:has(#comments)") | |
list.prepend(comments) | |
list.prepend($("<ul/>")) | |
var tab = list.children("ul") | |
$(".bRelatedList").each(function() { | |
var t = $(this).find(".pbTitle h3").text() | |
var id = $(this).attr('id') | |
if ( t = t.match(/(Emails|Time Cards|Team|Comments|Files|Case History|Activity History|SurveyMonkey)/) ) { | |
t = t[0] | |
if ( t == "Emails" ) { t = "<span class='mr-2'>📧</span>" + t } | |
else if ( t == "Time Cards" ) { t = "<span class='mr-2'>🕔</span>" + t } | |
else if ( t == "Team" ) { t = "👨<span class='mr-2'>💻</span>" + t } | |
else if ( t == "Comments") { t = "<span class='mr-2'>✍️</span>" + t } | |
else if ( t == "Files" ) { t = "<span class='mr-2'>📁</span>" + t } | |
else if ( t == "Case History" ) { t = "<span class='mr-2'>🍉</span>" + t } | |
else if ( t == "Activity History" ) { t = "<span class='mr-2'>📞</span>" + t } | |
else if ( t == "SurveyMonkey" ) { t = "<span class='mr-2'>🐵</span>" + t } | |
tab.append($("<li class='text-sm'><a href='#" + id + "'>" + t + "</a></li>")) | |
} else { | |
$(this).remove() | |
} | |
}) | |
list.tabs({ active: 0 }) | |
} | |
function restore_time_card_link() { | |
let heading=$(".bRelatedList .pbTitle h3:contains('Time Cards')").parent() | |
let case_no=document.title.match(/: (\d+).*/)[1] | |
let name=$("#globalHeaderNameMink .chatter-avatar").attr("title") | |
heading.after($(` | |
<td class='pbButton'> | |
<a id='new_time_card' class='px-3 py-1 bg-gray-200 font-semibold rounded-md text-sm hover:bg-gray-300 focus:outline-none' href='/a0H/e?CF00N200000016JAU=${name}&CF00N200000015mpa=${case_no}&00N200000015mpZ=30'> | |
NEW | |
</a> | |
</td>`)) | |
} | |
function clear_comments() { | |
var attachments = $(".pbBody[id$=RelatedFileList_body]").first() | |
// replace table with div | |
var table = $(".pbBody[id*='CommentsList'] > table") | |
table.parent().prepend("<div id='comments' class='m-auto w-full font-sans'/>") | |
var div=table.parent().children().first() | |
table.find("tr.dataRow").each(function() { | |
// bye bye Athena | |
if ($(this).find(':contains("-Athena")').length > 0 ) { return true } | |
div.append("<div class='comment_row w-full text-base flex justify-center border-b-2 border-blue-200 text-gray'/>") | |
let row = div.children().last() | |
let title = $(this).find(".dataCell b").first() | |
let author = $(this).find(".dataCell b a").first() | |
let time = title.text().match(/\((\d+\/\d+\/\d{4}.*? [AP]M)\)/)[1] | |
let content = $(this).children().last() | |
title.remove() | |
content.find("br").get(0).remove() | |
let time_utc_line = content.contents().get(0) | |
let time_utc = time_utc_line.nodeValue.slice(0, -2) | |
time_utc_line.remove() | |
content.find("br").get(0).remove() | |
let first_line = content.contents().get(0) | |
first_line.nodeValue = first_line.nodeValue.substring(1) | |
row.append("<div class='w-1/5 p-3 text-center text-base m-auto'>" + $(this).children(".actionColumn").html() + "</div>") | |
let has_attachment=content.text().match(/New File added: (.*?) \[/) | |
let by_bot = /STS-API/.test(author.text()) | |
let by_customer = /(portal|NTT)/.test(author.text()) | |
let public_comment = /Private/.test($(this).children(".actionColumn").text()) | |
// highlight public comments and comments from customers | |
if ( by_customer ) { | |
row.addClass("bg-yellow-100") | |
} else if ( public_comment ) { | |
row.addClass("bg-blue-50") | |
} else if ( by_bot ) { | |
row.addClass("bg-green-50") | |
} | |
// comment says there is an attachment | |
if ( has_attachment ) { | |
var attachment_name=has_attachment[1].replace(/\.[^.]+$/, '') | |
var candidates = attachments.find( `tr:contains('${attachment_name}')` ) | |
// attachment actually found in the list without going to next page | |
if ( candidates.length == 1 ) { | |
content = mark_attachment(has_attachment[1], time, candidates.first()) | |
} else if (candidates.length > 1) { | |
candidates.each(function() { | |
var match = $(this).text().match(time) | |
if ( match && match.length == 1) { | |
content = mark_attachment(has_attachment[1], time, $(this).first()) | |
} | |
}) | |
} | |
row.append("<div class='w-4/5 p-3 font-mono border-l-2 border-blue-200 flex items-center'>" + content.prop('outerHTML') + "</div>") | |
} else if ( by_bot ) { | |
row.append("<div class='w-4/5 p-3 font-mono text-xs italic break-normal md:break-words text-base border-l-2 border-blue-200 leading-snug'>" + content.prop('outerHTML') + "</div>") | |
} else { | |
row.append("<div class='w-4/5 p-3 font-mono italic break-normal md:break-words text-base border-l-2 border-blue-200 leading-snug'>" + content.prop('outerHTML') + "</div>") | |
} | |
let action_column = row.children().first() | |
action_column.prepend("<p class='comment_time text-gray-600'><b>" + time + "</b></p>") | |
action_column.prepend("<p class='text-sm italic text-gray-400'>" + time_utc + "</p>") | |
action_column.prepend("<p><b>" + author.get(0).outerHTML + "</b></p>") | |
}) | |
table.remove() | |
} | |
function mark_attachment(file, time, attachment_row) { | |
var link = attachment_row.find("td a.actionLink:contains('Download')").attr('href') | |
var button = $(`<a class="rounded-lg italic font-medium text-sm bg-green-100 font-sans border px-3 py-1 border-green-200 shadow-md" target="_blank" href="${link}">${file}</a>`) | |
return button | |
} | |
function move_comment_form() { | |
let iframe = $( ".efdFields " ).children(":has(iframe)") | |
iframe.nextAll().before(iframe) | |
} | |
function style_description() { | |
let des = $("#cas15_ileinner") | |
des.addClass("text-sm bg-yellow-100 break-normal md:break-words font-mono italic p-2") | |
} | |
// class="x-grid3-col-CASES_LAST_UPDATE" | |
function colorize_cases_last_update() { | |
let now = dayjs() | |
let last_updates = $("div[class$='col-CASES_LAST_UPDATE'],div[class$='col-CASES_LAST_UPDATE_DATE_ONLY']") | |
last_updates.each(function() { | |
let case_status = $(this).parent().parent().find('.x-grid3-col-CASES_STATUS').text() | |
let diff = dayjs( $(this).text() ).diff(now, 'day') | |
if ( case_status.match(/(Resolved|Expired)/) ) { return } | |
if ( diff <= -3 ) { | |
if ( diff <= -7 ) { | |
$(this).addClass("bg-red-400 text-white") | |
} else { | |
$(this).addClass("bg-red-200 text-gray") | |
} | |
} | |
}) | |
} | |
// class="x-grid3-col-CASES_STATUS" | |
function colorize_cases_status() { | |
let WoC = $("div[class$='col-CASES_STATUS']:contains('Customer')") | |
let WoS = $("div[class$='col-CASES_STATUS']:contains('Support')") | |
let WoE = $("div[class$='col-CASES_STATUS']:contains('Engineering')") | |
let WoU = $("div[class$='col-CASES_STATUS']:contains('Upstream')") | |
let WoO = $("div[class$='col-CASES_STATUS']:contains('Operation')") | |
let New = $("div[class$='col-CASES_STATUS']:contains('New')") | |
WoC.addClass("bg-green-200") | |
WoS.addClass("bg-red-200") | |
WoE.addClass("bg-yellow-200") | |
WoU.addClass("bg-blue-200") | |
WoO.addClass("bg-indigo-200") | |
New.addClass("bg-red-200") | |
} | |
function load_comment_box() { | |
let case_id=$("div[id*='RelatedCommentsList']").attr('id').split("_")[0] | |
$("#comments").before(`<div id="comment_box"/>`) | |
$("#comment_box").load(`/00a/e?parent_id=${case_id} #editPage`, function() { | |
$("#comment_box textarea").addClass("font-mono leading-5 h-64 text-sm w-4/5 px-4 py-2 mt-2 mr-4 text-base text-black transition duration-500 ease-in-out transform rounded-lg bg-indigo-100 focus:border-gray-500 focus:outline-none focus:shadow-outline focus:ring-2 ring-offset-current ring-offset-2") | |
$("#cancelURL").attr("value", `/${case_id}`) | |
$("#retURL").attr("value", `/${case_id}`) | |
}) | |
} | |
function load_time_card_box() { | |
let case_id=$("div[id*='RelatedCommentsList']").attr('id').split("_")[0] | |
let case_no=$("#cas2_ileinner").text() | |
let name=$("#globalHeaderNameMink .chatter-avatar").attr("title") | |
$(`#${case_id}_00N200000015mpa_body`).before(`<div id="time_card_box"/>`) | |
$("#time_card_box").load(`/a0H/e #editPage`, function() { | |
$('#CF00N200000016JAU').val(name) | |
$('#CF00N200000015mpa').val(case_no) | |
$('#00N200000015mpZ').val("30") | |
$('#retURL').val(`/${case_id}`) | |
$('#cancelURL').val(`/${case_id}`) | |
$('#saveURL').val(`/${case_id}`) | |
}) | |
} | |
function remove_sf_junk() { | |
// .pbHelp - SF help | |
// .navLinks - Switch to Lightning | |
$(".pbHelp,.navLinks").remove() | |
} | |
function redo_header() { | |
let header = $(".headerContent") | |
let customer = header.find(".efhpLeftContent .efhpHighlight a") | |
let title = header.find(".efhpTitle").text() | |
let case_number = header.find(".efhpCenterTopRow span.efhpCenterValue > span").text() | |
let status = header.find(".efhpRightContent tbody>tr:nth-child(1) .efhpLabeledFieldValue > span").text() | |
let severity = header.find(".efhpRightContent tbody>tr:nth-child(2) .efhpLabeledFieldValue > span").text().split("-")[0] | |
let owner = header.find(".efhpRightContent tbody>tr:nth-child(3) .efhpLabeledFieldValue>a") | |
let customer_line = $("<div/>").addClass("flex items-center justify-center gap-3 text-lg text-gray-700 mb-2") | |
customer_line.append(customer.addClass("text-blue-500 font-semibold")) | |
customer_line.append($(`<span class="text-gray-200">|</span><span>${severity}</span><span class="text-gray-200">|</span><span>${status}</span><p/>`)) | |
let owner_line = $("<div/>").addClass("flex items-center justify-center gap-3 text-lg text-gray-700 mb-2") | |
owner_line.append(owner.addClass("text-gray-700 font-semibold italic font-serif")) | |
header.html(`<h1 class="font-semibold block text-gray-900 text-center my-5 text-2xl">${case_number} - ${title}</h1><p/>`) | |
header.append(customer_line) | |
header.append(owner_line) | |
let buttons = $("#topButtonRow").addClass("flex justify-center gap-3 mb-5").removeClass("pbButton") | |
buttons.closest(".pbHeader").html(buttons).addClass("flex justify-center") | |
} | |
function restore_close_case_button() { | |
let buttons = $("#topButtonRow") | |
let case_id=$("div[id*='RelatedCommentsList']").attr('id').split("_")[0] | |
buttons.append(`<input type="button" class="btn" value="CLOSE" onclick="navigateToUrl('/${case_id}/s?retURL=${encodeURIComponent("/"+case_id)}')"/>`) | |
} | |
if ( $(document).attr("title").match(/^Case: \d+/) ) { | |
inject_css() | |
remove_sf_junk() | |
redo_header() | |
always_show_details() | |
style_description() | |
clear_comments() | |
move_sections() | |
move_comment_form() | |
load_comment_box() | |
restore_time_card_link() | |
load_time_card_box() | |
restore_close_case_button() | |
} | |
else { | |
inject_css() | |
remove_sf_junk() | |
waitForKeyElements(".x-grid3-row", function() { | |
colorize_cases_last_update() | |
colorize_cases_status() | |
}) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment