From 75e5b2d2945cea93a5427c8eda297f2b52d15a8e Mon Sep 17 00:00:00 2001 From: Chuck Werner Date: Wed, 16 Oct 2024 10:29:33 -0400 Subject: [PATCH 1/7] Replace jQuery in Retirement app - Add dollar-sign.js - Change some event handlers in retirement --- .../retirement-before-you-claim-listeners.js | 14 +- .../js/disclosures/index.js | 2 +- .../js/disclosures/utils/dollar-sign.js | 569 +++++++++++++-- .../retirement/js/utils/is-element-in-view.js | 2 +- .../apps/retirement/js/views/graph-view.js | 9 +- .../retirement/js/views/next-steps-view.js | 5 +- .../retirement/js/views/questions-view.js | 3 +- .../apps/retirement/js/views/tooltips-view.js | 3 +- .../js/modules/util/dollar-sign.js | 675 ++++++++++++++++++ 9 files changed, 1197 insertions(+), 85 deletions(-) create mode 100644 cfgov/unprocessed/js/modules/util/dollar-sign.js diff --git a/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js b/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js index d05d29fa130..2d5ac2b9855 100644 --- a/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js +++ b/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js @@ -162,12 +162,14 @@ import { analyticsSendEvent, analyticsLog } from '@cfpb/cfpb-analytics'; .querySelector('#retirement-age-selector') .addEventListener('change', function (event) { const target = event.currentTarget; - const val = target[target.selectedIndex].value; - analyticsSendEvent({ - event: 'Before You Claim Interaction', - action: 'Planned Retirement Age selected', - label: val, - }); + if ( target.selectedIndex > -1 ) { + const val = target[target.selectedIndex].value; + analyticsSendEvent({ + event: 'Before You Claim Interaction', + action: 'Planned Retirement Age selected', + label: val, + }); + } }); document diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/index.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/index.js index f87bfd4e834..2db6935cf54 100755 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/index.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/index.js @@ -1,4 +1,4 @@ -import $ from './utils/dollar-sign.js'; +import $ from '../../../../js/modules/util/dollar-sign.js'; import getApiValues from './dispatchers/get-api-values.js'; import verifyOffer from './dispatchers/post-verify.js'; import financialModel from './models/financial-model.js'; diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js index 61fdde92110..dc1b7824d96 100644 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js @@ -2,64 +2,135 @@ * @param {Document|Window|string} selector - A jQuery-style selector. * @returns {Document|Window|undefined} The document, window, or nothing. */ + function Query(selector) { this.elements = []; - - if (selector === document) { - return document; + + if ( typeof selector === 'undefined' ) { + return this; } - if (selector === window) { - return window; + if ( selector.nodeType ) { + this.elements[0] = selector; + + return this; } this.selector = selector; - if (typeof selector === 'string' && selector !== '') { - this.elements = document.querySelectorAll(selector); - } -} -Query.prototype.attr = function (name, value) { - if (typeof value === 'undefined') { - return this.elements.length ? this.elements[0].getAttribute(name) : null; - } else { - this.elements.forEach((elem) => { - elem.setAttribute(name, value); - }); - } -}; + // handle :last jquery selector + if ( selector.indexOf( ':last' ) !== -1 ) { + selector = selector.split( ':' )[0]; + const arr = document.querySelectorAll(selector); + this.elements = arr.length ? [arr[arr.length - 1]] : []; + this.selector = selector; -Query.prototype.text = function (value) { - // getter - if (typeof value === 'undefined') { - return this.elements.length ? this.elements[0].textContent : null; + return this; } - //setter - else { - this.elements.forEach((elem) => { - elem.textContent = value; - }); + + // handle :selected jquery selector + if ( selector.indexOf( ':selected' ) !== -1 ) { + selector = selector.split( ':' )[0]; + const arr = document.querySelector( selector ); + const parent = arr.parentElement; + + this.elements.push( parent.options[parent.selectedIndex]); + this.selector = selector; + + return this; } -}; -Query.prototype.val = function (value) { - // getter - if (typeof value === 'undefined' && this.elements.length > 0) { - return this.elements.length ? this.elements[0].value : null; + if (selector === document) { + this.elements.push( document ); + } else if (selector === '$WINDOW' ) { + this.elements.push( document.defaultView ); + } else if (typeof selector === 'string' && selector !== '') { + this.elements = document.querySelectorAll(selector); } - //setter - else { - this.elements.forEach((elem) => { - elem.value = value; - }); - return this; + + return this; +} + +/**************************/ +// Helper functions +/**************************/ + +function pixelator( val ) { + if ( typeof val === 'number' ) { + return val.toString() + 'px' + } else if ( typeof val === 'string') { + return val.replace(/\D/g,'') + 'px'; } + + return '0px'; +} + +/**************************/ +// Reference variables +/**************************/ + +const pixelStyles = [ + 'width', 'height', 'left', 'right', 'top', 'bottom' +]; + +const slideUpVars = { + styles: { + transitionProperty: 'height, margin, padding', + boxSizing: 'border-box', + overflow: 'hidden', + height: 0, + paddingTop: 0, + paddingBottom: 0, + marginTop: 0, + marginBottom: 0 + }, + removals: [ + 'height', + 'padding-top', + 'padding-bottom', + 'margin-top', + 'margin-bottom', + 'overflow', + 'transition-duration', + 'transition-property' + ] +} + +const slideDownVars = { + styles: { + overflow: 'hidden', + height: 0, + paddingTop: 0, + paddingBottom: 0, + marginTop: 0, + marginBottom: 0, + boxSizing: 'border-box', + transitionProperty: 'height, margin, padding' + }, + removals: [ + 'padding-top', + 'padding-bottom', + 'margin-top', + 'margin-bottom' + ], + delayedRemovals: [ + 'height', + 'overflow', + 'transition-duration', + 'transition-property' + ] + + }; +/**************************/ +// Object elements[] helpers +/**************************/ + Query.prototype.each = function (callback) { - this.elements.forEach((elem, index) => { - callback(elem, index); - }); + this.elements.forEach( function(elem, index) { + callback.call( elem, elem, index); + } ); }; Query.prototype.find = function (findSelector) { @@ -79,6 +150,16 @@ Query.prototype.find = function (findSelector) { } }; +Query.prototype.closest = function( closestSelector) { + if ( typeof closestSelector === 'undefined' || this.elements.length < 1 ) return this; + const q = new Query(); + const elemArr = []; + elemArr.push( this.elements[0].closest( closestSelector ) ); + q.elements = elemArr; + + return q; +} + Query.prototype.not = function (notSelector) { if (typeof this.selector !== 'undefined') { return new Query(this.selector + ':not(' + notSelector + ')'); @@ -136,38 +217,138 @@ Query.prototype.remove = function () { this.elements.forEach((elem) => { elem.remove(); }); -}; -Query.prototype.hide = function () { - this.elements.forEach((elem) => { - elem.style.display = 'none'; - }); + return this; }; -Query.prototype.show = function (className) { - this.elements.forEach((elem) => { - if (typeof className !== 'undefined') { - elem.style.display = className; - } else { - elem.style.display = 'block'; - } - }); -}; +/**************************/ +// DOM element text, value +/**************************/ -Query.prototype.height = function () { - return this.elements.length ? this.elements[0].offsetHeight : null; +Query.prototype.text = function (value) { + // getter + if (typeof value === 'undefined') { + return this.elements.length ? this.elements[0].textContent : null; + } + //setter + else { + this.elements.forEach((elem) => { + elem.textContent = value; + }); + + return this; + } }; -Query.prototype.top = function () { - return this.elements.length ? this.elements[0].offsetTop : null; +Query.prototype.html = function ( value ) { + //getter + if (typeof value === 'undefined') { + return this.elements.length ? this.elements[0].innerHTML : null; + } + //setter + else { + this.elements.forEach( elem => { + elem.innerHTML = value; + }); + + return this; + } +} + +Query.prototype.val = function (value) { + // getter + if (typeof value === 'undefined' && this.elements.length > 0) { + return this.elements.length ? this.elements[0].value : null; + } + //setter + else { + this.elements.forEach((elem) => { + elem.value = value; + }); + return this; + } }; +/**************************/ +// event listeners and triggers +/**************************/ Query.prototype.listen = function (eventType, callback) { this.elements.forEach((elmo) => { elmo.addEventListener(eventType, callback); }); }; +Query.prototype.click = function( callback ) { + this.listen( 'click', callback ); +} + +Query.prototype.submit = function( callback ) { + this.listen( 'submit', callback ); +} + +/** + * Handles events in eventType + * @param {string|function} paramOne - A selector or callback + * @param {function} paramTwo - A callback if paramOne is a selector. + */ +Query.prototype.on = function( eventType, paramOne, paramTwo ) { + if ( typeof paramOne == 'function' ) { + this.elements.forEach( elem => { + eventType.split(' ').forEach( type => { + elem.addEventListener( type, paramOne ); + }); + }) + } else { + this.elements.forEach( elem => { + const callback = function( ev ) { + if ( !ev.target ) return; + const elem = ev.target.closest( paramOne ); + if ( elem ) { + paramTwo.call( elem, ev ); + } + } + eventType.split(' ').forEach( type => { + elem.addEventListener( type, callback ); + }); + return callback; + }); + } + + return this; +} + +Query.prototype.blur = function() { + this.elements.forEach( elem => { + elem.blur(); + }) +} + +Query.prototype.change = function () { + this.elements.forEach( elem => { + if (elem.tagName === 'SELECT') { + let change = new Event('change'); + change.currentTarget = elem; + elem.dispatchEvent(change); + } + }); +}; + +Query.prototype.keypress = function( callback ) { + this.listen( 'keyup', callback ); +} + +Query.prototype.keyup = function( callback ) { + this.listen( 'keyup', callback ); +} + +Query.prototype.resize = function( callback ) { + document.defaultView.addEventListener('resize', callback ); +} + +/**************************/ +// classes, styles, attributes, and properties +/**************************/ + Query.prototype.tagName = function () { return this.elements.length ? this.elements[0].tagName : null; }; @@ -177,6 +358,8 @@ Query.prototype.addClass = function (classNames) { this.elements.forEach((elem) => { elem.classList.add(...classArr); }); + + return this; }; Query.prototype.removeClass = function (classNames) { @@ -184,20 +367,261 @@ Query.prototype.removeClass = function (classNames) { this.elements.forEach((elem) => { elem.classList.remove(...classArr); }); + + return this; }; -Query.prototype.change = function () { +Query.prototype.attr = function (name, value) { + if (typeof value === 'undefined') { + return this.elements.length ? this.elements[0].getAttribute(name) : null; + } else if (value === false) { + this.elements.forEach((elem) => { + elem.removeAttribute( name ); + }); + } else { + if ( this.elements.length < 1 ) return this; + this.elements.forEach( (elem) => { + elem.setAttribute(name, value); + }); + } + + return this; +}; + +Query.prototype.is = function( selector ) { + if ( typeof selector === 'string' ) { + return this.elements.length ? this.elements[0].matches( selector ) : this; + } + + return this; +} + +Query.prototype.hide = function () { this.elements.forEach((elem) => { - if (elem.tagName === 'SELECT') { - elem.dispatchEvent(new Event('change')); + elem.style.display = 'none'; + }); + + return this; +}; + +Query.prototype.show = function (className) { + this.elements.forEach((elem) => { + if (typeof className !== 'undefined') { + elem.style.display = className; + } else { + elem.style.display = 'block'; } }); + + return this; }; +Query.prototype.slideUp = function (duration=500) { + this.elements.forEach( elem => { + elem.style.transitionDuration = duration + 'ms'; + for (let key in slideUpVars.styles ) { + elem.style[key] = slideUpVars.styles[key]; + } + elem.style.height = elem.offsetHeight + 'px'; + void elem.offsetWidth; + document.defaultView.setTimeout( () => { + slideUpVars.removals.forEach( item => { + elem.style.removeProperty( item ); + }); + elem.style.display = 'none'; + }, duration ); + }); + + return this; +}; + +Query.prototype.slideDown = function(duration=500) { + this.elements.forEach( elem => { + elem.style.removeProperty( 'display' ); + let display = document.defaultView.getComputedStyle(elem).display; + if (display === 'none') display = 'block'; + elem.style.display = display; + let height = elem.offsetHeight; + for (let key in slideDownVars.styles) { + elem.style[key] = slideDownVars.styles[key]; + + } + void elem.offsetWidth; + elem.style.transitionDuration = duration + 'ms'; + elem.style.height = height + 'px'; + slideDownVars.removals.forEach( item => { + elem.style.removeProperty( item ); + }); + document.defaultView.setTimeout( () => { + slideDownVars.delayedRemovals.forEach( item => { + elem.style.removeProperty( item ); + }); + }) + }); + + return this; +} + +Query.prototype.height = function ( val ) { + if ( typeof val === 'undefined' ) { + if ( this.elements.length > 0 ) { + const elem = this.elements[0]; + if ( elem.hasOwnProperty( 'getBoundingClientRect' ) ) { + return elem.getBoundingClientRect().height + } + return this.elements[0].offsetHeight; + } + return null; + } else { + this.elements.forEach( elem => { + if ( typeof val === 'function' ) { + val = val(); + } else { + if ( typeof val !== 'string' ) { + val += 'px'; + } + elem.style.height = val; + } + }); + + return this; + } +}; + +Query.prototype.outerHeight = function ( val ) { + if ( typeof val === 'undefined' ) { + return this.elements.length ? this.elements[0].offsetHeight : null; + } else { + this.elements.forEach( elem => { + if ( typeof val === 'function' ) { + val = val(); + } else { + if ( typeof val !== 'string' ) { + val += 'px'; + } + elem.style.height = val; + } + }); + + return this; + } +}; + + +Query.prototype.top = function () { + return this.elements.length ? this.elements[0].offsetTop : null; +}; + +Query.prototype.width = function( val ) { + if ( typeof val === 'undefined' ) { + if ( this.elements.length < 1 ) return this; + const elem = this.elements[0]; + if ( elem.window === elem ) { + return document.defaultView.outerWidth; + } else { + return elem.getBoundingClientRect().width + } + } else { + if (typeof val === 'function') { + val = val(); + } else { + if ( this.elements.length < 1 ) return this; + if (typeof val !== 'string') val = val + 'px'; + this.elements.forEach( elem => { + elem.style.width = val; + }); + } + } +} + +Query.prototype.outerWidth = function( val ) { + if ( this.elements.length < 1 ) return this; + const elem = this.elements[0]; + if ( val === true ) { + const style = getComputedStyle( elem ); + return elem.getBoundingClientRect().width + + parseFloat( style.marginLeft ) + parseFloat( style.marginRight ); + } else { + return elem.offsetWidth; + } +} + +Query.prototype.scrollTop = function( val ) { + if ( typeof val === undefined ) { + const elem = this.elements[0]; + if ( elem.window === elem ) { + return document.defaultView.pageYOffset; + } else { + return elem.scrollTop; + } + } else { + this.elements.forEach( elem => { + if ( elem.window === elem ) { + const xOff = document.defaultView.pageXOffset; + document.defaultView.scrollTo( xOff, val ); + } else { + elem.scrollTop = val; + } + }); + } +} + +Query.prototype.offset = function( val ) { + if ( this.elements.length < 1 ) return this; + const elem = this.elements[0]; + const win = document.defaultView; + const bound = elem.getBoundingClientRect(); + const docTop = document.documentElement.clientTop; + const docLeft = document.documentElement.clientLeft; + return { + top: bound.top + win.pageYOffset + docTop, + left: bound.left + win.pageXOffset + docLeft + }; +} + +Query.prototype.css = function( param, value ) { + if ( this.elements.length < 1 ) return this; + let obj = {}; + if ( typeof param !== 'object' && typeof value === 'undefined' ) { + const styles = getComputedStyle( this.elements[0] ); + return styles[param]; + } else if ( typeof param !== 'object' ) { + obj[param] = value; + } else { + obj = param; + } + for ( let key in obj ) { + this.elements.forEach( elem => { + if ( elem.style.hasOwnProperty( key ) ) { + let val = obj[key]; + if ( pixelStyles.indexOf( key ) > -1 ) val = pixelator(val) ; + elem.style[key] = val; + } + }); + } + return this; +} + +/**************************/ +// DOM manipulation +/**************************/ + Query.prototype.cloner = function () { return this.elements.length ? this.elements[0].cloneNode(true) : null; }; +Query.prototype.append = function( elem ) { + this.elements.forEach( parent => { + if ( typeof elem === 'string' ) { + parent.innerHTML += elem; + } else { + parent.append( elem ); + } + }); + + return this; +} + Query.prototype.appendTo = function (newParents) { // This method is designed only to work on instances of Query if (newParents instanceof Query !== true) { @@ -215,11 +639,18 @@ Query.prototype.appendTo = function (newParents) { return this; }; +Query.prototype.empty = function() { + this.elements.forEach( elem => { + elem.replaceChildren(); + }); +} + +// Constructor function const $ = function (param) { if (typeof param === 'string') { return new Query(param); } else if (typeof param === 'object') { - if (typeof param.length === 'undefined') { + if ( param.nodeType ) { const q = new Query(); q.elements = [param]; return q; @@ -228,7 +659,17 @@ const $ = function (param) { this.elements = param; return q; } - } + } }; +$.each = function( obj, callback) { + obj.forEach( function(elem, index) { + // This is an odd reversal of parameters, but matches jQuery.each() + callback( index, elem ); + }, obj ); +} + + +export const window = '$WINDOW'; + export default $; diff --git a/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js b/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js index bc0fb3fbe52..86ae347dadb 100644 --- a/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js +++ b/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js @@ -1,5 +1,5 @@ // TODO: remove jquery. -import $ from 'jquery'; +import $ from '../../../../js/modules/util/dollar-sign.js'; /** * This function determines if the element specified is currently in the diff --git a/cfgov/unprocessed/apps/retirement/js/views/graph-view.js b/cfgov/unprocessed/apps/retirement/js/views/graph-view.js index 0e33fe9c6ea..9d50fcfa761 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/graph-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/graph-view.js @@ -6,8 +6,7 @@ import questionsView from './questions-view.js'; import { convertStringToNumber } from '../../../../js/modules/util/format.js'; import validDates from '../utils/valid-dates.js'; -// TODO: remove jquery. -import $ from 'jquery'; +import $, { window } from '../../../../js/modules/util/dollar-sign.js'; const graphSettings = { graphHeight: 0, @@ -107,7 +106,7 @@ function init() { moveIndicatorToAge($(this).attr('data-age-value')); }); - $('[data-bar_age]').click(function () { + $('#claim-canvas').on('click', '[data-bar_age]', function () { const age = $(this).attr('data-bar_age'); moveIndicatorToAge(age); }); @@ -192,11 +191,9 @@ function checkEstimateReady() { * Initializes the listener on the slider indicator */ function initIndicator() { - const $indicator = $('#graph__slider-input'); - /* Need both onchange and oninput to work in all browsers https://www.impressivewebs.com/onchange-vs-oninput-for-range-sliders/ */ - $indicator.on('change input', function () { + $('#claim-canvas').on('change input', '#graph_slider-input', function () { const indicatorValue = Number($(this).val()); setAgeWithIndicator(indicatorValue); }); diff --git a/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js b/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js index 9746869562e..2c7f76c8abf 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js @@ -1,7 +1,6 @@ import isElementInView from '../utils/is-element-in-view.js'; -// TODO: remove jquery. -import $ from 'jquery'; +import $ from '../../../../js/modules/util/dollar-sign.js'; /** * @param {number} ageRightNow - The current age. @@ -10,7 +9,7 @@ import $ from 'jquery'; function init(ageRightNow = 0, fullRetirementAge = 0) { limitAgeSelector(ageRightNow); - $('#retirement-age-selector').change(function () { + $('.step-three').on( 'change', '#retirement-age-selector', function () { chooseClaimingAge(fullRetirementAge); }); } diff --git a/cfgov/unprocessed/apps/retirement/js/views/questions-view.js b/cfgov/unprocessed/apps/retirement/js/views/questions-view.js index 004cb08bb65..4fdba22a27b 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/questions-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/questions-view.js @@ -1,5 +1,4 @@ -// TODO: remove jquery. -import $ from 'jquery'; +import $ from '../../../../js/modules/util/dollar-sign.js'; /** * Initialize the questions view. diff --git a/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js b/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js index 29adadb0c86..ed1c722f854 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js @@ -1,5 +1,4 @@ -// TODO: remove jquery. -import $ from 'jquery'; +import $ from '../../../../js/modules/util/dollar-sign.js'; /** * Initialize the tooltips view. diff --git a/cfgov/unprocessed/js/modules/util/dollar-sign.js b/cfgov/unprocessed/js/modules/util/dollar-sign.js new file mode 100644 index 00000000000..dc1b7824d96 --- /dev/null +++ b/cfgov/unprocessed/js/modules/util/dollar-sign.js @@ -0,0 +1,675 @@ +/** + * @param {Document|Window|string} selector - A jQuery-style selector. + * @returns {Document|Window|undefined} The document, window, or nothing. + */ + +function Query(selector) { + this.elements = []; + + if ( typeof selector === 'undefined' ) { + return this; + } + + if ( selector.nodeType ) { + this.elements[0] = selector; + + return this; + } + + this.selector = selector; + + // handle :last jquery selector + if ( selector.indexOf( ':last' ) !== -1 ) { + selector = selector.split( ':' )[0]; + const arr = document.querySelectorAll(selector); + this.elements = arr.length ? [arr[arr.length - 1]] : []; + this.selector = selector; + + return this; + } + + // handle :selected jquery selector + if ( selector.indexOf( ':selected' ) !== -1 ) { + selector = selector.split( ':' )[0]; + const arr = document.querySelector( selector ); + const parent = arr.parentElement; + + this.elements.push( parent.options[parent.selectedIndex]); + this.selector = selector; + + return this; + } + + if (selector === document) { + this.elements.push( document ); + } else if (selector === '$WINDOW' ) { + this.elements.push( document.defaultView ); + } else if (typeof selector === 'string' && selector !== '') { + this.elements = document.querySelectorAll(selector); + } + + return this; +} + +/**************************/ +// Helper functions +/**************************/ + +function pixelator( val ) { + if ( typeof val === 'number' ) { + return val.toString() + 'px' + } else if ( typeof val === 'string') { + return val.replace(/\D/g,'') + 'px'; + } + + return '0px'; +} + +/**************************/ +// Reference variables +/**************************/ + +const pixelStyles = [ + 'width', 'height', 'left', 'right', 'top', 'bottom' +]; + +const slideUpVars = { + styles: { + transitionProperty: 'height, margin, padding', + boxSizing: 'border-box', + overflow: 'hidden', + height: 0, + paddingTop: 0, + paddingBottom: 0, + marginTop: 0, + marginBottom: 0 + }, + removals: [ + 'height', + 'padding-top', + 'padding-bottom', + 'margin-top', + 'margin-bottom', + 'overflow', + 'transition-duration', + 'transition-property' + ] +} + +const slideDownVars = { + styles: { + overflow: 'hidden', + height: 0, + paddingTop: 0, + paddingBottom: 0, + marginTop: 0, + marginBottom: 0, + boxSizing: 'border-box', + transitionProperty: 'height, margin, padding' + }, + removals: [ + 'padding-top', + 'padding-bottom', + 'margin-top', + 'margin-bottom' + ], + delayedRemovals: [ + 'height', + 'overflow', + 'transition-duration', + 'transition-property' + ] + + +}; + +/**************************/ +// Object elements[] helpers +/**************************/ + +Query.prototype.each = function (callback) { + this.elements.forEach( function(elem, index) { + callback.call( elem, elem, index); + } ); +}; + +Query.prototype.find = function (findSelector) { + if (typeof this.selector !== 'undefined') { + return new Query(this.selector + ' ' + findSelector); + } else { + const q = new Query(); + const elemArr = []; + this.elements.forEach((elem) => { + elem.querySelectorAll(findSelector).forEach((elem) => { + elemArr.push(elem); + }); + }); + q.elements = elemArr; + + return q; + } +}; + +Query.prototype.closest = function( closestSelector) { + if ( typeof closestSelector === 'undefined' || this.elements.length < 1 ) return this; + const q = new Query(); + const elemArr = []; + elemArr.push( this.elements[0].closest( closestSelector ) ); + q.elements = elemArr; + + return q; +} + +Query.prototype.not = function (notSelector) { + if (typeof this.selector !== 'undefined') { + return new Query(this.selector + ':not(' + notSelector + ')'); + } else { + const q = new Query(); + const elemArr = []; + this.elements.forEach((elem) => { + if (!elem.matches(notSelector)) { + elemArr.push(elem); + } + }); + q.elements = elemArr; + + return q; + } +}; + +Query.prototype.filter = function (selector) { + return new Query(this.selector + '' + selector); +}; + +Query.prototype.siblings = function (selector) { + const q = new Query(); + const elemArr = []; + if (typeof selector === 'undefined') selector = '*'; + this.elements.forEach((elem) => { + let node = elem.parentNode.firstElementChild; + for (node; node !== null; node = node.nextElementSibling) { + if (node.matches(selector) && node !== elem) { + elemArr.push(node); + } + } + }); + q.elements = elemArr; + + return q; +}; + +Query.prototype.parent = function (selector) { + const q = new Query(); + const elemArr = []; + this.elements.forEach((elem) => { + const parent = elem.parentElement; + if (typeof selector === 'undefined' || parent.matches(selector)) { + elemArr.push(parent); + } + }); + + q.elements = elemArr; + + return q; +}; + +Query.prototype.remove = function () { + this.elements.forEach((elem) => { + elem.remove(); + }); + + return this; +}; + +/**************************/ +// DOM element text, value +/**************************/ + +Query.prototype.text = function (value) { + // getter + if (typeof value === 'undefined') { + return this.elements.length ? this.elements[0].textContent : null; + } + //setter + else { + this.elements.forEach((elem) => { + elem.textContent = value; + }); + + return this; + } +}; + +Query.prototype.html = function ( value ) { + //getter + if (typeof value === 'undefined') { + return this.elements.length ? this.elements[0].innerHTML : null; + } + //setter + else { + this.elements.forEach( elem => { + elem.innerHTML = value; + }); + + return this; + } +} + +Query.prototype.val = function (value) { + // getter + if (typeof value === 'undefined' && this.elements.length > 0) { + return this.elements.length ? this.elements[0].value : null; + } + //setter + else { + this.elements.forEach((elem) => { + elem.value = value; + }); + return this; + } +}; + +/**************************/ +// event listeners and triggers +/**************************/ +Query.prototype.listen = function (eventType, callback) { + this.elements.forEach((elmo) => { + elmo.addEventListener(eventType, callback); + }); +}; + +Query.prototype.click = function( callback ) { + this.listen( 'click', callback ); +} + +Query.prototype.submit = function( callback ) { + this.listen( 'submit', callback ); +} + +/** + * Handles events in eventType + * @param {string|function} paramOne - A selector or callback + * @param {function} paramTwo - A callback if paramOne is a selector. + */ +Query.prototype.on = function( eventType, paramOne, paramTwo ) { + if ( typeof paramOne == 'function' ) { + this.elements.forEach( elem => { + eventType.split(' ').forEach( type => { + elem.addEventListener( type, paramOne ); + }); + }) + } else { + this.elements.forEach( elem => { + const callback = function( ev ) { + if ( !ev.target ) return; + const elem = ev.target.closest( paramOne ); + if ( elem ) { + paramTwo.call( elem, ev ); + } + } + eventType.split(' ').forEach( type => { + elem.addEventListener( type, callback ); + }); + return callback; + }); + } + + return this; +} + +Query.prototype.blur = function() { + this.elements.forEach( elem => { + elem.blur(); + }) +} + +Query.prototype.change = function () { + this.elements.forEach( elem => { + if (elem.tagName === 'SELECT') { + let change = new Event('change'); + change.currentTarget = elem; + elem.dispatchEvent(change); + } + }); +}; + +Query.prototype.keypress = function( callback ) { + this.listen( 'keyup', callback ); +} + +Query.prototype.keyup = function( callback ) { + this.listen( 'keyup', callback ); +} + +Query.prototype.resize = function( callback ) { + document.defaultView.addEventListener('resize', callback ); +} + +/**************************/ +// classes, styles, attributes, and properties +/**************************/ + +Query.prototype.tagName = function () { + return this.elements.length ? this.elements[0].tagName : null; +}; + +Query.prototype.addClass = function (classNames) { + const classArr = classNames.split(' '); + this.elements.forEach((elem) => { + elem.classList.add(...classArr); + }); + + return this; +}; + +Query.prototype.removeClass = function (classNames) { + const classArr = classNames.split(' '); + this.elements.forEach((elem) => { + elem.classList.remove(...classArr); + }); + + return this; +}; + +Query.prototype.attr = function (name, value) { + if (typeof value === 'undefined') { + return this.elements.length ? this.elements[0].getAttribute(name) : null; + } else if (value === false) { + this.elements.forEach((elem) => { + elem.removeAttribute( name ); + }); + } else { + if ( this.elements.length < 1 ) return this; + this.elements.forEach( (elem) => { + elem.setAttribute(name, value); + }); + } + + return this; +}; + +Query.prototype.is = function( selector ) { + if ( typeof selector === 'string' ) { + return this.elements.length ? this.elements[0].matches( selector ) : this; + } + + return this; +} + +Query.prototype.hide = function () { + this.elements.forEach((elem) => { + elem.style.display = 'none'; + }); + + return this; +}; + +Query.prototype.show = function (className) { + this.elements.forEach((elem) => { + if (typeof className !== 'undefined') { + elem.style.display = className; + } else { + elem.style.display = 'block'; + } + }); + + return this; +}; + +Query.prototype.slideUp = function (duration=500) { + this.elements.forEach( elem => { + elem.style.transitionDuration = duration + 'ms'; + for (let key in slideUpVars.styles ) { + elem.style[key] = slideUpVars.styles[key]; + } + elem.style.height = elem.offsetHeight + 'px'; + void elem.offsetWidth; + document.defaultView.setTimeout( () => { + slideUpVars.removals.forEach( item => { + elem.style.removeProperty( item ); + }); + elem.style.display = 'none'; + }, duration ); + }); + + return this; +}; + +Query.prototype.slideDown = function(duration=500) { + this.elements.forEach( elem => { + elem.style.removeProperty( 'display' ); + let display = document.defaultView.getComputedStyle(elem).display; + if (display === 'none') display = 'block'; + elem.style.display = display; + let height = elem.offsetHeight; + for (let key in slideDownVars.styles) { + elem.style[key] = slideDownVars.styles[key]; + + } + void elem.offsetWidth; + elem.style.transitionDuration = duration + 'ms'; + elem.style.height = height + 'px'; + slideDownVars.removals.forEach( item => { + elem.style.removeProperty( item ); + }); + document.defaultView.setTimeout( () => { + slideDownVars.delayedRemovals.forEach( item => { + elem.style.removeProperty( item ); + }); + }) + }); + + return this; +} + +Query.prototype.height = function ( val ) { + if ( typeof val === 'undefined' ) { + if ( this.elements.length > 0 ) { + const elem = this.elements[0]; + if ( elem.hasOwnProperty( 'getBoundingClientRect' ) ) { + return elem.getBoundingClientRect().height + } + return this.elements[0].offsetHeight; + } + return null; + } else { + this.elements.forEach( elem => { + if ( typeof val === 'function' ) { + val = val(); + } else { + if ( typeof val !== 'string' ) { + val += 'px'; + } + elem.style.height = val; + } + }); + + return this; + } +}; + +Query.prototype.outerHeight = function ( val ) { + if ( typeof val === 'undefined' ) { + return this.elements.length ? this.elements[0].offsetHeight : null; + } else { + this.elements.forEach( elem => { + if ( typeof val === 'function' ) { + val = val(); + } else { + if ( typeof val !== 'string' ) { + val += 'px'; + } + elem.style.height = val; + } + }); + + return this; + } +}; + + +Query.prototype.top = function () { + return this.elements.length ? this.elements[0].offsetTop : null; +}; + +Query.prototype.width = function( val ) { + if ( typeof val === 'undefined' ) { + if ( this.elements.length < 1 ) return this; + const elem = this.elements[0]; + if ( elem.window === elem ) { + return document.defaultView.outerWidth; + } else { + return elem.getBoundingClientRect().width + } + } else { + if (typeof val === 'function') { + val = val(); + } else { + if ( this.elements.length < 1 ) return this; + if (typeof val !== 'string') val = val + 'px'; + this.elements.forEach( elem => { + elem.style.width = val; + }); + } + } +} + +Query.prototype.outerWidth = function( val ) { + if ( this.elements.length < 1 ) return this; + const elem = this.elements[0]; + if ( val === true ) { + const style = getComputedStyle( elem ); + return elem.getBoundingClientRect().width + + parseFloat( style.marginLeft ) + parseFloat( style.marginRight ); + } else { + return elem.offsetWidth; + } +} + +Query.prototype.scrollTop = function( val ) { + if ( typeof val === undefined ) { + const elem = this.elements[0]; + if ( elem.window === elem ) { + return document.defaultView.pageYOffset; + } else { + return elem.scrollTop; + } + } else { + this.elements.forEach( elem => { + if ( elem.window === elem ) { + const xOff = document.defaultView.pageXOffset; + document.defaultView.scrollTo( xOff, val ); + } else { + elem.scrollTop = val; + } + }); + } +} + +Query.prototype.offset = function( val ) { + if ( this.elements.length < 1 ) return this; + const elem = this.elements[0]; + const win = document.defaultView; + const bound = elem.getBoundingClientRect(); + const docTop = document.documentElement.clientTop; + const docLeft = document.documentElement.clientLeft; + return { + top: bound.top + win.pageYOffset + docTop, + left: bound.left + win.pageXOffset + docLeft + }; +} + +Query.prototype.css = function( param, value ) { + if ( this.elements.length < 1 ) return this; + let obj = {}; + if ( typeof param !== 'object' && typeof value === 'undefined' ) { + const styles = getComputedStyle( this.elements[0] ); + return styles[param]; + } else if ( typeof param !== 'object' ) { + obj[param] = value; + } else { + obj = param; + } + for ( let key in obj ) { + this.elements.forEach( elem => { + if ( elem.style.hasOwnProperty( key ) ) { + let val = obj[key]; + if ( pixelStyles.indexOf( key ) > -1 ) val = pixelator(val) ; + elem.style[key] = val; + } + }); + } + return this; +} + +/**************************/ +// DOM manipulation +/**************************/ + +Query.prototype.cloner = function () { + return this.elements.length ? this.elements[0].cloneNode(true) : null; +}; + +Query.prototype.append = function( elem ) { + this.elements.forEach( parent => { + if ( typeof elem === 'string' ) { + parent.innerHTML += elem; + } else { + parent.append( elem ); + } + }); + + return this; +} + +Query.prototype.appendTo = function (newParents) { + // This method is designed only to work on instances of Query + if (newParents instanceof Query !== true) { + throw Error( + 'Error: appendTo can only accept an instance of Query as a parameter', + ); + } else { + newParents.elements.forEach((parent) => { + this.elements.forEach((child) => { + parent.appendChild(child); + }); + }); + } + + return this; +}; + +Query.prototype.empty = function() { + this.elements.forEach( elem => { + elem.replaceChildren(); + }); +} + +// Constructor function +const $ = function (param) { + if (typeof param === 'string') { + return new Query(param); + } else if (typeof param === 'object') { + if ( param.nodeType ) { + const q = new Query(); + q.elements = [param]; + return q; + } else { + const q = new Query(); + this.elements = param; + return q; + } + } +}; + +$.each = function( obj, callback) { + obj.forEach( function(elem, index) { + // This is an odd reversal of parameters, but matches jQuery.each() + callback( index, elem ); + }, obj ); +} + + +export const window = '$WINDOW'; + +export default $; From 8a3bee22ebd0ec919307d889840763c46074028e Mon Sep 17 00:00:00 2001 From: Wyatt Pearsall Date: Wed, 16 Oct 2024 09:38:32 -0700 Subject: [PATCH 2/7] Rewrite retirement visibility checks --- cfgov/unprocessed/apps/retirement/js/views/graph-view.js | 4 ++-- cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cfgov/unprocessed/apps/retirement/js/views/graph-view.js b/cfgov/unprocessed/apps/retirement/js/views/graph-view.js index 9d50fcfa761..6cd8d3ef9f7 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/graph-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/graph-view.js @@ -35,6 +35,7 @@ const textlets = { }; const currentLanguage = document.querySelector('html').getAttribute('lang'); +const graphContainer = document.getElementById('graph-container'); // TODO: merge textlets and catalog hashes. const catalog = { @@ -147,8 +148,7 @@ function init() { // Window resize handler $(window).resize(function () { - const hiddenContent = '.step-one-hidden, .step-three .hidden-content'; - if ($(hiddenContent).is(':visible')) { + if (graphContainer.style.display == 'block') { redrawGraph(); } }); diff --git a/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js b/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js index ed1c722f854..22a63ccd5af 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/tooltips-view.js @@ -1,5 +1,7 @@ import $ from '../../../../js/modules/util/dollar-sign.js'; +const tooltipContainer = document.getElementById('tooltip-container'); + /** * Initialize the tooltips view. */ @@ -95,8 +97,8 @@ function toolTipper(elem) { }); window.addEventListener('resize', function () { - if ($('#tooltip-container').is(':visible')) { - $('#tooltip-container').hide(); + if (tooltipContainer.style.display == 'block') { + tooltipContainer.style.display = 'none'; } }); } From 5f8f79091910de4afddebcc86a82bd08fcdb60e7 Mon Sep 17 00:00:00 2001 From: Chuck Werner Date: Thu, 17 Oct 2024 12:42:39 -0400 Subject: [PATCH 3/7] - Fix incorrect selector --- cfgov/unprocessed/apps/retirement/js/views/graph-view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfgov/unprocessed/apps/retirement/js/views/graph-view.js b/cfgov/unprocessed/apps/retirement/js/views/graph-view.js index 6cd8d3ef9f7..4ad65bc0e87 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/graph-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/graph-view.js @@ -193,7 +193,7 @@ function checkEstimateReady() { function initIndicator() { /* Need both onchange and oninput to work in all browsers https://www.impressivewebs.com/onchange-vs-oninput-for-range-sliders/ */ - $('#claim-canvas').on('change input', '#graph_slider-input', function () { + $('#claim-canvas').on('change input', '#graph__slider-input', function () { const indicatorValue = Number($(this).val()); setAgeWithIndicator(indicatorValue); }); From 67aef05e4fc8ce61871055219cf39c23f4af6987 Mon Sep 17 00:00:00 2001 From: Wyatt Pearsall Date: Mon, 21 Oct 2024 09:09:24 -0700 Subject: [PATCH 4/7] Move disclosures to modules version of dollar-sign --- .../js/disclosures/utils/dollar-sign.js | 675 ------------------ .../js/disclosures/views/expenses-view.js | 2 +- .../js/disclosures/views/financial-view.js | 2 +- .../js/disclosures/views/links-view.js | 2 +- .../js/disclosures/views/metric-view.js | 2 +- .../js/disclosures/views/question-view.js | 2 +- 6 files changed, 5 insertions(+), 680 deletions(-) delete mode 100644 cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js deleted file mode 100644 index dc1b7824d96..00000000000 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/utils/dollar-sign.js +++ /dev/null @@ -1,675 +0,0 @@ -/** - * @param {Document|Window|string} selector - A jQuery-style selector. - * @returns {Document|Window|undefined} The document, window, or nothing. - */ - -function Query(selector) { - this.elements = []; - - if ( typeof selector === 'undefined' ) { - return this; - } - - if ( selector.nodeType ) { - this.elements[0] = selector; - - return this; - } - - this.selector = selector; - - // handle :last jquery selector - if ( selector.indexOf( ':last' ) !== -1 ) { - selector = selector.split( ':' )[0]; - const arr = document.querySelectorAll(selector); - this.elements = arr.length ? [arr[arr.length - 1]] : []; - this.selector = selector; - - return this; - } - - // handle :selected jquery selector - if ( selector.indexOf( ':selected' ) !== -1 ) { - selector = selector.split( ':' )[0]; - const arr = document.querySelector( selector ); - const parent = arr.parentElement; - - this.elements.push( parent.options[parent.selectedIndex]); - this.selector = selector; - - return this; - } - - if (selector === document) { - this.elements.push( document ); - } else if (selector === '$WINDOW' ) { - this.elements.push( document.defaultView ); - } else if (typeof selector === 'string' && selector !== '') { - this.elements = document.querySelectorAll(selector); - } - - return this; -} - -/**************************/ -// Helper functions -/**************************/ - -function pixelator( val ) { - if ( typeof val === 'number' ) { - return val.toString() + 'px' - } else if ( typeof val === 'string') { - return val.replace(/\D/g,'') + 'px'; - } - - return '0px'; -} - -/**************************/ -// Reference variables -/**************************/ - -const pixelStyles = [ - 'width', 'height', 'left', 'right', 'top', 'bottom' -]; - -const slideUpVars = { - styles: { - transitionProperty: 'height, margin, padding', - boxSizing: 'border-box', - overflow: 'hidden', - height: 0, - paddingTop: 0, - paddingBottom: 0, - marginTop: 0, - marginBottom: 0 - }, - removals: [ - 'height', - 'padding-top', - 'padding-bottom', - 'margin-top', - 'margin-bottom', - 'overflow', - 'transition-duration', - 'transition-property' - ] -} - -const slideDownVars = { - styles: { - overflow: 'hidden', - height: 0, - paddingTop: 0, - paddingBottom: 0, - marginTop: 0, - marginBottom: 0, - boxSizing: 'border-box', - transitionProperty: 'height, margin, padding' - }, - removals: [ - 'padding-top', - 'padding-bottom', - 'margin-top', - 'margin-bottom' - ], - delayedRemovals: [ - 'height', - 'overflow', - 'transition-duration', - 'transition-property' - ] - - -}; - -/**************************/ -// Object elements[] helpers -/**************************/ - -Query.prototype.each = function (callback) { - this.elements.forEach( function(elem, index) { - callback.call( elem, elem, index); - } ); -}; - -Query.prototype.find = function (findSelector) { - if (typeof this.selector !== 'undefined') { - return new Query(this.selector + ' ' + findSelector); - } else { - const q = new Query(); - const elemArr = []; - this.elements.forEach((elem) => { - elem.querySelectorAll(findSelector).forEach((elem) => { - elemArr.push(elem); - }); - }); - q.elements = elemArr; - - return q; - } -}; - -Query.prototype.closest = function( closestSelector) { - if ( typeof closestSelector === 'undefined' || this.elements.length < 1 ) return this; - const q = new Query(); - const elemArr = []; - elemArr.push( this.elements[0].closest( closestSelector ) ); - q.elements = elemArr; - - return q; -} - -Query.prototype.not = function (notSelector) { - if (typeof this.selector !== 'undefined') { - return new Query(this.selector + ':not(' + notSelector + ')'); - } else { - const q = new Query(); - const elemArr = []; - this.elements.forEach((elem) => { - if (!elem.matches(notSelector)) { - elemArr.push(elem); - } - }); - q.elements = elemArr; - - return q; - } -}; - -Query.prototype.filter = function (selector) { - return new Query(this.selector + '' + selector); -}; - -Query.prototype.siblings = function (selector) { - const q = new Query(); - const elemArr = []; - if (typeof selector === 'undefined') selector = '*'; - this.elements.forEach((elem) => { - let node = elem.parentNode.firstElementChild; - for (node; node !== null; node = node.nextElementSibling) { - if (node.matches(selector) && node !== elem) { - elemArr.push(node); - } - } - }); - q.elements = elemArr; - - return q; -}; - -Query.prototype.parent = function (selector) { - const q = new Query(); - const elemArr = []; - this.elements.forEach((elem) => { - const parent = elem.parentElement; - if (typeof selector === 'undefined' || parent.matches(selector)) { - elemArr.push(parent); - } - }); - - q.elements = elemArr; - - return q; -}; - -Query.prototype.remove = function () { - this.elements.forEach((elem) => { - elem.remove(); - }); - - return this; -}; - -/**************************/ -// DOM element text, value -/**************************/ - -Query.prototype.text = function (value) { - // getter - if (typeof value === 'undefined') { - return this.elements.length ? this.elements[0].textContent : null; - } - //setter - else { - this.elements.forEach((elem) => { - elem.textContent = value; - }); - - return this; - } -}; - -Query.prototype.html = function ( value ) { - //getter - if (typeof value === 'undefined') { - return this.elements.length ? this.elements[0].innerHTML : null; - } - //setter - else { - this.elements.forEach( elem => { - elem.innerHTML = value; - }); - - return this; - } -} - -Query.prototype.val = function (value) { - // getter - if (typeof value === 'undefined' && this.elements.length > 0) { - return this.elements.length ? this.elements[0].value : null; - } - //setter - else { - this.elements.forEach((elem) => { - elem.value = value; - }); - return this; - } -}; - -/**************************/ -// event listeners and triggers -/**************************/ -Query.prototype.listen = function (eventType, callback) { - this.elements.forEach((elmo) => { - elmo.addEventListener(eventType, callback); - }); -}; - -Query.prototype.click = function( callback ) { - this.listen( 'click', callback ); -} - -Query.prototype.submit = function( callback ) { - this.listen( 'submit', callback ); -} - -/** - * Handles events in eventType - * @param {string|function} paramOne - A selector or callback - * @param {function} paramTwo - A callback if paramOne is a selector. - */ -Query.prototype.on = function( eventType, paramOne, paramTwo ) { - if ( typeof paramOne == 'function' ) { - this.elements.forEach( elem => { - eventType.split(' ').forEach( type => { - elem.addEventListener( type, paramOne ); - }); - }) - } else { - this.elements.forEach( elem => { - const callback = function( ev ) { - if ( !ev.target ) return; - const elem = ev.target.closest( paramOne ); - if ( elem ) { - paramTwo.call( elem, ev ); - } - } - eventType.split(' ').forEach( type => { - elem.addEventListener( type, callback ); - }); - return callback; - }); - } - - return this; -} - -Query.prototype.blur = function() { - this.elements.forEach( elem => { - elem.blur(); - }) -} - -Query.prototype.change = function () { - this.elements.forEach( elem => { - if (elem.tagName === 'SELECT') { - let change = new Event('change'); - change.currentTarget = elem; - elem.dispatchEvent(change); - } - }); -}; - -Query.prototype.keypress = function( callback ) { - this.listen( 'keyup', callback ); -} - -Query.prototype.keyup = function( callback ) { - this.listen( 'keyup', callback ); -} - -Query.prototype.resize = function( callback ) { - document.defaultView.addEventListener('resize', callback ); -} - -/**************************/ -// classes, styles, attributes, and properties -/**************************/ - -Query.prototype.tagName = function () { - return this.elements.length ? this.elements[0].tagName : null; -}; - -Query.prototype.addClass = function (classNames) { - const classArr = classNames.split(' '); - this.elements.forEach((elem) => { - elem.classList.add(...classArr); - }); - - return this; -}; - -Query.prototype.removeClass = function (classNames) { - const classArr = classNames.split(' '); - this.elements.forEach((elem) => { - elem.classList.remove(...classArr); - }); - - return this; -}; - -Query.prototype.attr = function (name, value) { - if (typeof value === 'undefined') { - return this.elements.length ? this.elements[0].getAttribute(name) : null; - } else if (value === false) { - this.elements.forEach((elem) => { - elem.removeAttribute( name ); - }); - } else { - if ( this.elements.length < 1 ) return this; - this.elements.forEach( (elem) => { - elem.setAttribute(name, value); - }); - } - - return this; -}; - -Query.prototype.is = function( selector ) { - if ( typeof selector === 'string' ) { - return this.elements.length ? this.elements[0].matches( selector ) : this; - } - - return this; -} - -Query.prototype.hide = function () { - this.elements.forEach((elem) => { - elem.style.display = 'none'; - }); - - return this; -}; - -Query.prototype.show = function (className) { - this.elements.forEach((elem) => { - if (typeof className !== 'undefined') { - elem.style.display = className; - } else { - elem.style.display = 'block'; - } - }); - - return this; -}; - -Query.prototype.slideUp = function (duration=500) { - this.elements.forEach( elem => { - elem.style.transitionDuration = duration + 'ms'; - for (let key in slideUpVars.styles ) { - elem.style[key] = slideUpVars.styles[key]; - } - elem.style.height = elem.offsetHeight + 'px'; - void elem.offsetWidth; - document.defaultView.setTimeout( () => { - slideUpVars.removals.forEach( item => { - elem.style.removeProperty( item ); - }); - elem.style.display = 'none'; - }, duration ); - }); - - return this; -}; - -Query.prototype.slideDown = function(duration=500) { - this.elements.forEach( elem => { - elem.style.removeProperty( 'display' ); - let display = document.defaultView.getComputedStyle(elem).display; - if (display === 'none') display = 'block'; - elem.style.display = display; - let height = elem.offsetHeight; - for (let key in slideDownVars.styles) { - elem.style[key] = slideDownVars.styles[key]; - - } - void elem.offsetWidth; - elem.style.transitionDuration = duration + 'ms'; - elem.style.height = height + 'px'; - slideDownVars.removals.forEach( item => { - elem.style.removeProperty( item ); - }); - document.defaultView.setTimeout( () => { - slideDownVars.delayedRemovals.forEach( item => { - elem.style.removeProperty( item ); - }); - }) - }); - - return this; -} - -Query.prototype.height = function ( val ) { - if ( typeof val === 'undefined' ) { - if ( this.elements.length > 0 ) { - const elem = this.elements[0]; - if ( elem.hasOwnProperty( 'getBoundingClientRect' ) ) { - return elem.getBoundingClientRect().height - } - return this.elements[0].offsetHeight; - } - return null; - } else { - this.elements.forEach( elem => { - if ( typeof val === 'function' ) { - val = val(); - } else { - if ( typeof val !== 'string' ) { - val += 'px'; - } - elem.style.height = val; - } - }); - - return this; - } -}; - -Query.prototype.outerHeight = function ( val ) { - if ( typeof val === 'undefined' ) { - return this.elements.length ? this.elements[0].offsetHeight : null; - } else { - this.elements.forEach( elem => { - if ( typeof val === 'function' ) { - val = val(); - } else { - if ( typeof val !== 'string' ) { - val += 'px'; - } - elem.style.height = val; - } - }); - - return this; - } -}; - - -Query.prototype.top = function () { - return this.elements.length ? this.elements[0].offsetTop : null; -}; - -Query.prototype.width = function( val ) { - if ( typeof val === 'undefined' ) { - if ( this.elements.length < 1 ) return this; - const elem = this.elements[0]; - if ( elem.window === elem ) { - return document.defaultView.outerWidth; - } else { - return elem.getBoundingClientRect().width - } - } else { - if (typeof val === 'function') { - val = val(); - } else { - if ( this.elements.length < 1 ) return this; - if (typeof val !== 'string') val = val + 'px'; - this.elements.forEach( elem => { - elem.style.width = val; - }); - } - } -} - -Query.prototype.outerWidth = function( val ) { - if ( this.elements.length < 1 ) return this; - const elem = this.elements[0]; - if ( val === true ) { - const style = getComputedStyle( elem ); - return elem.getBoundingClientRect().width + - parseFloat( style.marginLeft ) + parseFloat( style.marginRight ); - } else { - return elem.offsetWidth; - } -} - -Query.prototype.scrollTop = function( val ) { - if ( typeof val === undefined ) { - const elem = this.elements[0]; - if ( elem.window === elem ) { - return document.defaultView.pageYOffset; - } else { - return elem.scrollTop; - } - } else { - this.elements.forEach( elem => { - if ( elem.window === elem ) { - const xOff = document.defaultView.pageXOffset; - document.defaultView.scrollTo( xOff, val ); - } else { - elem.scrollTop = val; - } - }); - } -} - -Query.prototype.offset = function( val ) { - if ( this.elements.length < 1 ) return this; - const elem = this.elements[0]; - const win = document.defaultView; - const bound = elem.getBoundingClientRect(); - const docTop = document.documentElement.clientTop; - const docLeft = document.documentElement.clientLeft; - return { - top: bound.top + win.pageYOffset + docTop, - left: bound.left + win.pageXOffset + docLeft - }; -} - -Query.prototype.css = function( param, value ) { - if ( this.elements.length < 1 ) return this; - let obj = {}; - if ( typeof param !== 'object' && typeof value === 'undefined' ) { - const styles = getComputedStyle( this.elements[0] ); - return styles[param]; - } else if ( typeof param !== 'object' ) { - obj[param] = value; - } else { - obj = param; - } - for ( let key in obj ) { - this.elements.forEach( elem => { - if ( elem.style.hasOwnProperty( key ) ) { - let val = obj[key]; - if ( pixelStyles.indexOf( key ) > -1 ) val = pixelator(val) ; - elem.style[key] = val; - } - }); - } - return this; -} - -/**************************/ -// DOM manipulation -/**************************/ - -Query.prototype.cloner = function () { - return this.elements.length ? this.elements[0].cloneNode(true) : null; -}; - -Query.prototype.append = function( elem ) { - this.elements.forEach( parent => { - if ( typeof elem === 'string' ) { - parent.innerHTML += elem; - } else { - parent.append( elem ); - } - }); - - return this; -} - -Query.prototype.appendTo = function (newParents) { - // This method is designed only to work on instances of Query - if (newParents instanceof Query !== true) { - throw Error( - 'Error: appendTo can only accept an instance of Query as a parameter', - ); - } else { - newParents.elements.forEach((parent) => { - this.elements.forEach((child) => { - parent.appendChild(child); - }); - }); - } - - return this; -}; - -Query.prototype.empty = function() { - this.elements.forEach( elem => { - elem.replaceChildren(); - }); -} - -// Constructor function -const $ = function (param) { - if (typeof param === 'string') { - return new Query(param); - } else if (typeof param === 'object') { - if ( param.nodeType ) { - const q = new Query(); - q.elements = [param]; - return q; - } else { - const q = new Query(); - this.elements = param; - return q; - } - } -}; - -$.each = function( obj, callback) { - obj.forEach( function(elem, index) { - // This is an odd reversal of parameters, but matches jQuery.each() - callback( index, elem ); - }, obj ); -} - - -export const window = '$WINDOW'; - -export default $; diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/expenses-view.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/expenses-view.js index fe7d1520909..4af15263dc8 100755 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/expenses-view.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/expenses-view.js @@ -1,4 +1,4 @@ -import $ from '../utils/dollar-sign.js'; +import $ from '../../../../../js/modules/util/dollar-sign.js'; import { analyticsSendEvent } from '@cfpb/cfpb-analytics'; import getExpenses from '../dispatchers/get-expenses-values.js'; import publish from '../dispatchers/publish-update.js'; diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/financial-view.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/financial-view.js index 8f67a618d56..86aa1f84e55 100755 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/financial-view.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/financial-view.js @@ -1,4 +1,4 @@ -import $ from '../utils/dollar-sign.js'; +import $ from '../../../../../js/modules/util/dollar-sign.js'; import { analyticsSendEvent } from '@cfpb/cfpb-analytics'; import getFinancial from '../dispatchers/get-financial-values.js'; import getExpenses from '../dispatchers/get-expenses-values.js'; diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/links-view.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/links-view.js index dc6a680eae5..af66787e150 100755 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/links-view.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/links-view.js @@ -1,6 +1,6 @@ import formatURL from '../utils/format-url.js'; import constructScorecardSearch from '../utils/construct-scorecard-search.js'; -import $ from '../utils/dollar-sign.js'; +import $ from '../../../../../js/modules/util/dollar-sign.js'; const linksView = { $gradLinkText: $('.graduation-link'), diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/metric-view.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/metric-view.js index 92a69eb0986..c55cfc8e6d8 100755 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/metric-view.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/metric-view.js @@ -1,4 +1,4 @@ -import $ from '../utils/dollar-sign.js'; +import $ from '../../../../../js/modules/util/dollar-sign.js'; import getFinancial from '../dispatchers/get-financial-values.js'; import getSchool from '../dispatchers/get-school-values.js'; import { formatUSD } from '../../../../../js/modules/util/format.js'; diff --git a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/question-view.js b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/question-view.js index ff13cf2608d..fd73181c4eb 100755 --- a/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/question-view.js +++ b/cfgov/unprocessed/apps/paying-for-college/js/disclosures/views/question-view.js @@ -1,4 +1,4 @@ -import $ from '../utils/dollar-sign.js'; +import $ from '../../../../../js/modules/util/dollar-sign.js'; import { analyticsSendEvent } from '@cfpb/cfpb-analytics'; import postVerification from '../dispatchers/post-verify.js'; import getFinancial from '../dispatchers/get-financial-values.js'; From e91ced131521ad8dda32d01a7fae4ff625f1e5fa Mon Sep 17 00:00:00 2001 From: Wyatt Pearsall Date: Mon, 21 Oct 2024 09:47:53 -0700 Subject: [PATCH 5/7] Linting improvements --- .../js/modules/util/dollar-sign.js | 382 +++++++++--------- 1 file changed, 190 insertions(+), 192 deletions(-) diff --git a/cfgov/unprocessed/js/modules/util/dollar-sign.js b/cfgov/unprocessed/js/modules/util/dollar-sign.js index dc1b7824d96..4ca111d51de 100644 --- a/cfgov/unprocessed/js/modules/util/dollar-sign.js +++ b/cfgov/unprocessed/js/modules/util/dollar-sign.js @@ -2,15 +2,14 @@ * @param {Document|Window|string} selector - A jQuery-style selector. * @returns {Document|Window|undefined} The document, window, or nothing. */ - function Query(selector) { this.elements = []; - - if ( typeof selector === 'undefined' ) { + + if (typeof selector === 'undefined') { return this; } - if ( selector.nodeType ) { + if (selector.nodeType) { this.elements[0] = selector; return this; @@ -19,8 +18,8 @@ function Query(selector) { this.selector = selector; // handle :last jquery selector - if ( selector.indexOf( ':last' ) !== -1 ) { - selector = selector.split( ':' )[0]; + if (selector.indexOf(':last') !== -1) { + selector = selector.split(':')[0]; const arr = document.querySelectorAll(selector); this.elements = arr.length ? [arr[arr.length - 1]] : []; this.selector = selector; @@ -29,21 +28,21 @@ function Query(selector) { } // handle :selected jquery selector - if ( selector.indexOf( ':selected' ) !== -1 ) { - selector = selector.split( ':' )[0]; - const arr = document.querySelector( selector ); + if (selector.indexOf(':selected') !== -1) { + selector = selector.split(':')[0]; + const arr = document.querySelector(selector); const parent = arr.parentElement; - this.elements.push( parent.options[parent.selectedIndex]); + this.elements.push(parent.options[parent.selectedIndex]); this.selector = selector; return this; } if (selector === document) { - this.elements.push( document ); - } else if (selector === '$WINDOW' ) { - this.elements.push( document.defaultView ); + this.elements.push(document); + } else if (selector === '$WINDOW') { + this.elements.push(document.defaultView); } else if (typeof selector === 'string' && selector !== '') { this.elements = document.querySelectorAll(selector); } @@ -55,11 +54,16 @@ function Query(selector) { // Helper functions /**************************/ -function pixelator( val ) { - if ( typeof val === 'number' ) { - return val.toString() + 'px' - } else if ( typeof val === 'string') { - return val.replace(/\D/g,'') + 'px'; +/** + * + * @param {number|string} val - The value to make into stringified pixels + * @returns {string} - Value converted to stringifed pixels + */ +function pixelator(val) { + if (typeof val === 'number') { + return val.toString() + 'px'; + } else if (typeof val === 'string') { + return val.replace(/\D/g, '') + 'px'; } return '0px'; @@ -69,9 +73,7 @@ function pixelator( val ) { // Reference variables /**************************/ -const pixelStyles = [ - 'width', 'height', 'left', 'right', 'top', 'bottom' -]; +const pixelStyles = ['width', 'height', 'left', 'right', 'top', 'bottom']; const slideUpVars = { styles: { @@ -82,7 +84,7 @@ const slideUpVars = { paddingTop: 0, paddingBottom: 0, marginTop: 0, - marginBottom: 0 + marginBottom: 0, }, removals: [ 'height', @@ -92,9 +94,9 @@ const slideUpVars = { 'margin-bottom', 'overflow', 'transition-duration', - 'transition-property' - ] -} + 'transition-property', + ], +}; const slideDownVars = { styles: { @@ -105,22 +107,15 @@ const slideDownVars = { marginTop: 0, marginBottom: 0, boxSizing: 'border-box', - transitionProperty: 'height, margin, padding' + transitionProperty: 'height, margin, padding', }, - removals: [ - 'padding-top', - 'padding-bottom', - 'margin-top', - 'margin-bottom' - ], + removals: ['padding-top', 'padding-bottom', 'margin-top', 'margin-bottom'], delayedRemovals: [ 'height', 'overflow', 'transition-duration', - 'transition-property' - ] - - + 'transition-property', + ], }; /**************************/ @@ -128,9 +123,9 @@ const slideDownVars = { /**************************/ Query.prototype.each = function (callback) { - this.elements.forEach( function(elem, index) { - callback.call( elem, elem, index); - } ); + this.elements.forEach(function (elem, index) { + callback.call(elem, elem, index); + }); }; Query.prototype.find = function (findSelector) { @@ -150,15 +145,16 @@ Query.prototype.find = function (findSelector) { } }; -Query.prototype.closest = function( closestSelector) { - if ( typeof closestSelector === 'undefined' || this.elements.length < 1 ) return this; +Query.prototype.closest = function (closestSelector) { + if (typeof closestSelector === 'undefined' || this.elements.length < 1) + return this; const q = new Query(); const elemArr = []; - elemArr.push( this.elements[0].closest( closestSelector ) ); + elemArr.push(this.elements[0].closest(closestSelector)); q.elements = elemArr; return q; -} +}; Query.prototype.not = function (notSelector) { if (typeof this.selector !== 'undefined') { @@ -240,20 +236,20 @@ Query.prototype.text = function (value) { } }; -Query.prototype.html = function ( value ) { +Query.prototype.html = function (value) { //getter if (typeof value === 'undefined') { return this.elements.length ? this.elements[0].innerHTML : null; } //setter else { - this.elements.forEach( elem => { + this.elements.forEach((elem) => { elem.innerHTML = value; }); return this; } -} +}; Query.prototype.val = function (value) { // getter @@ -278,72 +274,74 @@ Query.prototype.listen = function (eventType, callback) { }); }; -Query.prototype.click = function( callback ) { - this.listen( 'click', callback ); -} +Query.prototype.click = function (callback) { + this.listen('click', callback); +}; -Query.prototype.submit = function( callback ) { - this.listen( 'submit', callback ); -} +Query.prototype.submit = function (callback) { + this.listen('submit', callback); +}; /** * Handles events in eventType - * @param {string|function} paramOne - A selector or callback - * @param {function} paramTwo - A callback if paramOne is a selector. + * @param {string} eventType - the type of event + * @param {string | Function} paramOne - A selector or callback + * @param {Function} paramTwo - A callback if paramOne is a selector. + * @returns {object} - this */ -Query.prototype.on = function( eventType, paramOne, paramTwo ) { - if ( typeof paramOne == 'function' ) { - this.elements.forEach( elem => { - eventType.split(' ').forEach( type => { - elem.addEventListener( type, paramOne ); +Query.prototype.on = function (eventType, paramOne, paramTwo) { + if (typeof paramOne == 'function') { + this.elements.forEach((elem) => { + eventType.split(' ').forEach((type) => { + elem.addEventListener(type, paramOne); }); - }) + }); } else { - this.elements.forEach( elem => { - const callback = function( ev ) { - if ( !ev.target ) return; - const elem = ev.target.closest( paramOne ); - if ( elem ) { - paramTwo.call( elem, ev ); + this.elements.forEach((elem) => { + const callback = function (ev) { + if (!ev.target) return; + const elem = ev.target.closest(paramOne); + if (elem) { + paramTwo.call(elem, ev); } - } - eventType.split(' ').forEach( type => { - elem.addEventListener( type, callback ); + }; + eventType.split(' ').forEach((type) => { + elem.addEventListener(type, callback); }); return callback; }); } return this; -} +}; -Query.prototype.blur = function() { - this.elements.forEach( elem => { +Query.prototype.blur = function () { + this.elements.forEach((elem) => { elem.blur(); - }) -} + }); +}; Query.prototype.change = function () { - this.elements.forEach( elem => { + this.elements.forEach((elem) => { if (elem.tagName === 'SELECT') { - let change = new Event('change'); + const change = new Event('change'); change.currentTarget = elem; elem.dispatchEvent(change); } }); }; -Query.prototype.keypress = function( callback ) { - this.listen( 'keyup', callback ); -} +Query.prototype.keypress = function (callback) { + this.listen('keyup', callback); +}; -Query.prototype.keyup = function( callback ) { - this.listen( 'keyup', callback ); -} +Query.prototype.keyup = function (callback) { + this.listen('keyup', callback); +}; -Query.prototype.resize = function( callback ) { - document.defaultView.addEventListener('resize', callback ); -} +Query.prototype.resize = function (callback) { + document.defaultView.addEventListener('resize', callback); +}; /**************************/ // classes, styles, attributes, and properties @@ -376,11 +374,11 @@ Query.prototype.attr = function (name, value) { return this.elements.length ? this.elements[0].getAttribute(name) : null; } else if (value === false) { this.elements.forEach((elem) => { - elem.removeAttribute( name ); + elem.removeAttribute(name); }); } else { - if ( this.elements.length < 1 ) return this; - this.elements.forEach( (elem) => { + if (this.elements.length < 1) return this; + this.elements.forEach((elem) => { elem.setAttribute(name, value); }); } @@ -388,13 +386,13 @@ Query.prototype.attr = function (name, value) { return this; }; -Query.prototype.is = function( selector ) { - if ( typeof selector === 'string' ) { - return this.elements.length ? this.elements[0].matches( selector ) : this; +Query.prototype.is = function (selector) { + if (typeof selector === 'string') { + return this.elements.length ? this.elements[0].matches(selector) : this; } return this; -} +}; Query.prototype.hide = function () { this.elements.forEach((elem) => { @@ -416,191 +414,192 @@ Query.prototype.show = function (className) { return this; }; -Query.prototype.slideUp = function (duration=500) { - this.elements.forEach( elem => { +Query.prototype.slideUp = function (duration = 500) { + this.elements.forEach((elem) => { elem.style.transitionDuration = duration + 'ms'; - for (let key in slideUpVars.styles ) { + for (const key in slideUpVars.styles) { elem.style[key] = slideUpVars.styles[key]; } elem.style.height = elem.offsetHeight + 'px'; void elem.offsetWidth; - document.defaultView.setTimeout( () => { - slideUpVars.removals.forEach( item => { - elem.style.removeProperty( item ); + document.defaultView.setTimeout(() => { + slideUpVars.removals.forEach((item) => { + elem.style.removeProperty(item); }); elem.style.display = 'none'; - }, duration ); + }, duration); }); return this; }; -Query.prototype.slideDown = function(duration=500) { - this.elements.forEach( elem => { - elem.style.removeProperty( 'display' ); +Query.prototype.slideDown = function (duration = 500) { + this.elements.forEach((elem) => { + elem.style.removeProperty('display'); let display = document.defaultView.getComputedStyle(elem).display; if (display === 'none') display = 'block'; elem.style.display = display; - let height = elem.offsetHeight; - for (let key in slideDownVars.styles) { + const height = elem.offsetHeight; + for (const key in slideDownVars.styles) { elem.style[key] = slideDownVars.styles[key]; - } void elem.offsetWidth; elem.style.transitionDuration = duration + 'ms'; elem.style.height = height + 'px'; - slideDownVars.removals.forEach( item => { - elem.style.removeProperty( item ); + slideDownVars.removals.forEach((item) => { + elem.style.removeProperty(item); }); - document.defaultView.setTimeout( () => { - slideDownVars.delayedRemovals.forEach( item => { - elem.style.removeProperty( item ); + document.defaultView.setTimeout(() => { + slideDownVars.delayedRemovals.forEach((item) => { + elem.style.removeProperty(item); }); - }) + }); }); return this; -} +}; -Query.prototype.height = function ( val ) { - if ( typeof val === 'undefined' ) { - if ( this.elements.length > 0 ) { +Query.prototype.height = function (val) { + if (typeof val === 'undefined') { + if (this.elements.length > 0) { const elem = this.elements[0]; - if ( elem.hasOwnProperty( 'getBoundingClientRect' ) ) { - return elem.getBoundingClientRect().height + if (Object.prototype.hasOwnProperty.call(elem, 'getBoundingClientRect')) { + return elem.getBoundingClientRect().height; } return this.elements[0].offsetHeight; } return null; } else { - this.elements.forEach( elem => { - if ( typeof val === 'function' ) { - val = val(); - } else { - if ( typeof val !== 'string' ) { - val += 'px'; + this.elements.forEach((elem) => { + if (typeof val === 'function') { + val = val(); + } else { + if (typeof val !== 'string') { + val += 'px'; + } + elem.style.height = val; } - elem.style.height = val; - } }); return this; } }; -Query.prototype.outerHeight = function ( val ) { - if ( typeof val === 'undefined' ) { +Query.prototype.outerHeight = function (val) { + if (typeof val === 'undefined') { return this.elements.length ? this.elements[0].offsetHeight : null; } else { - this.elements.forEach( elem => { - if ( typeof val === 'function' ) { - val = val(); - } else { - if ( typeof val !== 'string' ) { - val += 'px'; + this.elements.forEach((elem) => { + if (typeof val === 'function') { + val = val(); + } else { + if (typeof val !== 'string') { + val += 'px'; + } + elem.style.height = val; } - elem.style.height = val; - } }); return this; } }; - Query.prototype.top = function () { return this.elements.length ? this.elements[0].offsetTop : null; }; -Query.prototype.width = function( val ) { - if ( typeof val === 'undefined' ) { - if ( this.elements.length < 1 ) return this; +Query.prototype.width = function (val) { + if (typeof val === 'undefined') { + if (this.elements.length < 1) return this; const elem = this.elements[0]; - if ( elem.window === elem ) { - return document.defaultView.outerWidth; + if (elem.window === elem) { + return document.defaultView.outerWidth; } else { - return elem.getBoundingClientRect().width + return elem.getBoundingClientRect().width; } } else { if (typeof val === 'function') { val = val(); } else { - if ( this.elements.length < 1 ) return this; - if (typeof val !== 'string') val = val + 'px'; - this.elements.forEach( elem => { + if (this.elements.length < 1) return this; + if (typeof val !== 'string') val = val + 'px'; + this.elements.forEach((elem) => { elem.style.width = val; }); } } -} +}; -Query.prototype.outerWidth = function( val ) { - if ( this.elements.length < 1 ) return this; +Query.prototype.outerWidth = function (val) { + if (this.elements.length < 1) return this; const elem = this.elements[0]; - if ( val === true ) { - const style = getComputedStyle( elem ); - return elem.getBoundingClientRect().width + - parseFloat( style.marginLeft ) + parseFloat( style.marginRight ); + if (val === true) { + const style = getComputedStyle(elem); + return ( + elem.getBoundingClientRect().width + + parseFloat(style.marginLeft) + + parseFloat(style.marginRight) + ); } else { return elem.offsetWidth; } -} +}; -Query.prototype.scrollTop = function( val ) { - if ( typeof val === undefined ) { +Query.prototype.scrollTop = function (val) { + if (typeof val === 'undefined') { const elem = this.elements[0]; - if ( elem.window === elem ) { + if (elem.window === elem) { return document.defaultView.pageYOffset; } else { return elem.scrollTop; } } else { - this.elements.forEach( elem => { - if ( elem.window === elem ) { - const xOff = document.defaultView.pageXOffset; - document.defaultView.scrollTo( xOff, val ); + this.elements.forEach((elem) => { + if (elem.window === elem) { + const xOff = document.defaultView.pageXOffset; + document.defaultView.scrollTo(xOff, val); } else { elem.scrollTop = val; } }); } -} +}; -Query.prototype.offset = function( val ) { - if ( this.elements.length < 1 ) return this; +Query.prototype.offset = function () { + if (this.elements.length < 1) return this; const elem = this.elements[0]; const win = document.defaultView; - const bound = elem.getBoundingClientRect(); + const bound = elem.getBoundingClientRect(); const docTop = document.documentElement.clientTop; const docLeft = document.documentElement.clientLeft; return { top: bound.top + win.pageYOffset + docTop, - left: bound.left + win.pageXOffset + docLeft + left: bound.left + win.pageXOffset + docLeft, }; -} +}; -Query.prototype.css = function( param, value ) { - if ( this.elements.length < 1 ) return this; +Query.prototype.css = function (param, value) { + if (this.elements.length < 1) return this; let obj = {}; - if ( typeof param !== 'object' && typeof value === 'undefined' ) { - const styles = getComputedStyle( this.elements[0] ); + if (typeof param !== 'object' && typeof value === 'undefined') { + const styles = getComputedStyle(this.elements[0]); return styles[param]; - } else if ( typeof param !== 'object' ) { + } else if (typeof param !== 'object') { obj[param] = value; } else { obj = param; } - for ( let key in obj ) { - this.elements.forEach( elem => { - if ( elem.style.hasOwnProperty( key ) ) { + for (const key in obj) { + this.elements.forEach((elem) => { + if (Object.prototype.hasOwnProperty.call(elem.style, key)) { let val = obj[key]; - if ( pixelStyles.indexOf( key ) > -1 ) val = pixelator(val) ; + if (pixelStyles.indexOf(key) > -1) val = pixelator(val); elem.style[key] = val; } }); } return this; -} +}; /**************************/ // DOM manipulation @@ -610,17 +609,17 @@ Query.prototype.cloner = function () { return this.elements.length ? this.elements[0].cloneNode(true) : null; }; -Query.prototype.append = function( elem ) { - this.elements.forEach( parent => { - if ( typeof elem === 'string' ) { +Query.prototype.append = function (elem) { + this.elements.forEach((parent) => { + if (typeof elem === 'string') { parent.innerHTML += elem; } else { - parent.append( elem ); + parent.append(elem); } }); return this; -} +}; Query.prototype.appendTo = function (newParents) { // This method is designed only to work on instances of Query @@ -639,18 +638,18 @@ Query.prototype.appendTo = function (newParents) { return this; }; -Query.prototype.empty = function() { - this.elements.forEach( elem => { +Query.prototype.empty = function () { + this.elements.forEach((elem) => { elem.replaceChildren(); }); -} +}; // Constructor function const $ = function (param) { if (typeof param === 'string') { return new Query(param); } else if (typeof param === 'object') { - if ( param.nodeType ) { + if (param.nodeType) { const q = new Query(); q.elements = [param]; return q; @@ -659,16 +658,15 @@ const $ = function (param) { this.elements = param; return q; } - } + } }; -$.each = function( obj, callback) { - obj.forEach( function(elem, index) { +$.each = function (obj, callback) { + obj.forEach(function (elem, index) { // This is an odd reversal of parameters, but matches jQuery.each() - callback( index, elem ); - }, obj ); -} - + callback(index, elem); + }, obj); +}; export const window = '$WINDOW'; From be974eb51a7593a97ed370cfdeae585b54cbbcde Mon Sep 17 00:00:00 2001 From: Wyatt Pearsall Date: Mon, 21 Oct 2024 10:17:09 -0700 Subject: [PATCH 6/7] protect window access --- cfgov/unprocessed/js/modules/util/dollar-sign.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cfgov/unprocessed/js/modules/util/dollar-sign.js b/cfgov/unprocessed/js/modules/util/dollar-sign.js index 4ca111d51de..c997e3a40c1 100644 --- a/cfgov/unprocessed/js/modules/util/dollar-sign.js +++ b/cfgov/unprocessed/js/modules/util/dollar-sign.js @@ -548,10 +548,12 @@ Query.prototype.outerWidth = function (val) { Query.prototype.scrollTop = function (val) { if (typeof val === 'undefined') { const elem = this.elements[0]; - if (elem.window === elem) { - return document.defaultView.pageYOffset; - } else { - return elem.scrollTop; + if (elem) { + if (elem.window === elem) { + return document.defaultView.pageYOffset; + } else { + return elem.scrollTop; + } } } else { this.elements.forEach((elem) => { From 2669995eba3825ede7eb27dadb35860a89951895 Mon Sep 17 00:00:00 2001 From: Chuck Werner Date: Tue, 22 Oct 2024 11:48:07 -0400 Subject: [PATCH 7/7] - Before You Claim linting fixes - Before You Claim testing fixes --- .../js/retirement-before-you-claim-listeners.js | 2 +- .../css/college-costs/financial-item.scss | 2 +- .../apps/retirement/js/utils/is-element-in-view.js | 3 +-- .../apps/retirement/js/views/next-steps-view.js | 2 +- cfgov/unprocessed/js/modules/util/dollar-sign.js | 12 ++++++------ 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js b/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js index 2d5ac2b9855..000438211dd 100644 --- a/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js +++ b/cfgov/unprocessed/apps/analytics-gtm/js/retirement-before-you-claim-listeners.js @@ -162,7 +162,7 @@ import { analyticsSendEvent, analyticsLog } from '@cfpb/cfpb-analytics'; .querySelector('#retirement-age-selector') .addEventListener('change', function (event) { const target = event.currentTarget; - if ( target.selectedIndex > -1 ) { + if (target.selectedIndex > -1) { const val = target[target.selectedIndex].value; analyticsSendEvent({ event: 'Before You Claim Interaction', diff --git a/cfgov/unprocessed/apps/paying-for-college/css/college-costs/financial-item.scss b/cfgov/unprocessed/apps/paying-for-college/css/college-costs/financial-item.scss index 2578e682b5d..dbb96e39ff5 100644 --- a/cfgov/unprocessed/apps/paying-for-college/css/college-costs/financial-item.scss +++ b/cfgov/unprocessed/apps/paying-for-college/css/college-costs/financial-item.scss @@ -91,7 +91,7 @@ margin-left: math.div(10px, $base-font-size-px) + em; padding-left: math.div(10px, $base-font-size-px) + em; border-left: 1px solid var(--gray-20); - width: 3em; + width: 3em; } } diff --git a/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js b/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js index 86ae347dadb..5235507613d 100644 --- a/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js +++ b/cfgov/unprocessed/apps/retirement/js/utils/is-element-in-view.js @@ -1,5 +1,4 @@ -// TODO: remove jquery. -import $ from '../../../../js/modules/util/dollar-sign.js'; +import $, {window} from '../../../../js/modules/util/dollar-sign.js'; /** * This function determines if the element specified is currently in the diff --git a/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js b/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js index 2c7f76c8abf..9f97e3cda97 100644 --- a/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js +++ b/cfgov/unprocessed/apps/retirement/js/views/next-steps-view.js @@ -9,7 +9,7 @@ import $ from '../../../../js/modules/util/dollar-sign.js'; function init(ageRightNow = 0, fullRetirementAge = 0) { limitAgeSelector(ageRightNow); - $('.step-three').on( 'change', '#retirement-age-selector', function () { + $('.step-three').on('change', '#retirement-age-selector', function () { chooseClaimingAge(fullRetirementAge); }); } diff --git a/cfgov/unprocessed/js/modules/util/dollar-sign.js b/cfgov/unprocessed/js/modules/util/dollar-sign.js index c997e3a40c1..200466d24de 100644 --- a/cfgov/unprocessed/js/modules/util/dollar-sign.js +++ b/cfgov/unprocessed/js/modules/util/dollar-sign.js @@ -1,6 +1,6 @@ /** * @param {Document|Window|string} selector - A jQuery-style selector. - * @returns {Document|Window|undefined} The document, window, or nothing. + * @returns {Document|Window|undefined} The Query object. */ function Query(selector) { this.elements = []; @@ -55,9 +55,9 @@ function Query(selector) { /**************************/ /** - * - * @param {number|string} val - The value to make into stringified pixels - * @returns {string} - Value converted to stringifed pixels + * Turns a number into a string ending with 'px' + * @param {number} val - numeric value + * @returns {string} string ending with 'px' */ function pixelator(val) { if (typeof val === 'number') { @@ -284,10 +284,10 @@ Query.prototype.submit = function (callback) { /** * Handles events in eventType - * @param {string} eventType - the type of event + * @param {string} eventType - Type of event for which to listen * @param {string | Function} paramOne - A selector or callback * @param {Function} paramTwo - A callback if paramOne is a selector. - * @returns {object} - this + * @returns {object} this Query object */ Query.prototype.on = function (eventType, paramOne, paramTwo) { if (typeof paramOne == 'function') {