From 5fb6044fbdb2d0ac6a03a076e9fad42560dd4aa2 Mon Sep 17 00:00:00 2001 From: Diego Dereck Sosa-Escamilla Date: Mon, 14 Sep 2026 09:55:45 -0400 Subject: [PATCH 1/3] Rename tdx-enhanced.js to tdx-enhanced_OLD.js --- tdx-enhanced.js => tdx-enhanced_OLD.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tdx-enhanced.js => tdx-enhanced_OLD.js (100%) diff --git a/tdx-enhanced.js b/tdx-enhanced_OLD.js similarity index 100% rename from tdx-enhanced.js rename to tdx-enhanced_OLD.js From ac2465bbc26ef38bdb4b9058adb719ad703a8a90 Mon Sep 17 00:00:00 2001 From: Diego Dereck Sosa-Escamilla Date: Mon, 14 Sep 2026 09:57:37 -0400 Subject: [PATCH 2/3] Create tdx-enhanced.js New TDX script reviewed by Astra. Size decreased from 143,600 to 111,325 bytes (22.5% smaller). --- tdx-enhanced.js | 1373 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1373 insertions(+) create mode 100644 tdx-enhanced.js diff --git a/tdx-enhanced.js b/tdx-enhanced.js new file mode 100644 index 0000000..987a04c --- /dev/null +++ b/tdx-enhanced.js @@ -0,0 +1,1373 @@ +// ==UserScript== +// @name tdx-enhanced +// @namespace purdue-it +// @version 2026-09-14-01 +// @description enhanced tdx coloring & formatting. follows system color scheme. +// @author Purdue STEM IT - it@purdue.edu +// @match https://service.purdue.edu/TDWorkManagement* +// @match https://service.purdue.edu/tdworkmanagement* +// @match https://service.purdue.edu/TDNext* +// @match https://service.purdue.edu/TDWebApi* +// @match https://purdue.teamdynamixpreview.com/TDWorkManagement* +// @match https://purdue.teamdynamixpreview.com/TDNext* +// @require https://momentjs.com/downloads/moment.min.js +// @require https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.6.0/tinycolor.min.js +// @downloadURL https://raw.github.itap.purdue.edu/ECN/tdx-userscript/refs/heads/beta/tdx-enhanced.js +// @updateURL https://raw.github.itap.purdue.edu/ECN/tdx-userscript/refs/heads/beta/tdx-enhanced.js +// @grant GM_addStyle +// @run-at document-end +// ==/UserScript== + +(() => { + 'use strict'; + + /* Install in a userscript manager on the matched Purdue TDX sites. The wrench + * beside search controls themes, links, highlights, AI visibility, and layout. + * Report columns are recognized by their displayed headings, not column order. + * Titles can contain !! text !!, ~~ text ~~, ## text ##, or %% text %%; a + * widget title of {"name":"Title","url":"https://..."} embeds that URL. + * Moment preserves TDX's local date/calendar semantics; tinycolor handles CSS + * colors. No build step is required. Settings stay in localStorage.userSettings. + */ + + // Department colors are shared with the original Purdue webqueue script. + const QUEUES = { + 'aae': {bg: '#7030A0', txt: 'white' }, + 'abe' : {bg: '#256f78', txt: 'white'}, + 'ag' : {bg: '#99CC00', txt: 'white'}, + 'bidc' : {bg: '#C5FFC5', txt: 'black'}, + 'biology' : {bg: '#67c1ee', txt: 'black'}, + 'bme' : {bg: '#0D69FF', txt: 'white'}, + 'ce' : {bg: '#008000', txt: 'white' }, + 'che' : {bg: '#00FF00', txt: 'black'}, + 'chem' : {bg: '#ffb1b7', txt: 'black'}, + 'coea' : {bg: '#00D6BD', txt: 'black'}, + 'coep' : {bg: '#FFDDB7', txt: 'black'}, + 'comp.sci.' : {bg: '#772212', txt: 'white'}, + 'cos admin' : {bg: '#ddaa33', txt: 'black'}, + 'dp' : {bg: '#0070A8', txt: 'white'}, + 'doe': {bg: 'red', txt: 'white'}, + 'ece' : {bg: '#FF6600', txt: 'white'}, + 'ecnaccts' : {bg: '#98A4AE', txt: 'white'}, + 'eaps' : {bg: '#629454', txt: 'white'}, + 'eee' : {bg: '#F66A6A', txt: 'white'}, + 'ene': {bg: '#A45CD0', txt: 'white' }, + 'engineering' : {bg: '#FFD900', txt: 'black'}, + 'engr - south' : {bg: '#663300', txt: 'white'}, + 'engr - north' : {bg: '#663300', txt: 'white'}, + 'epics': {bg: '#E399E3', txt: 'black'}, + 'flex' : {bg: '#A7ECFF', txt: 'black'}, + 'helpdesk' : {bg: '#98A4AE', txt: 'white'}, + 'hhs' : {bg: '#CFB991', txt: 'black'}, + 'ie' : {bg: '#FFB7B7', txt: 'black'}, + 'imi' : {bg: '#FFB96D', txt: 'black'}, + 'apple' : {bg: '#666666', txt: 'white'}, + 'mailman' : {bg: 'white', txt: 'black'}, + 'math' : {bg: '#f7977a', txt: 'black'}, + 'me' : {bg: '#b30000', txt: 'white'}, + 'mse': {bg: '#EFD3F5', txt: 'black'}, + 'ne': {bg: '#af6377', txt: 'white'}, + 'pari' : {bg: '#3f5c8a', txt: 'white'}, + 'pharmacy' : {bg: '#FF33FF', txt: 'white'}, + 'physics' : {bg: '#07622f', txt: 'white'}, + 'president' : {bg: '#cfb991', txt: 'black'}, + 'sacc' : {bg: '#434343', txt: 'white'}, + 'science' : {bg: '#22dd94', txt: 'black'}, + 'sitemgrs' : {bg: '#98A4AE', txt: 'white'}, + 'stat' : {bg: '#ff6f44', txt: 'white'}, + 'students' : {bg: '#98A4AE', txt: 'white'}, + 'swt' : {bg: '#000ED6', txt: '#FFEE00'}, + 'polytechnic' : {bg: 'black', txt: 'white'}, + 'cnit' : {bg: '#434343', txt: 'white'}, + 'uds' : {bg: '#98A4AE', txt: 'white'}, + 'udsprojects': {bg: '#98A4AE', txt: 'white'}, + 'vet' : {bg: '#CCFFFF', txt: 'black'}, + 'web' : {bg: '#B46086', txt: 'white'}, + 'windows' : {bg: '#b77729', txt: 'white'}, + 'zsite' : {bg: '#98A4AE', txt: 'white'}, + }; + + // CSS filters recolor monochrome image/sprite icons. These matrix operations + // simulate the browser's RGB transforms; clamping after each step is essential. + // Adapted from https://github.com/angel-rs/css-color-filter-generator. + class Color { + constructor(r, g, b) { this.set(r, g, b); } + set(r, g, b) { + this.r = Math.max(0, Math.min(255, r)); + this.g = Math.max(0, Math.min(255, g)); + this.b = Math.max(0, Math.min(255, b)); + } + multiply(m) { + const {r, g, b} = this; + this.set(r * m[0] + g * m[1] + b * m[2], r * m[3] + g * m[4] + b * m[5], r * m[6] + g * m[7] + b * m[8]); + } + linear(slope, intercept = 0) { + this.set(this.r * slope + intercept * 255, this.g * slope + intercept * 255, this.b * slope + intercept * 255); + } + invert(value) { this.linear(1 - 2 * value, value); } + brightness(value) { this.linear(value); } + contrast(value) { this.linear(value, (1 - value) / 2); } + sepia(value) { + this.multiply([ + 1 - 0.607 * value, 0.769 * value, 0.189 * value, + 0.349 * value, 1 - 0.314 * value, 0.168 * value, + 0.272 * value, 0.534 * value, 1 - 0.869 * value + ]); + } + saturate(value) { + this.multiply([ + 0.213 + 0.787 * value, 0.715 - 0.715 * value, 0.072 - 0.072 * value, + 0.213 - 0.213 * value, 0.715 + 0.285 * value, 0.072 - 0.072 * value, + 0.213 - 0.213 * value, 0.715 - 0.715 * value, 0.072 + 0.928 * value + ]); + } + hueRotate(angle) { + const sin = Math.sin(angle * Math.PI / 180), cos = Math.cos(angle * Math.PI / 180); + this.multiply([ + 0.213 + cos * 0.787 - sin * 0.213, 0.715 - cos * 0.715 - sin * 0.715, 0.072 - cos * 0.072 + sin * 0.928, + 0.213 - cos * 0.213 + sin * 0.143, 0.715 + cos * 0.285 + sin * 0.14, 0.072 - cos * 0.072 - sin * 0.283, + 0.213 - cos * 0.213 - sin * 0.787, 0.715 - cos * 0.715 + sin * 0.715, 0.072 + cos * 0.928 + sin * 0.072 + ]); + } + // HSL adds perceptual error to the RGB distance used by the optimizer. + // Adapted from https://stackoverflow.com/a/9493060/2688027 (CC BY-SA). + hsl() { + const r = this.r / 255, g = this.g / 255, b = this.b / 255; + const max = Math.max(r, g, b), min = Math.min(r, g, b), delta = max - min; + const l = (max + min) / 2; + let h = 0, s = 0; + if (delta) { + s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min); + if (max === r) h = (g - b) / delta + (g < b ? 6 : 0); + else if (max === g) h = (b - r) / delta + 2; + else h = (r - g) / delta + 4; + h /= 6; + } + return {h: h * 100, s: s * 100, l: l * 100}; + } + } + + class Solver { + constructor(target) { + this.target = target; + this.targetHSL = target.hsl(); + this.color = new Color(0, 0, 0); // Reuse the color object in the hot loop. + } + // Search broadly up to three times, then refine the best result locally. + solve() { + let best = {loss: Infinity}; + for (let i = 0; i < 3 && best.loss > 25; i++) { + const result = this.spsa(5, [60, 180, 18000, 600, 1.2, 1.2], 15, [50, 20, 3750, 50, 100, 100], 1000); + if (result.loss < best.loss) best = result; + } + const scale = best.loss + 1; + const refined = this.spsa(best.loss, [0.25, 0.25, 1, 0.25, 0.2, 0.2].map(value => value * scale), 2, best.values.slice(), 500); + if (refined.loss < best.loss) best = refined; + const fmt = (index, multiplier = 1) => Math.round(best.values[index] * multiplier); + return {filter: {invert: `${fmt(0)}%`, sepia: `${fmt(1)}%`, saturate: `${fmt(2)}%`, hue_rotate: `${fmt(3, 3.6)}deg`, brightness: `${fmt(4)}%`, contrast: `${fmt(5)}%`}}; + } + // SPSA estimates all six derivatives with only two loss evaluations per + // iteration. Decreasing step sizes stabilize the search around a minimum. + spsa(A, a, c, values, iterations) { + let best, bestLoss = Infinity; + const deltas = [], high = [], low = []; + for (let k = 0; k < iterations; k++) { + const step = c / Math.pow(k + 1, 1 / 6); + for (let i = 0; i < 6; i++) { + deltas[i] = Math.random() > 0.5 ? 1 : -1; + high[i] = values[i] + step * deltas[i]; + low[i] = values[i] - step * deltas[i]; + } + const difference = this.loss(high) - this.loss(low); + for (let i = 0; i < 6; i++) { + const value = values[i] - a[i] / (A + k + 1) * difference / (2 * step) * deltas[i]; + // Hue wraps around; other filters stay inside their valid bounds. + values[i] = i === 3 ? ((value % 100) + 100) % 100 : Math.max(0, Math.min(i === 2 ? 7500 : i >= 4 ? 200 : 100, value)); + } + const loss = this.loss(values); + if (loss < bestLoss) { best = values.slice(); bestLoss = loss; } + } + return {values: best, loss: bestLoss}; + } + loss(values) { + const color = this.color; + color.set(0, 0, 0); + color.invert(values[0] / 100); + color.sepia(values[1] / 100); + color.saturate(values[2] / 100); + color.hueRotate(values[3] * 3.6); + color.brightness(values[4] / 100); + color.contrast(values[5] / 100); + const hsl = color.hsl(), target = this.target, targetHSL = this.targetHSL; + return Math.abs(color.r - target.r) + Math.abs(color.g - target.g) + Math.abs(color.b - target.b) + + Math.abs(hsl.h - targetHSL.h) + Math.abs(hsl.s - targetHSL.s) + Math.abs(hsl.l - targetHSL.l); + } + } + + const TOOLS_ORIGIN = 'https://engineering.purdue.edu'; + const DATE_FORMAT = 'M/D/YYYY h:mm A'; + const LOCAL_FORMAT = 'YYYY-MM-DDTHH:mm'; + const DATE_COLUMNS = ['Modified', 'Created', 'Date Due', 'Due', 'Start', 'SLA Resolve By', 'SLA Respond By']; + const LINK_COLUMNS = ['Title', 'TicketID', 'TicketTitle']; + const REPORT_COLUMNS = new Set([...DATE_COLUMNS, ...LINK_COLUMNS, 'CSS Support', 'Status', 'Age', 'Requestor', 'Modified By', 'Prim Resp', 'Responsibility']); + const STATUS_COLORS = {'In Process': '#FF3C3C', New: '#FF3C3C', Assigned: '#FF3C3C', 'Awaiting Customer Response': '#33CC33', 'Pending External': '#FF9900', 'Pending Internal': '#FF9900'}; + const media = matchMedia('(prefers-color-scheme: dark)'); + const $ = (selector, root = document) => root.querySelector(selector); + const all = (selector, root = document) => [...root.querySelectorAll(selector)]; + const clamp = value => Math.max(0, Math.min(1, value)); + const make = (tag, className = '', text = '') => Object.assign(document.createElement(tag), {className, textContent: text}); + const defaults = {colorMode: 'auto', theme: 'gold', linkBehavior: 'tabs', highlightBehavior: 'block', aiBehavior: 'aiEnable', customHighlights: []}; + let config = readSettings(); + let rules = compileHighlights(config.customHighlights); + let revision = 0; + let colorWindow; + let frameRequest = 0; + const pending = new Set(); + const filters = new Map(); + const rows = new WeakMap(); + const sources = new WeakMap(); + const decorations = new WeakMap(); + const originalLinks = new WeakMap(); + const calendars = new WeakMap(); + const editors = new WeakSet(); + const editorDocuments = new WeakMap(); + const shadowRoots = new WeakSet(); + + // Accept both the legacy JSON-inside-JSON format and plain arrays. A bad rule + // must not discard unrelated preferences or stop the rest of the script. + function readSettings() { + try { + const saved = JSON.parse(localStorage.getItem('userSettings')) || {}; + return {...defaults, ...saved, customHighlights: parseHighlights(saved.customHighlights ?? [])}; + } catch { + return {...defaults, customHighlights: []}; + } + } + + function parseHighlights(value) { + try { + const parsed = typeof value === 'string' ? JSON.parse(value) : value; + return Array.isArray(parsed) ? parsed.filter(rule => rule && typeof rule === 'object' && rule.style && typeof rule.style === 'object') : []; + } catch { + return []; + } + } + + // Escape literal delimiters fully; omit /g so repeated matches have no cursor. + function compileHighlights(custom) { + const escape = text => String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const builtins = ['!!', '~~', '##', '%%'].map((value, i) => ({type: 'highlight', value, style: {background: `var(--col-highlight-${i + 1})`}})); + return [...builtins, ...custom].flatMap(rule => { + try { + const pattern = rule.type === 'highlight' ? `${escape(rule.value)} (.*) ${escape(rule.value)}` : rule.value; + return [{...rule, re: ['highlight', 'report-regex'].includes(rule.type) ? new RegExp(pattern) : null}]; + } catch { + return []; // One malformed user regex must not break every report. + } + }); + } + + // Keep the legacy storage shape so the external color editor and old installs + // can still read it. Merge form fields to preserve hidden settings such as layout. + function saveSettings() { + const form = $('#settingsForm'); + if (!form || !form.reportValidity()) return; + config = {...config, ...Object.fromEntries(new FormData(form)), stickyColumns: form.elements.stickyColumns.checked ? 'enabled' : ''}; + config.customHighlights = parseHighlights(config.customHighlights); + localStorage.setItem('userSettings', JSON.stringify({...config, customHighlights: JSON.stringify(config.customHighlights)})); + applySettings(); + } + + function fillSettings() { + const form = $('#settingsForm'); + if (!form) return; + for (const [name, value] of Object.entries(config)) { + const field = form.elements.namedItem(name); + if (!field) continue; + if (field.type === 'checkbox') field.checked = value === 'enabled' || value === true; + else field.value = typeof value === 'object' ? JSON.stringify(value) : value; + } + for (const [name, variable] of [['primaryColor', 'col-400'], ['backgroundColor', 'bg-0']]) { + if (!config[name]) form.elements[name].value = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(`--${variable}`).trim()).toHexString(); + } + } + + // CSS owns presentation, including live OS mode changes and newly inserted AI + // controls. Settings refresh existing rows once; ordinary mutations stay scoped. + function applySettings() { + const root = document.documentElement; + const mode = ['lightMode', 'darkMode'].includes(config.colorMode) ? config.colorMode : (media.matches ? 'darkMode' : 'lightMode'); + root.classList.remove('lightMode', 'darkMode'); + root.classList.add(mode); + root.classList.toggle('tdx-hide-ai', config.aiBehavior === 'aiDisable'); + root.classList.toggle('tdx-sticky-columns', config.stickyColumns === 'enabled' || config.stickyColumns === true); + root.classList.toggle('tdx-wide-column', config.layout === '1_100-66-33'); + for (const scheme of ['dark', 'light']) { + const theme = ['gold', 'blue', 'green'].includes(config.theme) ? config.theme : 'gold'; + const custom = config.theme === 'custom'; + root.style.setProperty(`--${scheme}-col-primary`, custom && tinycolor(config.primaryColor).isValid() ? config.primaryColor : `var(--${scheme}-${theme}-accent)`); + root.style.setProperty(`--${scheme}-bg-primary`, custom && tinycolor(config.backgroundColor).isValid() ? config.backgroundColor : `var(--${scheme}-bg-default)`); + } + localStorage.setItem('styles', config.colorMode); // Retain the original compatibility key. + setIconFilter(); + fillSettings(); + rules = compileHighlights(config.customHighlights); + revision++; + schedule(document); + } + + // Solving costs thousands of color transforms. Cache by resolved accent; shadow + // trees inherit the variables from their host instead of solving again. + function setIconFilter() { + const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--col-400').trim()); + if (!accent.isValid()) return; + const key = accent.toHexString(); + if (!filters.has(key)) { + const {r, g, b} = accent.toRgb(); + filters.set(key, new Solver(new Color(r, g, b)).solve().filter); + } + for (const [name, value] of Object.entries(filters.get(key))) { + document.documentElement.style.setProperty(`--filter-${name.replace('_', '-')}`, value); + } + } + + // Remember only properties we change. Undo them before reapplying rules so + // switching themes/highlights does not leave stale colors behind. + function paint(element, style) { + if (!style) return; + let saved = decorations.get(element); + if (!saved) decorations.set(element, saved = new Map()); + for (const [name, value] of Object.entries(style)) { + const property = name.startsWith('--') ? name : name.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`); + if (!saved.has(property)) saved.set(property, {value: element.style.getPropertyValue(property), priority: element.style.getPropertyPriority(property)}); + element.style.setProperty(property, String(value)); + } + } + + function resetPaint(element) { + const saved = decorations.get(element); + if (!saved) return; + for (const [name, {value, priority}] of saved) { + if (value) element.style.setProperty(name, value, priority); + else element.style.removeProperty(name); + } + decorations.delete(element); + } + + // Wrapping existing nodes preserves links and their event listeners. + function bubble(cell, style) { + let span = cell.firstElementChild; + if (!span?.classList.contains('qHighlight')) { + span = make('span', 'qHighlight'); + span.append(...cell.childNodes); + cell.append(span); + } + paint(span, {borderColor: 'transparent', ...style}); + return span; + } + + function highlight(type, text, element) { + let match; + let style; + // Built-in delimiters have first-match precedence; custom rules can override. + for (let i = 0; i < rules.length; i++) { + const rule = rules[i]; + if (rule.type === 'highlight') { + const found = rule.re.exec(String(text)); + if (found) { + match = found; + style = {...rule.style}; + if (i < 4) i = 3; + else break; + } + } else if (rule.type === type || (type === 'report' && rule.type === 'report-regex')) { + if (['person', 'report'].includes(rule.type) && rule.value !== text) continue; + if (rule.type === 'report-regex' && !rule.re.test(text)) continue; + style = {...rule.style}; + if (type === 'report') element.classList.add('reportTitle'); + if (type === 'person') element = bubble(element); + // Never mutate the stored style or build invalid one-digit hex alpha. + if (type === 'dateModified' && style.background && tinycolor(style.background).isValid()) { + style.background = tinycolor(style.background).setAlpha(clamp(text)).toRgbString(); + } + } + } + if (!style) { + if (type === 'reply') style = {backgroundColor: 'var(--col-reply)'}; + if (type === 'userModified') style = {backgroundColor: 'var(--col-modified)'}; + if (type === 'dateModified' || type === 'age') style = {backgroundColor: `rgba(255,0,0,${clamp(text)})`}; + } + const link = $('a', element); + if (style && match && link) { + if (config.highlightBehavior === 'block') link.textContent = match[1]; + else { + const span = make('span', '', link.textContent); + span.dataset.tdxHighlight = ''; + link.replaceChildren(span); + element = span; + paint(element, {lineHeight: '1.4'}); + } + } + paint(element, style); + } + + // TDX normally uses onclick popups. Report links become tabs; update/task + // actions retain the original centered-window dimensions in Tabs mode. + function handleLink(source, link) { + if (!originalLinks.has(link)) originalLinks.set(link, {onclick: link.onclick, target: link.getAttribute('target')}); + const original = originalLinks.get(link); + if (config.linkBehavior !== 'tabs') { + link.onclick = original.onclick; + if (original.target === null) link.removeAttribute('target'); + else link.target = original.target; + } else if (source === 'report') { + link.onclick = null; + link.target = '_blank'; + } else { + link.onclick = event => { + event.preventDefault(); + const w = screen.width / 1.75, h = screen.height / 1.25; + const left = (screen.width - w) / 2 + (window.screenLeft ?? 0); + const top = (screen.height - h) / 2 + (window.screenTop ?? 0); + const base = location.pathname.slice(0, location.pathname.lastIndexOf('/')); + window.open(`${base}/${source}${location.search}`, '_blank', `width=${w},height=${h},left=${left},top=${top}`); + }; + } + } + + // Keep raw dates/titles separately from rendered labels. Reprocessing "Yesterday" + // as a new date would otherwise corrupt timestamps after a report refresh. + function sourceText(cell) { + const current = cell.textContent.trim(); + const saved = sources.get(cell); + return saved && current === saved.output ? saved.input : current; + } + + function parseTable(table) { + const headerRow = [...table.rows].find(row => [...row.cells].some(cell => cell.tagName === 'TH')); + if (!headerRow) return; + const headers = [...headerRow.cells].map(cell => ($('span', cell) || cell).textContent.trim()); + const schema = headers.join('|'); + if (!headers.some(header => REPORT_COLUMNS.has(header))) return; + for (const row of table.rows) { + if (row === headerRow || !row.querySelector('td') || row.closest('table') !== table) continue; + const cells = [...row.cells]; + const links = all('a', row); + const last = rows.get(row); + if (last && last.revision === revision && last.schema === schema && last.text === row.textContent && cells.length === last.cells.length && cells.every((cell, i) => cell === last.cells[i]) && links.length === last.links.length && links.every((link, i) => link === last.links[i])) continue; + resetPaint(row); + const item = {}; + // A checkbox header occupies its physical column. Never shift data when + // a row contains an input or a header lacks the old nested . + cells.forEach((cell, i) => { + const input = sourceText(cell); + resetPaint(cell); + cell.classList.remove('light', 'dark'); + all('.qHighlight, [data-tdx-highlight]', cell).forEach(span => span.replaceWith(...span.childNodes)); + if (headers[i] === 'Title' && cell.textContent.trim() !== input) { + const link = $('a', cell); + if (link) link.textContent = input; + } + if (headers[i]) item[headers[i]] = {cell, text: input}; + }); + decorateRow(item, row); + // Store original source from the item, since formatting has now changed it. + Object.values(item).forEach(({cell, text}) => sources.set(cell, {input: text, output: cell.textContent.trim()})); + rows.set(row, {revision, schema, text: row.textContent, cells, links: all('a', row)}); + } + } + + function decorateRow(item, row) { + const queue = item['CSS Support']; + const palette = queue && QUEUES[queue.text.toLowerCase()]; + if (palette) bubble(queue.cell, {backgroundColor: palette.bg, color: palette.txt}); + const status = item.Status; + const statusColor = status && STATUS_COLORS[status.text]; + if (statusColor) bubble(status.cell, {backgroundColor: `${statusColor}20`, color: 'var(--txt-1)', borderColor: `${statusColor}50`}); + for (const name of DATE_COLUMNS) { + if (!item[name] || !item[name].text) continue; + const {cell, text} = item[name]; + const date = moment(text); + if (!date.isValid()) continue; + cell.textContent = name === 'Modified' ? date.fromNow() : date.calendar(); + cell.title = text; + if (name === 'Created' || name === 'Start') continue; + const hours = moment().diff(date, 'hours', true); + const alpha = clamp(name === 'Modified' ? hours / 336 : 1 + hours / 120); + highlight('dateModified', alpha, cell); + cell.classList.add(alpha > 0.5 ? 'light' : 'dark'); + } + if (item.Age) { + const days = Number(item.Age.text.slice(0, -1)); + if (days > 30) { + const alpha = clamp((days - 30) / 30); + highlight('age', alpha, item.Age.cell); + item.Age.cell.classList.add(alpha > 0.5 ? 'light' : 'dark'); + } + } + const requestor = item.Requestor, modified = item['Modified By'], assigned = item['Prim Resp']; + if (requestor && modified && assigned) { + if (requestor.text === modified.text && requestor.text !== assigned.text && assigned.text !== 'Unassigned') highlight('reply', null, row); + if (modified.text !== requestor.text && modified.text !== assigned.text) highlight('userModified', null, modified.cell); + } + const person = assigned || item.Responsibility; + if (person) highlight('person', person.text, person.cell); + if (item.Title) highlight('title', item.Title.text, item.Title.cell); + for (const name of LINK_COLUMNS) { + const link = item[name] && $('a', item[name].cell); + if (link) handleLink('report', link); + } + } + + // Local datetime controls write back to TDX's original named inputs, which must + // remain in the form for its validators and submission format to keep working. + function enhanceCalendar(calendar) { + const existing = calendars.get(calendar); + if (existing?.input.isConnected) { existing.sync(); return; } + const input = make('input', calendar.className); + input.type = 'datetime-local'; + input.id = `${calendar.id}-new`; + input.setAttribute('aria-label', calendar.getAttribute('aria-label') || (calendar.id === 'txtStartDate' ? 'Start date and time' : 'End date and time')); + const label = make('span'); + const buttons = make('div', 'calBox'); + const sync = () => { + const date = moment(calendar.value, DATE_FORMAT); + input.value = date.isValid() ? date.format(LOCAL_FORMAT) : ''; + label.textContent = date.isValid() ? date.calendar() : ''; + input.disabled = calendar.disabled; + input.readOnly = calendar.readOnly; + }; + const commit = date => { + calendar.value = date?.isValid() ? date.format(DATE_FORMAT) : ''; + sync(); + calendar.dispatchEvent(new Event('input', {bubbles: true})); + calendar.dispatchEvent(new Event('change', {bubbles: true})); + }; + input.addEventListener('input', () => commit(input.value ? moment(input.value, LOCAL_FORMAT, true) : null)); + calendar.addEventListener('change', sync); + for (const [text, action, value] of [['+1 day', 'add', {days: 1}], ['+1 week', 'add', {weeks: 1}], ['+1 month', 'add', {months: 1}], ['12PM', 'set', {hour: 12, minute: 0}], ['5PM', 'set', {hour: 17, minute: 0}]]) { + const button = make('button', 'btn btn-calendar', text); + button.type = 'button'; + button.addEventListener('click', () => { + if (calendar.disabled || calendar.readOnly) return; + let date = moment(calendar.value, DATE_FORMAT); + if (!date.isValid()) date = moment(); + date[action](value); + const day = date.isoWeekday(); + if (day >= 5 && date.hour() * 60 + date.minute() > 17 * 60) date.add(8 - day, 'days').hour(8).minute(0); + else if (day > 5) date.add(8 - day, 'days'); + commit(date); + }); + buttons.append(button); + } + calendar.style.display = 'none'; + calendar.after(input, label, buttons); + calendars.set(calendar, {input, sync}); + sync(); // Do not rewrite empty/invalid original fields during initialization. + } + + function enhanceTicket() { + const header = $('#thTicket_spnTitle, #upTaskHeader>div>h1'); + if (!header) return; + const title = [...header.childNodes].filter(node => !node.classList?.contains('qBox')).map(node => node.textContent).join('').trim(); + if (title && document.title !== title) document.title = title; + const queue = $('#ctlAttribute2285 span.wrap-text')?.textContent.trim(); + let box = $('.qBox', header); + if (queue) { + if (!box) header.append(box = make('span', 'qBox')); + if (box.textContent !== queue) box.textContent = queue; + resetPaint(box); + const palette = QUEUES[queue.toLowerCase()]; + if (palette) paint(box, {backgroundColor: palette.bg, color: palette.txt}); + } else box?.remove(); + for (const [selector, source] of [['#btnUpdateTicket, #divUpdateFromActions', 'Update'], ['#liAddTicketTask>a', 'TicketTaskNew'], ['#divAddTaskTemplate>a', 'TicketAddTaskTemplate']]) { + all(selector).forEach(link => handleLink(source, link)); + } + } + + function enhanceHeading(heading) { + const text = heading.textContent; + resetPaint(heading); + heading.classList.remove('reportTitle'); + highlight('report', text, heading); + if (!text.startsWith('{')) return; + try { + const data = JSON.parse(text); + const widget = heading.closest('.tdx-dashboard__widget-container') || heading.parentElement?.parentElement?.parentElement; + const body = widget && $('.tdx-widget', widget); + const url = new URL(data.url, location.href); + if (!body || typeof data.name !== 'string' || typeof data.url !== 'string' || !['http:', 'https:'].includes(url.protocol)) return; + const frame = make('iframe', 'customEmbed'); + frame.src = url.href; + frame.title = data.name; + heading.textContent = data.name; + body.replaceWith(frame); + } catch { /* Ordinary headings and incomplete JSON are left as text. */ } + } + + // CKEditor runs in a separate document, so it needs both the stylesheet and + // current root variables. Do not persist theme colors into editable ticket HTML. + function styleEditor(editor) { + const doc = editor.document?.$; + if (!doc?.head) return; + const existing = doc.getElementById('tdxuserscript-style'); + if (existing && editorDocuments.get(doc) === revision) return; + if (!existing) { + const style = doc.createElement('style'); + style.id = 'tdxuserscript-style'; + style.textContent = customStyles; + doc.head.append(style); + } + doc.documentElement.classList.remove('lightMode', 'darkMode'); + doc.documentElement.classList.add(document.documentElement.classList.contains('darkMode') ? 'darkMode' : 'lightMode'); + doc.documentElement.style.cssText = document.documentElement.style.cssText; + editorDocuments.set(doc, revision); + } + + function enhanceEditors() { + if (typeof CKEDITOR === 'undefined') return; + for (const editor of Object.values(CKEDITOR.instances)) { + if (!editors.has(editor)) { + editors.add(editor); + editor.on('contentDom', () => styleEditor(editor)); + editor.on('instanceReady', () => styleEditor(editor)); + } + styleEditor(editor); + } + } + + // Open shadow roots isolate CSS. Inject once, then observe their own child + // changes; custom properties naturally inherit from the surrounding document. + function enhanceShadow(host) { + const root = host.shadowRoot; + if (!root) return; + if (!root.querySelector('#customStyles')) { + const style = make('style'); + style.id = 'customStyles'; + style.textContent = customStyles; + root.append(style); + } + if (!shadowRoots.has(root)) { + shadowRoots.add(root); + new MutationObserver(() => { + all('tdx-close-x', root).forEach(enhanceShadow); + }).observe(root, {childList: true, subtree: true}); + } + all('tdx-close-x', root).forEach(enhanceShadow); + } + + // One animation-frame batch handles each burst of TDX updates. Disconnecting + // during our own edits prevents self-triggered observer loops and page freezes. + function schedule(root) { + pending.add(root); + if (!frameRequest) frameRequest = requestAnimationFrame(flush); + } + + const observer = new MutationObserver(records => { + for (const record of records) { + const target = record.target.nodeType === 1 ? record.target : record.target.parentElement; + if (!target || target.closest('style, script, #settingsMenu')) continue; + schedule(target.closest('table') || target); + } + }); + + function flush() { + frameRequest = 0; + // Include records queued since the observer last ran before disconnecting. + for (const record of observer.takeRecords()) { + const target = record.target.nodeType === 1 ? record.target : record.target.parentElement; + if (target && !target.closest('style, script, #settingsMenu')) pending.add(target.closest('table') || target); + } + observer.disconnect(); + const roots = [...pending].filter(root => root === document || root.isConnected); + pending.clear(); + try { + for (const root of roots) { + if (roots.some(other => other !== root && other.contains(root))) continue; + const select = selector => [...(root.matches?.(selector) ? [root] : []), ...all(selector, root)]; + select('table').forEach(parseTable); + select('#txtStartDate, #txtEndDate').forEach(enhanceCalendar); + select('.tdx-control-bar__title').forEach(enhanceHeading); + select('tdx-search-bar, .js_sidePanelX, tdx-close-x').forEach(enhanceShadow); + } + enhanceTicket(); + enhanceEditors(); + injectToolbar(); + const headline = $('.tdx-headerbar-headline a'); + if (headline && headline.textContent !== 'Purdue University IT') headline.textContent = 'Purdue University IT'; + } finally { + observer.observe(document.body, {childList: true, subtree: true, characterData: true}); + } + } + + // The status request is independent of rendering. A failed request must say + // "unavailable", rather than claiming every Purdue service is operational. + async function loadStatus(container) { + const icon = $('#statusIcon', container), text = $('#statusText', container), time = $('#statusTime', container); + try { + const response = await fetch('https://it.purdue.edu/api-status/messages.php', {signal: AbortSignal.timeout(10000)}); + if (!response.ok) throw new Error('Status request failed'); + const {Results} = await response.json(); + if (!Array.isArray(Results)) throw new Error('Unexpected status response'); + const message = Results[1] || 'All systems operational.'; + text.textContent = message; + icon.className = `fa-solid ${/^(?=(?:\w+\s){0,5}?(?:operational|green)\b(?!-))(\w+(?:\s\w+){0,5})/m.test(message) ? 'fa-check' : 'fa-triangle-exclamation'}`; + const date = Results[0] && moment(Results[0]); + time.textContent = date?.isValid() ? date.fromNow() : ''; + } catch { + text.textContent = 'Service status unavailable. Open Status for details.'; + time.textContent = ''; + icon.className = 'fa-solid fa-triangle-exclamation'; + } + } + + // Build repeated form controls from data; all original choices remain available. + function injectToolbar() { + const search = $('#globalSearchBar'); + if (!search || $('#settingsButton')) return; + const radios = (name, choices) => choices.map(([value, label]) => `
`).join(''); + const wrapper = make('span'); + wrapper.innerHTML = ` + `; + search.after(wrapper); + const form = $('form', wrapper), menu = $('#settingsMenu', wrapper), button = $('#settingsButton', wrapper); + button.addEventListener('click', () => { + const visible = menu.classList.toggle('show'); + menu.classList.toggle('hide', !visible); + button.setAttribute('aria-expanded', String(visible)); + }); + form.addEventListener('submit', event => event.preventDefault()); + const custom = form.elements.customHighlights; + custom.addEventListener('input', () => { + try { + if (!Array.isArray(JSON.parse(custom.value))) throw new Error(); + custom.setCustomValidity(''); + } catch { custom.setCustomValidity('Enter a JSON array of highlight rules.'); } + }); + form.addEventListener('change', saveSettings); + $('#colorPopup', wrapper).addEventListener('click', () => { + colorWindow = window.open(`${TOOLS_ORIGIN}/ecnuds/tdx/userscript/coloreditor`, 'TDX Color Editor', 'popup=true'); + // The cross-origin editor initiates the colorsReady handshake on load. + }); + $('#tdx-toolbar-links')?.remove(); + const links = make('span'); + links.id = 'tdx-toolbar-links'; + links.innerHTML = `
Status
+ `; + search.parentElement.prepend(links); + fillSettings(); + loadStatus(links); + } + + // Only the trusted editor popup may exchange settings. The SSO helper forwards + // its token solely to TDX Tools; never print keys or message payloads to the console. + window.addEventListener('message', event => { + if (event.origin !== TOOLS_ORIGIN || !colorWindow || event.source !== colorWindow || !event.data || typeof event.data !== 'object') return; + if (event.data.colorsReady) event.source.postMessage({customColors: config.customHighlights}, TOOLS_ORIGIN); + if (Array.isArray(event.data.colorData)) { + const field = $('#customHighlights'); + if (field) { + field.value = JSON.stringify(event.data.colorData); + field.setCustomValidity(''); + saveSettings(); + } + } + }); + + function forwardSSO() { + if (location.pathname.toLowerCase() !== '/tdwebapi/api/auth/loginsso' || !window.opener) return; + const pre = $('pre'); + if (pre) { + window.opener.postMessage({tdxkey: pre.textContent}, TOOLS_ORIGIN); + window.close(); + } + } + + window.addEventListener('storage', event => { + if (event.key === 'userSettings' || event.key === null) { + config = readSettings(); + applySettings(); + } + }); + media.addEventListener('change', () => { + if (config.colorMode === 'auto') applySettings(); + }); + + // Semantic variables let the large compatibility stylesheet share one palette. + // Keep selector order/specificity: TDX combines Bootstrap, Kendo, Select2, and + // CKEditor, so their overlapping overrides cannot safely be globally reordered. + function themeVariables(mode) { + const names = 'bg-0 bg-1 bg-2 bg-3 bg-4 bg-5 col-100 col-200 col-300 col-400 col-500 col-600 col-700 col-red col-green col-blue col-yellow col-link txt-0 txt-1 txt-2 txt-3 txt-4 txt-5 txt-invert txt-hue border-0 border-1 col-reply col-currentuser shadow-1 shadow-2'.split(' '); + const shared = 'col-highlight-1 col-highlight-2 col-highlight-3 col-highlight-4 col-modified'.split(' '); + return `color-scheme: ${mode};` + [...names.map(name => `--${name}: var(--${mode}-${name});`), ...shared.map(name => `--${name}: var(--dark-${name});`)].join(''); + } + + const customStyles = ` + +/* Root Styles */ +:root { + --dark-bg-default: #000; + --light-bg-default: #fff; + --dark-col-default: #ab9266; + --light-col-default: #cbb080; + --dark-col-primary: var(--dark-col-default); + --light-col-primary: var(--light-col-default); + --dark-bg-primary: var(--dark-bg-default); + --light-bg-primary: var(--light-bg-default); + --dark-gold-accent: #ab9266; + --light-gold-accent: #c3aa7f; + --dark-col-green: #295f29; + --dark-col-red: #912c2c; + --dark-col-blue: #32577a; + --dark-col-yellow: #654a24; + --light-col-green: #e3fcea; + --light-col-red: #ff6190; + --light-col-blue: #e1f5fc; + --light-col-yellow: #ffefd8; + --dark-blue-accent: #4c778d; + --light-blue-accent: #4b3bb9; + --dark-green-accent: #559170; + --light-green-accent: var(--dark-green-accent); + --dark-bg-0: color-mix(in srgb,var(--dark-bg-primary),#fff 5%); + --dark-bg-1: color-mix(in srgb,var(--dark-bg-primary),#fff 12%); + --dark-bg-2: color-mix(in srgb,var(--dark-bg-primary),#fff 20%); + --dark-bg-3: color-mix(in srgb,var(--dark-bg-primary),#fff 25%); + --dark-bg-4: color-mix(in srgb,var(--dark-bg-primary),#fff 30%); + --dark-col-100: color-mix(in srgb,var(--dark-col-primary),#000 70%); + --dark-col-200: color-mix(in srgb,var(--dark-col-primary),#000 60%); + --dark-col-300: color-mix(in srgb,var(--dark-col-primary),#000 50%); + --dark-col-400: var(--dark-col-primary); + --dark-col-500: color-mix(in srgb, var(--dark-col-primary), #aaa 50%); + --dark-col-600: color-mix(in srgb,var(--dark-col-primary),#fff 35%); + --dark-col-700: color-mix(in srgb,var(--dark-col-primary),#fff 25%); + --dark-col-link: #d1dbff; + --dark-txt-0: #bbb; + --dark-txt-1: #fff; + --dark-txt-2: #ececec; + --dark-txt-3: #929292; + --dark-txt-4: #ffffff8c; + --dark-txt-5: var(--light-txt-1); + --dark-border-0: color-mix(in srgb,var(--dark-bg-primary),#fff 18%); + --dark-border-1: color-mix(in srgb,var(--dark-bg-primary),#fff 30%); + --dark-col-reply: #1e3438; + --dark-col-highlight-1: #ffdb0059; + --dark-col-highlight-2: #5f00ff5e; + --dark-col-highlight-3: #ff3b1859; + --dark-col-highlight-4: #53f74d59; + --dark-col-currentuser: #09482f; + --dark-col-modified: #ff660012; + --dark-txt-invert: 1; + --dark-txt-hue: 180deg; + --dark-shadow-1: rgba(200, 200, 200, 0.1); + --dark-shadow-2: rgba(200, 200, 200, 0.06); + --light-txt-1: #000; + --light-txt-2: #222; + --light-txt-3: #333; + --light-txt-4: #0000008c; + --light-txt-5: var(--dark-txt-1); + --light-col-100: color-mix(in srgb,var(--light-col-primary),#fff 85%); + --light-col-200: color-mix(in srgb,var(--light-col-primary),#fff 75%); + --light-col-300: color-mix(in srgb,var(--light-col-primary),#fff 65%); + --light-col-400: var(--light-col-primary); + --light-col-500: color-mix(in srgb, var(--light-col-primary), #aaa 50%); + --light-col-600: color-mix(in srgb,var(--light-col-primary),#111 70%); + --light-col-700: color-mix(in srgb,var(--light-col-primary),#000 75%); + --light-bg-0: color-mix(in srgb,var(--light-bg-primary),#000 0%); + --light-bg-1: color-mix(in srgb,var(--light-bg-primary),#000 7%); + --light-bg-2: color-mix(in srgb,var(--light-bg-primary),#000 13%); + --light-bg-3: color-mix(in srgb,var(--light-bg-primary),#000 20%); + --light-bg-4: color-mix(in srgb,var(--light-bg-primary),#000 25%); + --light-bg-5: color-mix(in srgb,var(--light-col-primary),#ffffffd7 75%); + --light-border-0: color-mix(in srgb,var(--light-bg-primary),#000 18%); + --light-border-1: color-mix(in srgb,var(--light-bg-primary),#000 30%); + --light-col-reply: #d3f2ff; + --light-col-highlight-1: #fff3c4; + --light-col-highlight-2: #e3c4ff; + --light-col-highlight-3: #f75f5f; + --light-col-highlight-4: #92ff84; + --light-col-currentuser: #5de2ad; + --light-col-modified: #ffe8d1; + --light-txt-invert: 0; + --light-txt-hue: 0deg; + --light-shadow-1: rgba(50, 50, 50, 0.1); + --light-shadow-2: rgba(50, 50, 50, 0.06); + --body-text-color: var(--txt-0); + --dark-bg-5: var(--dark-bg-4); + --light-txt-0: var(--light-txt-2); + --light-col-link: #4b3bb9; + --col-0: var(--col-400); + --col-1: var(--col-600); + --col-2: var(--col-500); +} + +/* Body */ +body { background-color: var(--bg-0) !important; color: var(--txt-1) !important; } +::-webkit-scrollbar-track { background: var(--bg-0) !important; } +::-webkit-scrollbar-thumb { background: var(--col-400) !important; border: 5px solid var(--bg-0) !important; } +::-webkit-scrollbar-thumb:hover { border: 3px solid var(--bg-0) !important; background: var(--col-400) !important; } +::-webkit-scrollbar-corner { background-color: var(--bg-0) !important; } +.tdx-overlay { background-color: color-mix(in srgb,var(--col-400),#00000000 25%) !important; } +.tdx-overlay__item { background-color: var(--bg-0) !important; color: var(--col-400) !important; } +.tdx-overlay__item:hover, .tdx-overlay__item:focus-visible { background-color: var(--col-400) !important; color: var(--col-100) !important; } +.tdx-navigation-tab-bar { background-color: initial !important; } +.tdx-leftnav__container { background-color: var(--bg-0) !important; border-color: var(--border-0) !important; box-shadow: 0 1px 3px var(--shadow-1),0 2px 10px var(--shadow-2) !important; } +.tdx-leftnav__item { color: var(--txt-1) !important; } +.tdx-leftnav__item--active .tdx-leftnav__link { background-color: var(--col-100) !important; color: var(--col-400) !important; } +.tdx-leftnav__link:hover, .tdx-leftnav__link:focus-visible { background-color: var(--col-100) !important; } +.tdx-main-container { background-color: var(--bg-0) !important; } +.tdx-layout-option__layout-preview { background-color: var(--col-100) !important; } +.tdx-layout-option__column { background-color: var(--bg-0) !important; } +.tdx-layout-selector__layout-option:hover, .tdx-layout-selector__layout-option:focus, .tdx-layout-selector__layout-option--selected { background-color: var(--col-400) !important; } +.tdx-list__section { color: var(--txt-2); } + +/* Script-Specific Additions */ +#settingsMenu { right: 50px; padding: 10px; width: 250px; background: var(--bg-1); } +#settingsMenu.hide { visibility: hidden; } +#settingsMenu h5 { margin-bottom: 2px; } +#settingsMenu h3 { padding-top: 10px; font-weight: 500; font-size: 18px; } +#settingsForm .flex { display: flex; gap: 24px; align-items: baseline; } +#settingsForm .textBox { display: flex; flex-direction: column; gap: 8px; } +.customEmbed { border: 0; width: 100%; height: 400px; } +.calBox { display: flex; justify-content: space-around; flex-wrap: wrap; padding-top: 4px; gap: 4px; max-width: 220px; } +#statusBox:hover #statusMsg { display: block; width: 400px; line-height: 22px; padding: 8px; right: 390px; } +#statusTime, #statusText { margin: 6px; } +#statusTime { font-size: 12px; } +#statusBox .fa-triangle-exclamation { color: #ff8c4c; } +#statusBox .fa-check { color: #0dbb28; } +.qBox { margin-left: 10px; padding: 5px; font-size: 20px; border-radius: 7px; } +.qHighlight { padding: 4px; border-radius: 8px; border-width: 2px; border-style: solid; white-space: nowrap; } +.btn-calendar { flex-grow: 1; padding: 2px; background-color: var(--col-500); color: var(--txt-5); } +.btn-calendar:hover { background-color: var(--col-400); color: var(--txt-5); } +.reportTitle { border-radius: 12px; padding-left: 10px !important; padding-right: 10px !important; } + +/* Others */ +.tdx-loading-animation { color: var(--col-400) !important; } +.refresh-indicator { background: linear-gradient(180deg,var(--bg-0),hsla(0,0%,100%,0)) !important; } +.breadcrumb { background-color: var(--bg-0); } +.breadcrumb>.active { color: var(--txt-3); } +p.row { gap: 8px; } + +/* Headers */ +h1, h2, h3, h4, h5 { color: var(--txt-1) !important; } +.tdx-header-container { background-color: var(--col-100) !important; } +.tdx-headerbar-headline, .tdx-headerbar-headline a { color: var(--col-1) !important; } +.tdx-headline { color: var(--txt-1) !important; } +.tdx-headline--underline { border-color: var(--border-0); } +.tdx-navigation-tabs__tab { color: var(--col-600) !important; } +.tdx-navigation-tabs__home-tab--active, .tdx-navigation-tabs__tab--active { background-color: var(--bg-0) !important; color: var(--col-400) !important; } +.tdx-navigation-tabs__tab:hover:not(.tdx-navigation-tabs__tab--active), .tdx-navigation-tabs__home-tab:hover { background-color: var(--col-200) !important; } +.tdx-navigation-tabs__home-tab--active:hover { background-color: var(--bg-0) !important; } +.tdx-dropdown--headline { color: var(--txt-1) !important; } +.tdx-dropdown:hover { background-color: var(--col-200) !important; } +.tdx-dropdown.active { background-color: var(--col-300) !important; } +.tdx-dropdown-popout, .tdx-dropdown-popout__menu { background-color: var(--bg-0) !important; } +.tdx-dropdown-popout__menu-item { background-color: var(--bg-0) !important; color: var(--txt-1) !important; } +.tdx-dropdown-popout__menu-item:hover, .tdx-dropdown-popout__menu-item:focus { color: var(--txt-1) !important; background-color: var(--col-300) !important; } +.tdx-dropdown-popout__menu-item--selected { color: var(--col-400) !important; } +.tdx-dropdown-popout__header { background-color: var(--bg-0) !important; color: var(--col-600) !important; } +.tdx-dropdown-popout__break { border-color: var(--border-0) !important; } +.tdx-dropdown__divider { background-color: var(--bg-0) !important; } +.tdx-dropdown__divider::before { background: linear-gradient(90deg,var(--bg-0),var(--border-0),var(--border-0),var(--border-0),var(--bg-0)) !important; } +.tdx-label { color: var(--txt-1) !important; } + +/* Inputs */ +.tdx-dropdown--dark, .tdx-searchbar__textbox--dark { background-color: var(--col-200) !important; color: var(--txt-1) !important; } +.tdx-dropdown--dark:hover, .tdx-dropdown--dark:focus-visible { background-color: var(--col-300) !important; } +.tdx-searchbar__textbox:not(.tdx-searchbar__textbox--dark) { background-color: var(--bg-0) !important; color: var(--txt-1) !important; } +.tdx-searchbar__textbox:hover:not(:focus), .tdx-searchbar:hover .tdx-searchbar__textbox:not(:focus) { background-color: var(--col-200) !important; } +.tdx-searchbar__textbox:focus, .tdx-searchbar__textbox:active, .tdx-searchbar__textbox:focus-within { border-color: var(--col-400) !important; } +.tdx-searchbar__textbox::placeholder, .tdx-searchbar__textbox--dark:hover:not(:focus)::placeholder, .tdx-searchbar:hover .tdx-searchbar__textbox--dark:not(:focus)::placeholder, .tdx-searchbar__textbox:hover:not(:focus)::placeholder, .tdx-searchbar:hover .tdx-searchbar__textbox:not(:focus)::placeholder { color: var(--col-400) !important; } +input[type=checkbox], input[type=radio] { accent-color: var(--col-400) !important; } +.tdx-checkbox { background-color: var(--bg-0) !important; border-color: var(--border-0) !important; } +.tdx-checkbox:checked { background-color: var(--col-400) !important; } +.tdx-checkbox:checked:disabled { background-color: var(--col-200) !important; } +.control-label, label { color: var(--txt-2) !important; } +.form-control { background-color: var(--bg-0); color: var(--txt-2); border-color: var(--border-0); } +.form-control:hover { border-color: var(--border-0); } +.form-control:focus { border-color: var(--col-400); } +.talu_holder .luac_default { color: var(--col-500); } +.talu_holder .bit-input input { background-color: var(--bg-0); } +.talu_holder .bit-box { background-color: var(--col-100); color: var(--col-600); } +.talu_holder .bit-box:hover, .talu_holder .bit-box:active { background-color: var(--col-300); color: var(--col-500); } +.talu_holder .bit-box a.closebutton { color: var(--col-600); } +.lu_tdac-auto ul { background: var(--bg-0) !important; box-shadow: 0 1px 3px var(--shadow-1),0 2px 10px var(--shadow-2) !important; } +.lu_tdac-auto ul li { color: var(--txt-1) !important; } +.lu_tdac-auto ul .auto-focus { background-color: var(--col-300); color: var(--col-500); } +.lu_tdac-auto ul .auto-focus div.talu_subcaption { color: var(--col-400); } +div.WhiteOut, [role=status]:has(div>div>.tdx-loading-animation) { background-color: var(--bg-0) !important; } +.ms-drop .ms-search input::placeholder { color: var(--txt-3); } +.ms-choice { background-color: var(--bg-0); color: var(--txt-3); } +.ms-drop { background-color: var(--bg-0) !important; border-color: var(--border-0); } +button.ms-choice { background-color: var(--bg-0) !important; border-color: var(--border-0) !important; } +button.ms-choice:hover { border-color: var(--border-0) !important; } +button.ms-choice:focus { border-color: var(--col-400) !important; } +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { background-color: var(--bg-1); } +.tdx-lookup-group div.select2-container.form-control a.select2-choices, .input-group div.select2-container.form-control a.select2-choice { border-color: var(--border-0) !important; } +.tdx-lookup-group.input-group { background-color: var(--bg-0) !important; } +.input-group:not(:has(.input-group-section)):not(.input-group--no-border) { border-color: var(--border-0); } +.input-group:focus, .input-group:active, .input-group:focus-within { border-color: var(--col-400) !important; } +div.input-group .input-group-btn .btn-file:last-child { color: var(--txt-3); } +.tdx-lookup-group { background-color: var(--bg-0); border-color: var(--border-0); } +.tdx-lookup-group:hover, .tdx-lookup-group .k-input:hover, .tdx-lookup-group .k-input:focus, .tdx-lookup-group .k-input:focus-within, .tdx-lookup-group.input-group:hover, .tdx-lookup-group.input-group:focus, .tdx-lookup-group.input-group:focus-within, .tdx-lookup-group select:hover, .tdx-lookup-group select:focus, .tdx-lookup-group select:focus-within, .tdx-lookup-group input:hover, .tdx-lookup-group input:focus, .tdx-lookup-group input:focus-within, .tdx-lookup-group input.form-control:hover, .tdx-lookup-group input.form-control:focus, .tdx-lookup-group input.form-control:focus-within, .tdx-lookup-group .k-input, .tdx-lookup-group.input-group, .tdx-lookup-group select, .tdx-lookup-group input, .tdx-lookup-group input.form-control { border-color: var(--border-0); } +.tdx-lookup-group .k-svg-icon.k-svg-i-caret-alt-down::after { color: var(--txt-1); } +.tdx-lookup-group:focus, .tdx-lookup-group:active, .tdx-lookup-group:focus-within { border-color: var(--col-400); } +.tdx-lookup-group .k-input .k-input-inner::selection, .tdx-lookup-group.input-group .k-input-inner::selection, .tdx-lookup-group select .k-input-inner::selection, .tdx-lookup-group input .k-input-inner::selection, .tdx-lookup-group input.form-control .k-input-inner::selection { background-color: var(--bg-2); } +.tdx-lookup-group>.select2-container-multi .select2-choices:not(div.attributeValue .select2-container-multi .select2-choices), .tdx-searchbar__textbox--border, .tdx-lookup-group__input { border-color: var(--border-0) !important; } +.tdx-input-group__textbox, .tdx-input-group__textarea { color: var(--txt-1) !important; background-color: var(--bg-0) !important; border-color: var(--border-0) !important; } +.k-input { background-color: var(--bg-0) !important; } +.k-input-solid { background-color: var(--bg-0); } +.k-list { background-color: var(--bg-0); color: var(--txt-1); } +.k-list-container.k-popup { background-color: var(--bg-0); } +.k-list-content .k-list-item:hover, .k-list-content .k-list-item:focus, .k-list-content .k-list-item.k-focus { background-color: var(--col-300); color: var(--txt-1); } +.k-list-content .k-list-item { color: var(--txt-1); background-color: var(--bg-0); } +.k-popup { color: var(--txt-1); } +.k-popup:has(.k-list) { box-shadow: 0 1px 3px var(--shadow-1),0 2px 10px var(--shadow-2); } +.tdx-lookup-group .k-input, .tdx-lookup-group.input-group, .tdx-lookup-group select, .tdx-lookup-group input, .tdx-lookup-group input.form-control { color: var(--txt-1); } +.tdx-lookup-group .k-input .k-button-solid-base, .tdx-lookup-group.input-group .k-button-solid-base, .tdx-lookup-group select .k-button-solid-base, .tdx-lookup-group input .k-button-solid-base, .tdx-lookup-group input.form-control .k-button-solid-base { background-color: var(--bg-0); } +.k-input-md .k-input-inner::placeholder, .form-control::placeholder { color: var(--txt-3) !important; } +.select2-container .select2-choice, .select2-container .select2-choices, .select2-container .select2-choices .select2-search-field input { background: var(--bg-0) !important; } +div.select2-container.form-control .select2-choice.select2-default { border-color: var(--border-0); } +.select2-container .select2-choice>.select2-chosen { color: var(--txt-1) !important; } +.select2-container .select2-choice.select2-default>.select2-chosen, .tdx-lookup-group>.select2-container-multi .select2-choices:not(div.attributeValue .select2-container-multi .select2-choices) .select2-search-field input.select2-default { color: var(--txt-3) !important; } +.select2-results .select2-no-results, .select2-results .select2-selection-limit { background: var(--bg-1) !important; } +.select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-selection-limit { color: var(--txt-1) !important; } +.select2-drop { background: var(--bg-0); color: var(--txt-1); } +.select2-drop { color: var(--txt-1) !important; background-color: var(--bg-0); } +.select2-drop .select2-search input { background-color: var(--bg-0); border-color: var(--border-0); } +.select2-search input { color: var(--txt-1); } +div.select2-drop.select2-drop-active, div.select2-drop.select2-drop-above.select2-drop-active { box-shadow: 0 1px 3px var(--shadow-1),0 2px 10px var(--shadow-2); background-color: var(--bg-0); } +.select2-drop .select2-results .select2-highlighted { color: var(--txt-1) !important; background-color: var(--col-300); } +.select2-results div.select2-result-label { color: var(--txt-1); } +.select2-container:focus-within, .select2-container:focus-within a { border-color: var(--col-400) !important; } +.form-group:has(select:not([multiple]):not([size])):hover::after { color: var(--col-400) !important; } +div.select2-container.form-control .select2-choice, div.js-tagformfield .select2-container-multi .select2-choices, div.js-tags-inlineedit .select2-container-multi .select2-choices, div.attributeValue .select2-container-multi .select2-choices, div.select2-listbox .select2-container-multi .select2-choices, div.select2-container.form-control.select2-container-multi .select2-choices, div.select2-container.form-control .select2-choice:hover, div.js-tagformfield .select2-container-multi .select2-choices:hover, div.js-tags-inlineedit .select2-container-multi .select2-choices:hover, div.attributeValue .select2-container-multi .select2-choices:hover, div.select2-listbox .select2-container-multi .select2-choices:hover, div.select2-container.form-control.select2-container-multi .select2-choices:hover { border-color: var(--border-0); } +div.select2-container-multi .select2-choices .select2-search-choice { background-color: var(--col-100); color: var(--col-600); } +div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close { color: var(--col-600) !important; } +div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:hover, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:focus-visible, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:active, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:active:hover, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:focus:active, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:focus:hover { color: var(--col-400) !important; } +div.select2-container-multi .select2-choices .select2-search-choice:hover, div.select2-container-multi .select2-choices .select2-search-choice:active { background-color: var(--col-300); color: var(--col-500); } +select.form-control:hover:not(:focus), select.form-control { border-color: var(--border-0); } +.k-chip-solid-base { background-color: var(--col-100) !important; color: var(--col-600) !important; } +.k-chip-action { color: var(--col-600) !important; } +.k-chip-action:hover, .k-chip-action:focus-visible, .k-chip-action:active, .k-chip-action:active:hover, .k-chip-action:focus:active, .k-chip-action:focus:hover { color: var(--col-400) !important; } +.tooltip-inner { background-color: var(--bg-0); color: var(--txt-1); box-shadow: 0 .5px 1px 1px var(--shadow-1),0 2px 4px var(--shadow-2),0 4px 6px var(--shadow-2); } +div#ui-datepicker-div .ui-datepicker-title select, div#ui-datepicker-div .ui-timepicker-div.ui-timepicker-oneLine select, div.ui-datepicker .ui-datepicker-title select, div.ui-datepicker .ui-timepicker-div.ui-timepicker-oneLine select { color: var(--txt-1); border-color: var(--bg-0); background-color: var(--bg-0); } +div#ui-datepicker-div .ui-datepicker-title select:hover, div#ui-datepicker-div .ui-timepicker-div.ui-timepicker-oneLine select:hover, div.ui-datepicker .ui-datepicker-title select:hover, div.ui-datepicker .ui-timepicker-div.ui-timepicker-oneLine select:hover { border-color: var(--border-0); } +div#ui-datepicker-div .ui-state-default, div#ui-datepicker-div .ui-state-default:hover, div.ui-datepicker .ui-state-default, div.ui-datepicker .ui-state-default:hover { border-color: var(--bg-0) !important; } +div#ui-datepicker-div .ui-datepicker-header, div.ui-datepicker .ui-datepicker-header { background: var(--col-100) !important; } +div#ui-datepicker-div th, div#ui-datepicker-div td, div.ui-datepicker th, div.ui-datepicker td { color: var(--txt-1); } +div#ui-datepicker-div .ui-state-highlight, div#ui-datepicker-div .ui-widget-content .ui-state-highlight, div#ui-datepicker-div .ui-widget-header .ui-state-highlight, div.ui-datepicker .ui-state-highlight, div.ui-datepicker .ui-widget-content .ui-state-highlight, div.ui-datepicker .ui-widget-header .ui-state-highlight { background: var(--col-100) !important; color: var(--col-400) !important; } +button:has(>.ui-icon-circle-triangle-e), span:has(>.ui-icon-circle-triangle-e), a:has(>.ui-icon-circle-triangle-e), button:has(>.fa-chevron-circle-right), span:has(>.fa-chevron-circle-right), a:has(>.fa-chevron-circle-right), button:has(>.ui-icon-circle-triangle-w), span:has(>.ui-icon-circle-triangle-w), a:has(>.ui-icon-circle-triangle-w), button:has(>.fa-chevron-circle-left), span:has(>.fa-chevron-circle-left), a:has(>.fa-chevron-circle-left) { color: var(--col-400); } + +/* Links */ +.tdx-dashboard__widget-container a:not(.btn):not(.k-icon) { color: var(--col-400) !important; } +.tdx-dashboard__widget-container a:not(.btn):not(.k-icon):hover, .tdx-dashboard__widget-container a:not(.btn):not(.k-icon):focus-visible { color: var(--col-500) !important; } +a { color: var(--col-400); background-color: initial !important; } +a:hover, a:focus-visible, .tdworkmgmt td a.visited-link:hover, .tdworkmgmt td a.visited-link:focus-visible { color: var(--col-500) !important; } +a:visited, .tdworkmgmt td a.visited-link, .tdworkmgmt td a:visited, .tdworkmgmt.table .hilite a:visited, .tdworkmgmt.table-contents div.row > div > a { color: var(--col-400); } +.tdworkmgmt.table-striped > tbody > tr.tdx-grid__group-header > td, .tdworkmgmt.table-bordered > tbody > tr.tdx-grid__group-header > td { background-color: var(--col-100); } +.tdx-time-panel, .tdx-time-panel__heading { color: var(--txt-1) !important; background-color: var(--bg-1) !important; border: 1px solid var(--border-0) !important; } +.tdx-leftnav__badge { color: var(--txt-1) !important; background-color: var(--col-300) !important; } +body .tdworkmgmt.table.table--vertical-lines > tbody > tr > td:not(:last-of-type) { border-right: 1px solid var(--border-0); } +.TDGroupingRow { color: var(--txt-1); } +a.disabledLink { color: var(--txt-1) !important; } +.k-grid a.tdx-link, .tdx-link:visited, .tdx-btn--link { color: var(--col-400) !important; } + +/* Buttons */ +.tdx-btn--secondary { background-color: var(--col-200) !important; color: var(--col-400) !important; border: none !important; } +.tdx-btn--secondary > a { color: var(--col-400) !important; } +.tdx-btn--secondary:hover, .tdx-btn--secondary:hover:focus, .tdx-btn--secondary:focus-visible { background-color: var(--col-300) !important; color: var(--col-600) !important; } +.tdx-action-menu-dropdown__popout { background-color: var(--bg-0) !important; box-shadow: 0 1px 3px var(--shadow-1),0 2px 10px var(--shadow-2) !important; } +.tdx-action-menu-dropdown-popout__menu-item:not(.tdx-dropdown-popout__header) { background-color: var(--bg-0) !important; color: var(--txt-1) !important; } +.tdx-action-menu-dropdown-popout__menu-item:hover, .tdx-action-menu-dropdown-popout__menu-item:focus { background-color: var(--col-300) !important; } +.tdx-action-menu__item--primary { color: var(--txt-5) !important; background: var(--col-600) !important; border-color: var(--col-600) !important; } +.tdx-action-menu__item--primary:hover, .tdx-action-menu__item--primary:hover:focus, .tdx-action-menu__item--primary:focus-visible { color: var(--txt-5) !important; background: var(--col-400) !important; border-color: var(--col-400) !important; } +.tdx-action-menu__item:not(.tdx-action-menu__item--primary) { color: var(--txt-1) !important; background: var(--bg-0) !important; } +.tdx-action-menu__item:not(.tdx-action-menu__item--primary):hover, .tdx-action-menu__item.tdx-action-menu__item:not(.tdx-action-menu__item--primary):hover:focus, .tdx-action-menu__item.tdx-action-menu__item:not(.tdx-action-menu__item--primary):focus-visible { background: var(--col-200) !important; color: var(--txt-2) !important; } +.tdx-primary-button-override { background-color: var(--col-600) !important; color: var(--txt-5) !important; border-color: var(--col-600) !important; } +.tdx-primary-button-override:hover, .tdx-primary-button-override:hover:focus, .tdx-primary-button-override:focus-visible { background: var(--col-400) !important; border-color: var(--col-400) !important; color: var(--txt-5) !important; } +.tdx-primary-button-override.active, .tdx-primary-button-override.active:hover, .tdx-primary-button-override.active:focus, .tdx-primary-button-override:active, .tdx-primary-button-override:active:hover, .tdx-primary-button-override:active:focus, .tdx-primary-button-override:focus:not(:focus-visible,.active,:active,:hover) { background-color: var(--col-600) !important; color: var(--txt-5) !important; border-color: var(--col-600) !important; } +span.input-group-btn .btn-default { color: var(--txt-3); } +span.input-group-btn .btn-default:hover, span.input-group-btn .btn-default:focus-visible, span.input-group-btn .btn-default:active, span.input-group-btn .btn-default:active:hover, span.input-group-btn .btn-default:focus:active, span.input-group-btn .btn-default:focus:hover { color: var(--txt-2); } +div.input-group .input-group-btn .btn-file:last-child:hover, div.input-group .input-group-btn .btn-file:last-child:hover:focus, div.input-group .input-group-btn .btn-file:last-child:focus-visible { background-color: var(--col-100); color: var(--txt-2); } +.btn.active, .btn.active:hover, .btn.active:focus, .btn:active, .btn:active:hover, .btn:active:focus { background: var(--col-300); color: var(--txt-5); } +.btn-default { color: var(--txt-1); } +.btn-default:hover, .btn-default:hover:focus, .btn-default:focus-visible { background: var(--col-200); color: var(--txt-1); } +.tdx-btn--tertiary { color: var(--txt-1); } +.tdx-btn--tertiary:hover { background: var(--col-200); color: var(--txt-2); } +.tdx-btn--primary { background-color: var(--col-600) !important; color: var(--txt-5) !important; border-color: var(--col-600) !important; } +.tdx-btn--primary:hover, .tdx-btn--primary:hover:focus, .tdx-btn--primary:focus-visible { background: var(--col-400) !important; border-color: var(--col-400) !important; color: var(--txt-5) !important; } +.feed-button-group .btn-primary:hover, .feed-button-group .btn-primary:hover:focus, .feed-button-group .btn-primary:focus-visible { background: var(--col-200); } +.feed-button-group .btn-primary.active, .feed-button-group .btn-primary.active:hover, .feed-button-group .btn-primary.active:focus, .feed-button-group .btn-primary:active, .feed-button-group .btn-primary:active:hover, .feed-button-group .btn-primary:active:focus { background: var(--col-300); } +#btnSave.btn-primary, #btnClientSave.btn-primary, #btnRunReport.btn-primary, #btnApply.btn-primary, #btnAddSelected.btn-primary, #btnSubmit.btn-primary { color: var(--txt-5) !important; background-color: var(--col-500) !important; border-color: var(--col-500) !important; } +#btnSave.btn-primary:hover, #btnSave.btn-primary:hover:focus, #btnSave.btn-primary:focus-visible, #btnClientSave.btn-primary:hover, #btnClientSave.btn-primary:hover:focus, #btnClientSave.btn-primary:focus-visible, #btnRunReport.btn-primary:hover, #btnRunReport.btn-primary:hover:focus, #btnRunReport.btn-primary:focus-visible, #btnApply.btn-primary:hover, #btnApply.btn-primary:hover:focus, #btnApply.btn-primary:focus-visible, #btnAddSelected.btn-primary:hover, #btnAddSelected.btn-primary:hover:focus, #btnAddSelected.btn-primary:focus-visible, #btnSubmit.btn-primary:hover, #btnSubmit.btn-primary:hover:focus, #btnSubmit.btn-primary:focus-visible { background: var(--col-400) !important; border-color: var(--col-400) !important; color: var(--txt-5) !important; } +#btnSave.btn-primary.active, #btnSave.btn-primary.active:hover, #btnSave.btn-primary.active:focus, #btnSave.btn-primary:active, #btnSave.btn-primary:active:hover, #btnSave.btn-primary:active:focus, #btnClientSave.btn-primary.active, #btnClientSave.btn-primary.active:hover, #btnClientSave.btn-primary.active:focus, #btnClientSave.btn-primary:active, #btnClientSave.btn-primary:active:hover, #btnClientSave.btn-primary:active:focus, #btnRunReport.btn-primary.active, #btnRunReport.btn-primary.active:hover, #btnRunReport.btn-primary.active:focus, #btnRunReport.btn-primary:active, #btnRunReport.btn-primary:active:hover, #btnRunReport.btn-primary:active:focus, #btnApply.btn-primary.active, #btnApply.btn-primary.active:hover, #btnApply.btn-primary.active:focus, #btnApply.btn-primary:active, #btnApply.btn-primary:active:hover, #btnApply.btn-primary:active:focus, #btnAddSelected.btn-primary.active, #btnAddSelected.btn-primary.active:hover, #btnAddSelected.btn-primary.active:focus, #btnAddSelected.btn-primary:active, #btnAddSelected.btn-primary:active:hover, #btnAddSelected.btn-primary:active:focus, #btnSubmit.btn-primary.active, #btnSubmit.btn-primary.active:hover, #btnSubmit.btn-primary.active:focus, #btnSubmit.btn-primary:active, #btnSubmit.btn-primary:active:hover, #btnSubmit.btn-primary:active:focus { background: var(--col-600) !important; border-color: var(--col-600) !important; color: var(--txt-5) !important; } +#btnSave.btn-primary:focus:not(:focus-visible,.active,:active,:hover), #btnClientSave.btn-primary:focus:not(:focus-visible,.active,:active,:hover), #btnRunReport.btn-primary:focus:not(:focus-visible,.active,:active,:hover), #btnApply.btn-primary:focus:not(:focus-visible,.active,:active,:hover), #btnAddSelected.btn-primary:focus:not(:focus-visible,.active,:active,:hover), #btnSubmit.btn-primary:focus:not(:focus-visible,.active,:active,:hover) { background: var(--col-400) !important; border-color: var(--col-400) !important; color: var(--txt-5) !important; } +.btn-tertiary, .btn-info, .ButtonCellTopPadding .ButtonCellTop .btn-primary { color: var(--txt-1); background-color: var(--col-100); } +.btn-tertiary:hover, .btn-tertiary:hover:focus, .btn-tertiary:focus-visible, .btn-info:hover, .btn-info:hover:focus, .btn-info:focus-visible, .ButtonCellTopPadding .ButtonCellTop .btn-primary:hover, .ButtonCellTopPadding .ButtonCellTop .btn-primary:hover:focus, .ButtonCellTopPadding .ButtonCellTop .btn-primary:focus-visible { background-color: var(--col-200); color: var(--txt-1); } +.modal-footer .btn-default { background-color: var(--col-200) !important; color: var(--col-400) !important; border: none !important; } +.modal-footer .btn-default:hover, .modal-footer .btn-default:hover:focus, .modal-footer .btn-default:focus-visible { background-color: var(--col-300) !important; color: var(--col-600) !important; } +.TabButtonCell .btn-primary, .TabButtonCell .btn-info, .tab-row .btn-primary, #divViewButtons, .TabButtonCell .btn-primary:active, .tab-row .btn-primary:active { color: var(--txt-2); } +.TabButtonCell .btn-primary:hover, .TabButtonCell .btn-primary:hover:focus, .TabButtonCell .btn-primary:focus-visible, .TabButtonCell .btn-info:hover, .TabButtonCell .btn-info:hover:focus, .TabButtonCell .btn-info:focus-visible, .tab-row .btn-primary:hover, .tab-row .btn-primary:hover:focus, .tab-row .btn-primary:focus-visible, #divViewButtons:hover, #divViewButtons:hover:focus, #divViewButtons:focus-visible, .TabButtonCell .btn-primary:active:hover, .TabButtonCell .btn-primary:active:hover:focus, .TabButtonCell .btn-primary:active:focus-visible, .tab-row .btn-primary:active:hover, .tab-row .btn-primary:active:hover:focus, .tab-row .btn-primary:active:focus-visible { background: var(--col-200); color: var(--txt-2); } +.TabButtonCell .btn-danger, .tab-row .btn-danger { background: var(--bg-5); color: var(--txt-2); } +.TabButtonCell .btn-danger:hover, .TabButtonCell .btn-danger:hover:focus, .TabButtonCell .btn-danger:focus-visible, .tab-row .btn-danger:hover, .tab-row .btn-danger:hover:focus, .tab-row .btn-danger:focus-visible { background: var(--col-0); color: var(--txt-2); } +.ButtonCellTop { background: var(--bg-0); } +.feed-button-group .btn-primary { color: var(--txt-2) !important; } +.btn-secondary { background-color: var(--col-200); color: var(--col-400); border-color: var(--col-200); } +.btn-secondary:hover, .btn-secondary:hover:focus, .btn-secondary:focus-visible { background-color: var(--col-300) !important; color: var(--col-500) !important; border-color: var(--col-300) !important; } +.btn-danger { background-color: var(--col-200); color: var(--col-400); border-color: var(--col-200); } +.btn-danger:hover, .btn-danger:hover:focus, .btn-danger:focus-visible { background-color: var(--col-300); color: var(--col-500); border-color: var(--col-300); } +.tdx-button-bar .btn-primary { color: var(--txt-1); background-color: var(--col-100); border-color: var(--col-100); } +.tdx-button-bar .btn-primary:hover, .tdx-button-bar .btn-primary:hover:focus, .tdx-button-bar .btn-primary:focus-visible { background-color: var(--col-300); background: var(--col-300); color: var(--txt-2); border-color: var(--col-300); } +.tdx-button-bar .btn-primary:visited { color: var(--txt-1); } +.tdx-button-bar .btn-danger { color: var(--col-400); } +.tdx-button-bar .btn-danger:hover, .tdx-button-bar .btn-danger:hover:focus, .tdx-button-bar .btn-danger:focus-visible { background-color: var(--col-300); color: var(--col-500); border-color: var(--col-300); } +.tdx-tile-list__item-container, .tdx-tile-list__item-container:visited { background-color: var(--col-100) !important; color: var(--col-400) !important; } +.tdx-tile-list__item-container:hover, .tdx-tile-list__item-container:focus { background-color: var(--col-400) !important; color: var(--col-100) !important; } +a.btn.btn-primary { background-color: var(--col-600) !important; color: var(--txt-5) !important; border-color: var(--col-600) !important; } +a.btn.btn-primary:hover { background-color: var(--col-400) !important; color: var(--txt-5) !important; border-color: var(--col-400) !important; } +.btn-primary { background-color: var(--col-600); color: var(--txt-5); border-color: var(--col-600); } +.btn-primary:hover, .btn-primary:hover:focus, .btn-primary:focus-visible { background: var(--col-400); color: var(--txt-5); border-color: var(--col-400); } +td > a.btn.btn-primary { background-color: var(--col-400) !important; color: var(--txt-5) !important; border-color: var(--col-400); } +td > a.btn.btn-primary:hover { background-color: var(--col-500) !important; color: var(--txt-5) !important; border-color: var(--col-500); } +.btn-default.active, .btn-default.active:hover, .btn-default.active:focus, .btn-default:active, .btn-default:active:hover, .btn-default:active:focus { background: var(--col-100) !important; color: var(--txt-1) !important; border-color: var(--col-100) !important; } +a.btn.btn-link.tdx-primary-button-override { color: var(--txt-5) !important; } +.btn-link, .btn.btn-link, .sidebar-btn.btn-link, .btn-link:hover, .btn.btn-link:hover, .sidebar-btn.btn-link:hover { color: var(--col-400) !important; } +.btn-link[role=button]:hover, .btn.btn-link[role=button]:hover, .sidebar-btn.btn-link[role=button]:hover, a.btn.btn-link.tdx-tertiary-button-override:hover { background-color: var(--col-200) !important; } +.tdx-calendar .cal-views { border-color: var(--col-400) !important; } +.tdx-calendar .cal-view { color: var(--txt-2) !important; } +.tdx-calendar .cal-view.light-blue-bg { background-color: var(--col-400) !important; color: var(--txt-2) !important; } +.tdx-calendar .cal-view:not(.cal-view.light-blue-bg):hover, .tdx-calendar .cal-view:hover:focus, .tdx-calendar .cal-view:focus-visible { background-color: var(--col-200) !important; } +.tdx-calendar #btnDatePicker { background-color: var(--bg-0) !important; color: var(--col-400) !important; } +.tdx-calendar #btnDatePicker:hover, .tdx-calendar #btnDatePicker:focus-visible, .tdx-calendar #btnDatePicker:active, .tdx-calendar #btnDatePicker:active:hover, .tdx-calendar #btnDatePicker:focus:active, .tdx-calendar #btnDatePicker:focus:hover { color: var(--col-600) !important; } +button:has(.ui-icon-circle-triangle-w), span:has(.ui-icon-circle-triangle-w), button:has(.ui-icon-circle-triangle-e), span:has(.ui-icon-circle-triangle-e) { color: var(--col-400) !important; } +button:has(.ui-icon-circle-triangle-e):hover, button:has(.ui-icon-circle-triangle-e):focus-visible, button:has(.ui-icon-circle-triangle-e):active, button:has(.ui-icon-circle-triangle-e):active:hover, button:has(.ui-icon-circle-triangle-e):focus:active, button:has(.ui-icon-circle-triangle-e):focus:hover, span:has(.ui-icon-circle-triangle-e):hover, span:has(.ui-icon-circle-triangle-e):focus-visible, span:has(.ui-icon-circle-triangle-e):active, span:has(.ui-icon-circle-triangle-e):active:hover, span:has(.ui-icon-circle-triangle-e):focus:active, span:has(.ui-icon-circle-triangle-e):focus:hover, button:has(.ui-icon-circle-triangle-w):hover, button:has(.ui-icon-circle-triangle-w):focus-visible, button:has(.ui-icon-circle-triangle-w):active, button:has(.ui-icon-circle-triangle-w):active:hover, button:has(.ui-icon-circle-triangle-w):focus:active, button:has(.ui-icon-circle-triangle-w):focus:hover, span:has(.ui-icon-circle-triangle-w):hover, span:has(.ui-icon-circle-triangle-w):focus-visible, span:has(.ui-icon-circle-triangle-w):active, span:has(.ui-icon-circle-triangle-w):active:hover, span:has(.ui-icon-circle-triangle-w):focus:active, span:has(.ui-icon-circle-triangle-w):focus:hover { color: var(--col-600) !important; } +.ui-widget-header { background: var(--bg-0) !important; } +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-widget-content, .ui-widget.ui-widget-content { background: var(--bg-0) !important; border-color: var(--border-0) !important; } +.tdx-calendar .fc-day-header { color: var(--col-400) !important; } +.tdx-calendar .fc-day-number.fc-other-month a:first-child { color: var(--col-200) !important; } +.tdx-calendar .fc-day-number a { color: var(--col-600) !important; } +.tdx-calendar .ui-state-highlight a, .tdx-calendar .ui-widget-content .ui-state-highlight a, .tdx-calendar .ui-widget-header .ui-state-highlight a { color: var(--col-400) !important; } +.tdx-calendar .ui-state-highlight { background: var(--col-100) !important; } +.td-day-off { background: var(--bg-2) !important; } +.label-primary, .tdworkmgmt.table .hilite .label.label-primary { background-color: var(--col-300); } +.label-info, .label-success, .label-default { color: var(--col-200); background-color: var(--col-500); } +.tdTilemd, .tdTilesm, .tdTilemd.secondary, .tdTilesm.secondary, .tdTilesm.tertiary { background-color: var(--col-300); border-color: var(--col-300); } +.block-actions .btn.btn-link { color: var(--txt-1); } +.block-actions .btn.btn-link:hover, .block-actions .btn.btn-link:hover:focus, .block-actions .btn.btn-link:focus-visible { background: var(--bg-5); color: var(--txt-1); } +.btn-link:hover, .btn-link:focus-visible, .btn.btn-link:hover, .btn.btn-link:focus-visible, .sidebar-btn.btn-link:hover, .sidebar-btn.btn-link:focus-visible { color: var(--col-500); } +ul.dropdown-menu .btn.btn-link[data-v-775a00fa] { color: var(--txt-1) !important; } +ul.dropdown-menu .btn.btn.btn-link[data-v-775a00fa]:hover { background-color: var(--col-300) !important; color: var(--txt-1) !important; } +.copyButton { color: var(--txt-2) !important; background-color: var(--bg-0) !important; border-color: var(--border-0) !important; } +.copyButton:hover { background-color: var(--bg-2) !important; } +.js-open-monitor .btn { color: var(--col-1); } +.js-open-monitor .btn:hover { color: var(--col-2); } +.btn:focus:not(:focus-visible,.active,:active,:hover) { color: var(--txt-5); background-color: var(--col-400); } +.btn:hover, .btn:hover:focus, .btn:focus-visible { color: var(--txt-5); background-color: var(--col-600); } +.popover { background-color: var(--bg-0); box-shadow: 0 5px 10px var(--shadow-1); border-color: var(--border-0); } +.popover.right > .arrow { border-right-color: var(--border-0); } +.popover.right > .arrow::after { border-right-color: var(--bg-0); } + +/* Tables */ +table.table { color: var(--txt-1) !important; border-color: var(--border-0); } +.tdx-table__th { background-color: var(--bg-0) !important; color: var(--col-600) !important; } +.tdx-table__td { color: var(--txt-2) !important; } +.tdx-table-fade-right { background: linear-gradient(to right,transparent,var(--bg-0)) !important; } +thead, tbody, tfoot, tr, td, th { border-color: var(--border-0); } +.tdx-pagination-bar { background-color: var(--bg-0) !important; } +.tdx-pagination-bar__item-count { color: var(--txt-2) !important; } +.tdx-dashboard__widget-container { box-shadow: 0 .5px 1px 1px var(--shadow-1),0 2px 4px var(--shadow-2),0 4px 6px var(--shadow-1) !important; } +.tdx-widget-selector__widget-option { box-shadow: 0 .5px 1px 1px var(--shadow-1),0 2px 4px var(--shadow-2),0 4px 6px var(--shadow-2) !important; } +.tdx-widget-selector__widget-name { color: var(--txt-1) !important; } +.tdx-widget-selector__category { color: var(--col-600) !important; } +.tdx-widget-selector__widget-option:hover, .tdx-widget-selector__widget-option:focus { outline-color: var(--col-400) !important; } +.tdx-dashboard-row-editor { background-color: var(--col-100) !important; box-shadow: 0 .5px 1px 1px var(--shadow-1),0 2px 4px var(--shadow-2),0 4px 6px var(--shadow-2) !important; } +.tdx-dashboard-row-editor__column { background-color: var(--bg-0) !important; } +.tdx-dashboard-row-editor__selected-widget { background-color: var(--bg-0) !important; outline-color: var(--border-0) !important; } +.tdx-dashboard-row-editor__selected-widget-category-text { color: var(--col-600) !important; } +.tdx-dashboard-editor__add-widget { color: var(--txt-3) !important; } +.tdx-dashboard-editor__add-widget:hover, .tdx-dashboard-editor__add-widget:focus-visible { box-shadow: 0 .5px 1px 1px var(--shadow-1),0 2px 4px var(--shadow-2),0 4px 6px var(--shadow-2) !important; } +.tdx-control-bar { background-color: var(--bg-0) !important; } +.tdx-control-bar__title { color: var(--txt-1) !important; } +.tdx-confirmation-dialog { background-color: var(--bg-0); color: var(--col-600); } +.tdx-confirmation-dialog__text-area { color: var(--col-600); } +.k-grid, .k-pager, .k-table { background-color: var(--bg-0) !important; color: var(--txt-1) !important; } +.k-table-tbody>.k-table-row:not(.k-table-group-row):first-child>.k-table-td, .k-table-tbody>.k-table-row:not(.k-table-group-row)>.k-table-td { border-color: var(--border-0) !important; } +.k-grid-header, .k-grid-pager { background-color: var(--bg-0) !important; color: var(--txt-0) !important; } +.k-grid .k-table-row.k-table-alt-row { background-color: var(--bg-0); } +.k-grid .k-table-row { background-color: var(--bg-1); } +.k-grid .k-table-tbody > .k-alt.k-table-row:not(.k-detail-row) { background-color: var(--bg-0); } +.k-table-th { background-color: var(--bg-0) !important; } +.k-grid .k-grid-header .k-table-th { color: var(--txt-4) !important; } +.k-grid .k-grid-header .k-table-th, .k-table-tbody .k-group-cell, .k-table-th, .k-table-td, td.k-table-td { background-color: unset; } +.k-grid .k-table-tbody>.k-table-row:not(.k-detail-row):not([style]):hover, .k-grid .k-table-tbody>.k-table-row:not(.k-detail-row).k-hover { background-color: var(--bg-1) !important; } +.k-grid .k-table-tbody>.k-table-alt-row:not(.k-detail-row):not([style]):hover, .k-grid .k-table-tbody>.k-table-alt-row:not(.k-detail-row).k-hover:not([style]) { background-color: var(--bg-0) !important; } +button.k-pager-nav.k-button.k-button-flat.k-button-flat-base.k-icon-button.k-button-md, .k-pager-md .k-pager-numbers-wrap .k-button.k-pager-nav.k-disabled, button.k-pager-nav.k-button.k-button-flat.k-button-flat-base.k-icon-button.k-pager-last.k-button-md { background-color: var(--bg-0) !important; color: var(--col-400) !important; } +button.k-pager-nav.k-button.k-button-flat.k-button-flat-base.k-icon-button.k-pager-last.k-button-md.k-disabled, button.k-pager-nav.k-button.k-button-flat.k-button-flat-base.k-icon-button.k-button-md.k-disabled { color: var(--bg-2) !important; } +.k-pager-md .k-pager-numbers-wrap .k-button.k-pager-nav:hover { color: var(--col-600) !important; } +#lookupIframeWrapper { background-color: var(--bg-0); } +.k-window, .k-window:focus { background: var(--bg-0); box-shadow: 0 8px 16px var(--shadow-1),0 24px 38px var(--shadow-2); } +.k-window-titlebar, .k-window-content { background-color: var(--bg-0) !important; } +.k-window-title { color: var(--col-500) !important; } +.k-button .k-svg-i-window, .k-button .k-svg-i-x { color: var(--col-500); } +button.k-button.k-button-md.k-button-flat.k-button-flat-primary.k-selected { background-color: var(--col-100) !important; } +button.k-selected { background-color: var(--col-1) !important; color: var(--col-400) !important; } +.k-pager-md .k-pager-numbers-wrap .k-button { color: var(--col-400) !important; } +button.k-button.k-button-md.k-button-flat.k-button-flat-primary:hover { color: var(--col-500); background-color: var(--col-100) !important; } +.k-pager-md .k-pager-numbers-wrap .k-button.k-pager-nav { background-color: var(--bg-0) !important; } +.tdx-pagination-bar { background-color: var(--bg-0); } +.tdx-pagination-bar__active-page-item { color: var(--col-400); background-color: var(--col-200); } +.tdx-pagination-bar__page-item:hover:not(.tdx-pagination-bar__active-page-item,.tdx-pagination-bar__separator) { color: var(--col-500); background-color: var(--col-100); } +.tdx-pagination-bar .fa, .tdx-pagination-bar a { color: var(--col-400); } +.tdx-pager-button .fa-chevron-right, .tdx-pager-button .fa-chevron-left { -webkit-text-stroke-color: var(--bg-0); } +.hilite, .hilite .btn-link, .hilite a, .hilite td, .table>tbody>tr.hilite label, .table>tbody>tr.hilite.active, .table>tbody>tr.hilite.danger, .table>tbody>tr.hilite.info, .table>tbody>tr.hilite.success, .table>tbody>tr.hilite.warning, .table>tbody>tr.hilite>td.active, .table>tbody>tr.hilite>td.danger, .table>tbody>tr.hilite>td.info, .table>tbody>tr.hilite>td.success, .table>tbody>tr.hilite>td.warning { color: var(--txt-5); background-color: var(--bg-5) !important; } +.tdworkmgmt.table-header > div { background-color: var(--bg-1); color: var(--col-400); } + +/* Side Panels */ +.tdx-right-side-panel { background-color: var(--bg-0) !important; box-shadow: 0 8px 16px var(--shadow-1),0 24px 38px var(--shadow-2) !important; } +.white-bg, .white-hover-bg:hover { background-color: var(--bg-0) !important; } +.tdx-right-side-panel__drag-border { background: linear-gradient(var(--col-400),var(--col-400)) no-repeat left/1px 10% !important; } +.tdx-right-side-panel__drag-border:hover { border-color: var(--col-400) !important; } +.tdx-right-side-panel__header-title { color: var(--txt-2) !important; } +.tdx-right-side-panel__drag-border:focus-visible, .tdx-right-side-panel__drag-border.active { border-color: var(--col-500) !important; } +.tdx-right-side-panel__home-button { color: var(--col-400) !important; border-color: var(--col-500) !important; } +.tdx-right-side-panel__home-button:hover { color: var(--col-200) !important; border-color: var(--col-300) !important; } +.tdx-leftnav-drag-handle { background: linear-gradient(var(--col-400),var(--col-400)) no-repeat right/1px 10% !important; } +.tdx-leftnav-drag-handle:hover { background-color: var(--col-400) !important; } +.tdx-leftnav-drag-handle:focus-visible, .tdx-leftnav-drag-handle.active { background-color: var(--col-500) !important; } + +/* Tickets */ +.control-label, label.control-label, span.control-label { color: var(--txt-3) !important; } +.control-label~*, .black { color: var(--txt-1) !important; } +nav#divTabHeader, .TabButtonCell, .tab-row, .tdx-button-bar { background-color: var(--col-100) !important; } +.panel-default>.panel-heading, .panel-body, .panel-footer { background-color: var(--bg-0) !important; border-color: var(--border-0) !important; } +.panel { background-color: var(--bg-0) !important; } +.panel-body, .panel-footer { color: var(--txt-3) !important; } +.h2, h2, h2.h3, .panel-title { color: var(--txt-1) !important; border-color: var(--border-0) !important; } +.alert-info, div.alert-info { color: var(--txt-1) !important; background-color: var(--col-blue) !important; } +.alert-warning, div.alert-warning { color: var(--txt-1) !important; background-color: var(--col-yellow) !important; } +.nav-tabs>li.active>a, .nav-tabs>li.active>button, .nav-pills>li.active>a, .nav-pills>li.active>button { color: var(--col-600) !important; background-color: var(--bg-0) !important; border-color: var(--border-0); box-shadow: 0 .5px 1px 1px var(--shadow-1),0 2px 4px var(--shadow-2),0 4px 6px var(--shadow-2) !important; } +.nav-tabs>li>a, .nav-tabs>li>a:hover, .nav-tabs>li>button, .nav-tabs>li>button:hover, .nav-pills>li>a, .nav-pills>li>a:hover { color: var(--txt-3); } +.nav-tabs>li>a:hover, .nav-tabs>li>a:hover:hover { color: var(--txt-2); border-color: var(--border-0); } +.nav-tabs>li>a:hover { color: var(--col-600); } +a.btn.btn-link.tdx-tertiary-button-override { background-color: var(--col-100) !important; color: var(--txt-1) !important; } +.gray, .gray-hover:hover { color: var(--txt-3) !important; } +.dropdown-menu { background-color: var(--bg-0) !important; color: var(--txt-1) !important; box-shadow: 0 1px 3px var(--shadow-1),0 2px 10px var(--shadow-2); } +.dropdown-menu li { color: var(--txt-1); background-color: var(--bg-0); } +.dropdown-menu>span>li>a:hover, .dropdown-menu>span>li>a:focus, .dropdown-menu>span>li:hover, .dropdown-menu>span>li:focus, .dropdown-menu>li>a:hover, .dropdown-menu>li>span>a:hover, .dropdown-menu>li>a:focus, .dropdown-menu>li>span>a:focus, .dropdown-menu li:hover { color: var(--txt-1); background-color: var(--col-300); } +.TabButtonCell .btn-primary.active, .TabButtonCell .btn-primary.active:hover, .TabButtonCell .btn-primary.active:focus, .TabButtonCell .btn-primary:active, .TabButtonCell .btn-primary:active:hover, .TabButtonCell .btn-primary:active:focus, .TabButtonCell .btn-info.active, .TabButtonCell .btn-info.active:hover, .TabButtonCell .btn-info.active:focus, .TabButtonCell .btn-info:active, .TabButtonCell .btn-info:active:hover, .TabButtonCell .btn-info:active:focus, .tab-row .btn-primary.active, .tab-row .btn-primary.active:hover, .tab-row .btn-primary.active:focus, .tab-row .btn-primary:active, .tab-row .btn-primary:active:hover, .tab-row .btn-primary:active:focus, #divViewButtons.active, #divViewButtons.active:hover, #divViewButtons.active:focus, #divViewButtons:active, #divViewButtons:active:hover, #divViewButtons:active:focus, .TabButtonCell .btn-primary:active.active, .TabButtonCell .btn-primary:active.active:hover, .TabButtonCell .btn-primary:active.active:focus, .TabButtonCell .btn-primary:active:active, .TabButtonCell .btn-primary:active:active:hover, .TabButtonCell .btn-primary:active:active:focus, .tab-row .btn-primary:active.active, .tab-row .btn-primary:active.active:hover, .tab-row .btn-primary:active.active:focus, .tab-row .btn-primary:active:active, .tab-row .btn-primary:active:active:hover, .tab-row .btn-primary:active:active:focus { background: var(--col-600); color: var(--txt-1); } +div.btn-group.open>.dropdown-toggle.btn-primary, .open>.dropdown-toggle.btn-primary, .open>.dropdown-toggle.btn-primary:hover, .open>.dropdown-toggle.btn-primary:focus, .TabButtonCell .btn-primary:focus:not(:focus-visible,.active,:active,:hover), .TabButtonCell .btn-info:focus:not(:focus-visible,.active,:active,:hover), .tab-row .btn-primary:focus:not(:focus-visible,.active,:active,:hover), #divViewButtons:focus:not(:focus-visible,.active,:active,:hover), .TabButtonCell .btn-primary:active:focus:not(:focus-visible,.active,:active,:hover), .tab-row .btn-primary:active:focus:not(:focus-visible,.active,:active,:hover) { background: var(--col-100); color: var(--txt-1); } +.btn-primary--tertiary { background-color: var(--bg-0) !important; color: var(--txt-1) !important; border-color: var(--bg-0) !important; } +.btn-primary--tertiary:hover, .btn-primary--tertiary:hover:focus, .btn-primary--tertiary:focus-visible { background-color: var(--col-200) !important; color: var(--col-400) !important; } +.time-cell-approved { background-color: var(--col-green); } +.time-cell-rejected { background-color: var(--col-red); } +[class^=fa-].red, [class^=fa-].red:hover { color: var(--col-red) !important; } +[class^=fa-].green, [class^=fa-].green:hover { color: var(--col-green) !important; } +.tdx-error-text, span.tdx-error-text, .tdx-error-text:hover, span.tdx-error-text:hover { color: var(--col-600) !important; } +.red-bg, .status.red, .status-danger, .pri-hi { background-color: var(--col-red) !important; color: var(--txt-1) !important; } +.status.blue, .status.gray, .status-primary, .status-default { background-color: var(--col-blue); color: var(--txt-1) !important; } +.status-warning { background-color: var(--col-yellow); color: var(--txt-1) !important; } +.status.green, .status-success, .js-open-monitor .btn .label-success { background-color: var(--col-green); color: var(--txt-1) !important; } +div.feed-body>div.feed-child-box { background-color: var(--col-100); color: var(--txt-1); } +.modal-content { background-color: var(--bg-0); } +.modal-dialog { box-shadow: 0 8px 16px var(--shadow-1),0 24px 38px var(--shadow-2); } +.modal-title { color: var(--txt-1); } +.tdx-modal { background-color: var(--bg-0) !important; box-shadow: 0 0 8px var(--shadow-1),0 5px 12px var(--shadow-1),0 15px 35px var(--shadow-2); } +.tdx-modal .modal-content { color: var(--txt-1) !important; } +.tdx-modal__iframe-header, .tdx-modal__iframe-footer, .modal-content { background-color: var(--bg-0) !important; } +hr { background-color: var(--col-300); } +blockquote { border-color: var(--border-0); } +.code, code, pre { background-color: var(--bg-1); color: var(--txt-1); border-color: var(--border-0); } +div.comment { background-color: var(--col-100); } +.block-actions { background-color: var(--bg-0); } +body .tdworkmgmt.table>thead>tr>th, body .tdworkmgmt.table>tbody>tr>th, body .tdworkmgmt.table>tfoot>tr>th, body .tdworkmgmt.table>tbody>tr.TDGridHeader>td, body .tdworkmgmt.table>thead>tr.TDGridHeader>td { background-color: var(--bg-0); color: var(--txt-1); } +body .tdworkmgmt.table>tbody>tr:not(.tdx-grid__group-header):not(.TDGridHeader)>td { color: var(--txt-1); border-color: var(--border-0); } +body .tdworkmgmt.table>tbody>tr:not(.tdx-grid__group-header):not(.TDGridHeader):last-child:not(.TDPagerRow) th, body .tdworkmgmt.table>tbody>tr:not(.tdx-grid__group-header):not(.TDGridHeader):last-child:not(.TDPagerRow) td { border-color: var(--border-0); } +.tdworkmgmt td a, .tdworkmgmt.table .hilite a { color: var(--col-400); } +.tdworkmgmt td a:hover, .tdworkmgmt td a:focus-visible, .tdworkmgmt.table .hilite a:hover, .tdworkmgmt.table .hilite a:focus-visible { color: var(--col-0); } +.input-group-addon { background-color: var(--bg-5); color: var(--txt-1); } +.list-group-item { background-color: var(--bg-0) !important; border-color: var(--border-0); } + +/* Text Editor */ +.cke_editable { color: var(--txt-1); background-color: var(--bg-4); } +html .cke_editable { background-color: var(--bg-0) !important; color: var(--txt-1) !important; } +.cke_wysiwyg_div, .cke_wysiwyg_frame, .cke_combo_button, .cke_toolgroup { background: var(--bg-0) !important; } +.cke_button_icon { opacity: 1 !important; filter: invert(var(--txt-invert)) !important; } +.cke_combo_text { color: var(--txt-2) !important; background-color: var(--bg-0) !important; } +.cke_panel_grouptitle { background: var(--bg-2) !important; color: var(--txt-1); } +.cke_panel_listItem a:active, .cke_panel_listItem a:focus, .cke_panel_listItem a:hover, a.cke_button_disabled:active, a.cke_button_disabled:focus, a.cke_button_disabled:hover, a.cke_button_off:active, a.cke_button_off:focus, a.cke_button_off:hover { background: var(--bg-3) !important; } +.cke_bottom, .cke_top, .cke_chrome { background-color: var(--bg-1) !important; color: var(--txt-2) !important; } +.cke_combo_button, .cke_toolgroup, .cke_contents { border-color: var(--border-0) !important; } +div .cke_top, div .cke_bottom, div .comment-options, div .cke_chrome { background-color: var(--col-100) !important; } +.cke_panel_listItem a { border: none; } +.cke_panel, .cke_panel_grouptitle, a.cke_colorbox, a.cke_colorauto, a.cke_colormore { border-color: var(--border-0) !important; } +.cke_colorblock, .cke_colorblock a { color: var(--txt-1); } +a:active.cke_colorauto, a:active.cke_colormore, a:focus.cke_colorauto, a:focus.cke_colormore, a:hover.cke_colorauto, a:hover.cke_colormore, a:active.cke_colorbox, a:focus.cke_colorbox, a:hover.cke_colorbox { background: var(--bg-3) !important; } +.cke_dialog_contents, .cke_dialog_body { color: var(--txt-1) !important; background: var(--bg-2) !important; } +.cke_dialog_footer, .cke_dialog_title { background: var(--bg-1) !important; } +.cke_reset_all, .cke_reset_all * { color: var(--txt-2) !important; } +.cke_dialog_page_contents a.cke_dialog_ui_button, a.cke_dialog_ui_button_cancel, input.cke_dialog_ui_input_password, input.cke_dialog_ui_input_text, textarea.cke_dialog_ui_input_textarea { background: var(--bg-3) !important; } +a.cke_dialog_tab_selected { background: var(--bg-2) !important; } +a.cke_dialog_tab { background: var(--bg-3) !important; } +select.cke_dialog_ui_input_select { background-color: var(--bg-4) !important; } +.cke_button_on { background: var(--bg-2) !important; } +.dropdown-menu>li>a, .dropdown-menu>span>li>a, .dropdown-menu>li>span>a { color: var(--txt-1) !important; } + +/* Icons */ +.js-help a, .js-help .fa-solid, .js-default-help a, .js-default-help .fa-solid, .disp-group .control-label>a>span { color: var(--col-400); } +.js-help a:hover, .js-help a:focus-visible, .js-help a:active, .js-help a:active:hover, .js-help a:focus:active, .js-help a:focus:hover, .js-help .fa-solid:hover, .js-help .fa-solid:focus-visible, .js-help .fa-solid:active, .js-help .fa-solid:active:hover, .js-help .fa-solid:focus:active, .js-help .fa-solid:focus:hover, .js-default-help a:hover, .js-default-help a:focus-visible, .js-default-help a:active, .js-default-help a:active:hover, .js-default-help a:focus:active, .js-default-help a:focus:hover, .js-default-help .fa-solid:hover, .js-default-help .fa-solid:focus-visible, .js-default-help .fa-solid:active, .js-default-help .fa-solid:active:hover, .js-default-help .fa-solid:focus:active, .js-default-help .fa-solid:focus:hover, .disp-group .control-label>a>span:hover, .disp-group .control-label>a>span:focus-visible, .disp-group .control-label>a>span:active, .disp-group .control-label>a>span:active:hover, .disp-group .control-label>a>span:focus:active, .disp-group .control-label>a>span:focus:hover { color: var(--col-500); } +.tdx-lookup-button-group a, .tdx-lookup-button-group button, .input-btn-group a, .input-btn-group button, div.select2-container.select2-allowclear .select2-choice abbr { color: var(--col-400); } +.tdx-lookup-button-group a:hover, .tdx-lookup-button-group a:focus-visible, .tdx-lookup-button-group a:active, .tdx-lookup-button-group a:active:hover, .tdx-lookup-button-group a:focus:active, .tdx-lookup-button-group a:focus:hover, .tdx-lookup-button-group button:hover, .tdx-lookup-button-group button:focus-visible, .tdx-lookup-button-group button:active, .tdx-lookup-button-group button:active:hover, .tdx-lookup-button-group button:focus:active, .tdx-lookup-button-group button:focus:hover, .input-btn-group a:hover, .input-btn-group a:focus-visible, .input-btn-group a:active, .input-btn-group a:active:hover, .input-btn-group a:focus:active, .input-btn-group a:focus:hover, .input-btn-group button:hover, .input-btn-group button:focus-visible, .input-btn-group button:active, .input-btn-group button:active:hover, .input-btn-group button:focus:active, .input-btn-group button:focus:hover { color: var(--col-500); } +.tdx-icon__encircled--active { background-color: var(--col-200) !important; } +.tdx-icon__encircled--active:hover { background-color: var(--col-300) !important; } +.tdx-icon__encircled:not(.tdx-icon__encircled--active) { background-color: var(--bg-0) !important; } +.tdx-icon__encircled:not(.tdx-icon__encircled--active):hover { background-color: var(--col-200) !important; } +.tdx-user-icon, .tdx-multiuser-icon, div.profile-image.red-bg, div.profile-image.green-bg, div.profile-image.red-bg, div.profile-image.blue-bg, div.profile-image.yellow-bg, div.profile-image.gray-bg, div.profile-image.initials.red-bg, div.profile-image.initials.green-bg, div.profile-image.initials.red-bg, div.profile-image.initials.blue-bg, div.profile-image.initials.yellow-bg, div.profile-image.initials.gray-bg { background-color: var(--col-400) !important; color: var(--col-100) !important; } +.tdx-widget-indicator-icon { color: var(--col-600) !important; } +.tdx-dirty-indicator { color: var(--col-400) !important; } +.tdx-hover-darken { color: var(--col-400); } +.tdx-hover-darken:hover { color: var(--col-300); } +.form-group>a, .form-group>.checkbox>a, .disp-group>a, .form-group>.control-label>a, .checkbox>label>a { color: var(--col-400) !important; } +.form-group>a:hover, .form-group>.checkbox>a:hover, .disp-group>a:hover, .form-group>.control-label>a:hover, .checkbox>label>a:hover { color: var(--col-500) !important; } +.k-table-th[data-role=columnsorter] .k-link:not(:has(>.k-sort-icon)):after { color: var(--col-400) !important; } +.k-table-th[data-role=columnsorter] .k-link:not(:has(>.k-sort-icon)):hover:after { color: var(--col-500) !important; } +.tdx-calendar .fa-plus { color: var(--col-300) !important; } +.icon-button { color: var(--col-400) !important; } +.tdx-view-ticket-summary #divSummaryHeading a, .tdx-view-ticket-summary #divSummaryHeading a #spnCopySummaryIcon { color: var(--col-400); } + +/* Icons + Filters */ +.tdx-dropdown__menu-icon, .tdx-dropdown--headline:after, .tdx-dropdown:after, .tdx-icon--search, .k-icon { filter: brightness(0) invert(var(--filter-invert)) sepia(var(--filter-sepia)) saturate(var(--filter-saturate)) hue-rotate(var(--filter-hue-rotate)) brightness(var(--filter-brightness)) contrast(var(--filter-contrast)); } +#divDescription .wrap-text [style*="color"]:not([style*="color"] [style*="color"]), .feed-item-text [style*="color"]:not([style*="color"] [style*="color"]), #divCustomLoginPrompt [style*="color"]:not([style*="color"] [style*="color"]) { filter: none !important; } + +/* Same but with !important */ +.tdx-icon__encircled--active .tdx-icon, .tdx-icon--filter-purple, .tdx-navigation-tabs__tab--active .tdx-close-x, .tdx-icon--new-browser, .tdx-close-x, button.tdx-close-x, .tdx-action-menu__item--primary:after, .tdx-action-menu-dropdown:after, .tdx-icon.tdx-icon--hover-darken, .tdx-icon--hover-darken>.tdx-icon, .tdx-dropdown__menu-icon, .tdx-dropdown--headline:after, .tdx-dropdown:after, .tdx-icon--search, .tdx-icon:not(.label), div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:hover, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:focus-visible, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:active, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:active:hover, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:focus:active, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close:focus:hover { filter: brightness(0) invert(var(--filter-invert)) sepia(var(--filter-sepia)) saturate(var(--filter-saturate)) hue-rotate(var(--filter-hue-rotate)) brightness(var(--filter-brightness)) contrast(var(--filter-contrast)) !important; } + +/* Hover + !important */ +.tdx-icon__encircled:hover .tdx-icon, .tdx-close-x:hover, button.tdx-close-x:hover, .tdx-icon.tdx-icon--hover-darken:hover, .tdx-icon.tdx-icon--hover-darken:focus, .tdx-icon--hover-darken>.tdx-icon:hover, .tdx-icon--hover-darken>.tdx-icon:focus, .tdx-leftnav__item-expander:hover .tdx-icon, .tdx-leftnav__item-expander:focus .tdx-icon, .tdx-action-menu-dropdown--no-arrow:hover .tdx-icon, .tdx-action-menu-dropdown--no-arrow.active .tdx-icon, div.select2-container-multi .select2-choices .select2-search-choice>.select2-search-choice-close { filter: brightness(0) invert(var(--filter-invert)) sepia(var(--filter-sepia)) saturate(var(--filter-saturate)) hue-rotate(var(--filter-hue-rotate)) brightness(var(--filter-brightness)) contrast(var(--filter-contrast)) brightness(125%) !important; } +/* Live preferences and contrast: CSS also covers new content without DOM rewrites. */ +:root.tdx-hide-ai :is(#ToggleAIAgentAssist, .tdx-view-ticket-summary, #divFieldSuggestionsResponsible, #divFieldSuggestionsPriority, #divFieldSuggestionsForm, .cke_button__revisetext) { display: none !important; } +:root.tdx-sticky-columns .tdx-dashboard__column { position: sticky; align-self: flex-start; top: 4px; } +@media (min-width: 992px) { + :root.tdx-wide-column #divContent #Column2 { width: 66.666667%; } + :root.tdx-wide-column #divContent #Column3 { width: 33.333333%; } +} +:is(div.moreToggle, div.lessToggle, div.wrap-text, div.feed-item-text, #ttDescription, .cke_editable) [style*="color"]:not(a) { color: var(--txt-1) !important; } +#settingsForm input[type=color] { display: block; max-width: 90px; } +:root, :root.lightMode { ${themeVariables('light')} } +:root.darkMode { ${themeVariables('dark')} } + + `; + + // Initialize after CSS exists: computed colors and editor injection depend on it. + // The first scheduled pass handles content already present at document-end. + GM_addStyle(customStyles); + applySettings(); + forwardSSO(); +})(); From b85fa7243b5f89d93f00be4607a0209c38ca0f2c Mon Sep 17 00:00:00 2001 From: Diego Dereck Sosa-Escamilla Date: Mon, 14 Sep 2026 10:40:04 -0400 Subject: [PATCH 3/3] Update tdx-enhanced.js --- tdx-enhanced.js | 65 +++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/tdx-enhanced.js b/tdx-enhanced.js index 987a04c..af7d279 100644 --- a/tdx-enhanced.js +++ b/tdx-enhanced.js @@ -1,7 +1,7 @@ // ==UserScript== // @name tdx-enhanced // @namespace purdue-it -// @version 2026-09-14-01 +// @version 2026-09-14-02 // @description enhanced tdx coloring & formatting. follows system color scheme. // @author Purdue STEM IT - it@purdue.edu // @match https://service.purdue.edu/TDWorkManagement* @@ -216,7 +216,7 @@ const all = (selector, root = document) => [...root.querySelectorAll(selector)]; const clamp = value => Math.max(0, Math.min(1, value)); const make = (tag, className = '', text = '') => Object.assign(document.createElement(tag), {className, textContent: text}); - const defaults = {colorMode: 'auto', theme: 'gold', linkBehavior: 'tabs', highlightBehavior: 'block', aiBehavior: 'aiEnable', customHighlights: []}; + const defaults = {colorMode: 'auto', theme: '', linkBehavior: 'tabs', highlightBehavior: 'block', aiBehavior: 'aiEnable', customHighlights: []}; let config = readSettings(); let rules = compileHighlights(config.customHighlights); let revision = 0; @@ -285,7 +285,7 @@ const field = form.elements.namedItem(name); if (!field) continue; if (field.type === 'checkbox') field.checked = value === 'enabled' || value === true; - else field.value = typeof value === 'object' ? JSON.stringify(value) : value; + else field.value = name === 'theme' && !value ? 'gold' : typeof value === 'object' ? JSON.stringify(value) : value; } for (const [name, variable] of [['primaryColor', 'col-400'], ['backgroundColor', 'bg-0']]) { if (!config[name]) form.elements[name].value = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(`--${variable}`).trim()).toHexString(); @@ -303,9 +303,11 @@ root.classList.toggle('tdx-sticky-columns', config.stickyColumns === 'enabled' || config.stickyColumns === true); root.classList.toggle('tdx-wide-column', config.layout === '1_100-66-33'); for (const scheme of ['dark', 'light']) { - const theme = ['gold', 'blue', 'green'].includes(config.theme) ? config.theme : 'gold'; + const theme = ['gold', 'blue', 'green'].includes(config.theme) ? config.theme : ''; + // An unset theme used TDX's original default palette, not the Gold preset. + const accent = theme ? `var(--${scheme}-${theme}-accent)` : `var(--${scheme}-col-default)`; const custom = config.theme === 'custom'; - root.style.setProperty(`--${scheme}-col-primary`, custom && tinycolor(config.primaryColor).isValid() ? config.primaryColor : `var(--${scheme}-${theme}-accent)`); + root.style.setProperty(`--${scheme}-col-primary`, custom && tinycolor(config.primaryColor).isValid() ? config.primaryColor : accent); root.style.setProperty(`--${scheme}-bg-primary`, custom && tinycolor(config.backgroundColor).isValid() ? config.backgroundColor : `var(--${scheme}-bg-default)`); } localStorage.setItem('styles', config.colorMode); // Retain the original compatibility key. @@ -333,14 +335,14 @@ // Remember only properties we change. Undo them before reapplying rules so // switching themes/highlights does not leave stale colors behind. - function paint(element, style) { + function paint(element, style, priority = '') { if (!style) return; let saved = decorations.get(element); if (!saved) decorations.set(element, saved = new Map()); for (const [name, value] of Object.entries(style)) { const property = name.startsWith('--') ? name : name.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`); if (!saved.has(property)) saved.set(property, {value: element.style.getPropertyValue(property), priority: element.style.getPropertyPriority(property)}); - element.style.setProperty(property, String(value)); + element.style.setProperty(property, String(value), priority); } } @@ -363,6 +365,9 @@ cell.append(span); } paint(span, {borderColor: 'transparent', ...style}); + // TDX sets link colors with !important. Keep links clickable while letting + // the badge's selected foreground win in normal, visited, and hover states. + all('a', span).forEach(link => paint(link, {color: 'inherit'}, 'important')); return span; } @@ -462,6 +467,7 @@ cells.forEach((cell, i) => { const input = sourceText(cell); resetPaint(cell); + all('a', cell).forEach(resetPaint); // Restore link colors when a badge is removed. cell.classList.remove('light', 'dark'); all('.qHighlight, [data-tdx-highlight]', cell).forEach(span => span.replaceWith(...span.childNodes)); if (headers[i] === 'Title' && cell.textContent.trim() !== input) { @@ -721,22 +727,23 @@ } } - // Build repeated form controls from data; all original choices remain available. + // Keep the original toolbar element types and sibling structure: TDX styles + // buttons differently from links and lays out each toolbar sibling separately. function injectToolbar() { const search = $('#globalSearchBar'); if (!search || $('#settingsButton')) return; - const radios = (name, choices) => choices.map(([value, label]) => `
`).join(''); + const radios = (name, choices) => choices.map(([value, label]) => `
`).join(''); const wrapper = make('span'); - wrapper.innerHTML = ` - `; + wrapper.innerHTML = ` +
`; search.after(wrapper); const form = $('form', wrapper), menu = $('#settingsMenu', wrapper), button = $('#settingsButton', wrapper); button.addEventListener('click', () => { @@ -744,6 +751,9 @@ menu.classList.toggle('hide', !visible); button.setAttribute('aria-expanded', String(visible)); }); + button.addEventListener('keydown', event => { + if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); button.click(); } + }); form.addEventListener('submit', event => event.preventDefault()); const custom = form.elements.customHighlights; custom.addEventListener('input', () => { @@ -757,14 +767,15 @@ colorWindow = window.open(`${TOOLS_ORIGIN}/ecnuds/tdx/userscript/coloreditor`, 'TDX Color Editor', 'popup=true'); // The cross-origin editor initiates the colorsReady handshake on load. }); - $('#tdx-toolbar-links')?.remove(); + all('.tdx-toolbar-link').forEach(link => link.remove()); const links = make('span'); - links.id = 'tdx-toolbar-links'; - links.innerHTML = `
Status
- `; - search.parentElement.prepend(links); + links.innerHTML = `
+ `; + const status = $('#statusBox', links); + // Separate siblings prevent the links from stacking inside one flex item. + search.parentElement.prepend(...links.children); fillSettings(); - loadStatus(links); + loadStatus(status); } // Only the trusted editor popup may exchange settings. The SSO helper forwards @@ -1358,7 +1369,9 @@ select.cke_dialog_ui_input_select { background-color: var(--bg-4) !important; } :root.tdx-wide-column #divContent #Column2 { width: 66.666667%; } :root.tdx-wide-column #divContent #Column3 { width: 33.333333%; } } -:is(div.moreToggle, div.lessToggle, div.wrap-text, div.feed-item-text, #ttDescription, .cke_editable) [style*="color"]:not(a) { color: var(--txt-1) !important; } +/* Read-only descriptions also contain class-colored text; protect selected badge colors. */ +:is(div.moreToggle, div.lessToggle, div.wrap-text, div.feed-item-text) *:not(a, .qHighlight, .qHighlight *), +:is(#ttDescription, .cke_editable) [style*="color"]:not(a, .qHighlight, .qHighlight *) { color: var(--txt-1) !important; } #settingsForm input[type=color] { display: block; max-width: 90px; } :root, :root.lightMode { ${themeVariables('light')} } :root.darkMode { ${themeVariables('dark')} }