Skip to content

Instantly share code, notes, and snippets.

@roylez
Last active March 14, 2022 23:28
Show Gist options
  • Save roylez/daf9924aa1bdd759378a429472fd9e48 to your computer and use it in GitHub Desktop.
Save roylez/daf9924aa1bdd759378a429472fd9e48 to your computer and use it in GitHub Desktop.
// ==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