- Mark Minkin, CIR - Resource Recruiter Vendor Operations You must be able to communicate to a larger team exactly why a particular element is failing WCAG success criteria and provide examples of how to fix it. Don’t fix it for them but help your team understand how to identify the issue and fix it. Don’t simply make the change, be the agent of change.
- Scott C. Krause - A11y Agent of change Scott has absorbed the complexity of the Abbott Business Intelligence apparatus (Autosys, JBoss, Java, PowerCenter, Informatica MDM, Informatica Data Director, PL/SQL, SOA architecture, CRM). Scott prides himself on his ability to make complex topics approachable. He explains, simplifies and documents.
- Hubert Karst - Application Technology Manager Encouraging users to interact with data presented in a clearly-visual manner, a data-intensive story resonates as cognitively tangible. The real value of information is not found in its lineage or accuracy. Its true value is in its ability to convey a mental model that facilitates confident decision making. This of course is hard to measure, but I submit that you will know it when you see it. Pun intended.
- Scott C. Krause - Effective Data Storytelling It was a pleasure to work with Scott during many UI improvements Projects for www.ltdcommodities.com and www.lakeside.com. Scott always stood out for his problem solving skills and is a good listener. He also possess a great creative mind and is an awesome team player. He is an asset to any team and contributes towards the success of any Project.
- Ahmed Syed --CSPO & CSM Achieving accessibility nirvana is not easy for an eCommerce site. If you are uninspired, try to think of it in terms of exposing your business to a new group of shoppers who would have trouble shopping at your competitor’s site.
- Scott C. Krause - Accessibility It is with great pride that I announce Scott Krause’s promotion to Lead Systems Analyst. Scott has distinguished himself with his self-acquired Microsoft product expertise, a game plan both for professional development and project completion, a whatever-it-takes attitude to accomplishing his goals, and is being promoted two grades levels in accordance with the responsibilities he has accepted and perform within the last year. Scott’s leadership with Microsoft initiatives has been far-reaching for TSH, and his ability to envision the applications of their use within TSH products and services is creative and practical. Congratulations, Scott, on a well-deserved promotion.
- Anne T. Smyth - President The Systems House, Inc. Everyone appreciates software that is easy to use. I approach design from an user empathetic perspective. Understanding that Effortless is an achievement not an accident. I design user centric experiences and create high-fidelity prototypes allowing the user to interact with the app before it is developed. I can quickly test new designs and identify small flaws before they become big issues.
- Scott C. Krause - User centric design Scott has taken the forward looking initiative to master the official Salesforce certification curriculum. Cloud applications and particularly Salesforce are among the fastest growing technologies within ADD, within Abbott and within the life sciences / pharma industry. By embracing Salesforce Scott has proven again that he is an effective early adopter and can align his skill growth with the needs of Abbott Laboratories.
- Hubert Karst - Abbott Application Technology Manager The Progressive Web App represents the single greatest leap in engagement opportunity since the introduction of the browser. Because of its intimate relationship with the phone the potential to influence the micro-moment behavior of its user is vast and largely unexplored. When a Progressive Web App moves out of a tab and into a standalone app window, it transforms how users think about it and interact with it.
- Scott C. Krause - Emerging Technology Trends It is back to awesome! Unless you quickly say otherwise, the check will be in the mail. Thank you so much for your quick attention to all the adjustments and fixes. I suspect that I will be referring some others to you. Your work is good and your fees are fair and reasonable. Thanks
- Joel Mathews I believe that TypeScript represents the evolution of ECMAScript because its unobtrusive compile-time nature encourages code quality at scale. Meaning that a new project, complex in nature would benefit in maintainability over the life cycle of the software. A seasoned JS developer might do well to remove ego from this decision. It's easy to fall into the trap of thinking 'I don’t make type mistakes.', because you do.
- Scott C. Krause - Strong Type Stack Software as a Service is not always the fastest, cheapest, or most agile way to get things done. Renting a SaaS will cost you, not only in cash but in opportunity. We’ve been promised that if we move to the cloud we don’t have to share our office with programming nerds. How’s that working out for you? If you want to build truly differentiating solutions you still need to work with nerds, only now its cloud native nerds. Same difference but the tools that they use are owned by the cloud provider, not you. The day that you stop paying them YOUR code is rendered useless. You are an unwilling investor in the Big Tech monopolistic power dynamic.
- Scott C. Krause - SaaS Cycle of Dependency Scott- Many thanks for pulling this together. Great job writing it up as even for a non-developer like me, I was able to visualize from your description, what you want them to build. We’ve passed it along to them and will share with you the results to determine their prowess!
- Antonio C. - Marketing Director Scott’s ability to apply creative and innovative approaches to problems is well documented and thoroughly ingrained in his DNA. Scott’s forays into the unexplored have included: - User Centric Design / UX Prototyping - Inspire IT digital communications - Team Crowdsourcing - Java and PL/SQL User Exits - Release Candidate – Change Management - Informatica ETL / PCX - Salesforce / MDM API Integration feasibility
- Hubert Karst - Application Technology Manager I love interaction. I create websites that engage, excite, and inform in a way that is fun. Most of my day is spent enchanting websites with the little details that you will never find in a sea of boring template sites. It's important to present information in a way that tells a story. Like a mystery it should give up its secrets with a few inspired clicks.
- Scott C. Krause - Interactive Enchantment Thanks, Scott, for taking the time to fix our Access complaint database. We really appreciate your efforts.
- Marie Rasico 7/8/10 - Abbott Laboratories Compliance Tenacity: A driving need to bring order to chaos and build a solution that solves problems yet unimagined. The ability to break down complex systems into simple sub-systems. To keep in one’s memories’ scratch pad the interdependencies then derive a hypothesis explaining a problem, based on applied experience, critical thinking, and an unbiased scientific method.
- Scott C. Krause - What makes a great programmer? The stated business objected is to acquire new omni-channel touch points without cannibalizing existing customer engagement touch points. Meaning the IA should present the post check-out solicitations in a manor in which SMS sign-ups DO NOT negatively impact email sign-ups. I believe the goal has been achieved and the analytics support that conclusion.
- Scott C. Krause - UX Customer Journey Emerging tech that has potential to change the attention economy dynamic. These touch-points are available now, and can make your web properties anti-bounce or at least bounce resistant. Think, animation, video, web audio, PWA, push, value add Chrome extensions, 3D, interactive infographics, and data visualizations.
- Scott C. Krause - Emerging Marketing Tech A Business Accelerator is a targeted, authoritative, and conversion optimized landing page with automated onboarding processes that facilitates a non-linear, multi-touch sales cycle.
- Scott C. Krause - Bounce-proof Web Experience Understand that filling out a form is often the end of a long user journey. It may be the onboarding call-to-action that converts a visitor to a customer. It may be the result of tens of thousands of dollars invested in targeted omni-channel marketing. It would be a catastrophic waste to lose a conversion simply because the visitor became frustrated while filling out the form. If this form is submitted successfully then you have a new customer. If it is not successful then you do not have a new customer. It’s a high stakes endeavor.
- Scott C. Krause - JavaScript Validation Data Integrity
![Flickity Carousel A11y Observer](/patterns/javascript_tn.webp)
Flickity Carousel A11y Observer
Observe and listen for changes in the Flickity carousel
2023-10-08
![Javascript Tiny Type](/patterns/javascript_tn.webp)
Javascript Tiny Type
Replace text with a super small character set.
// Replace text with a super small character set.
/*
_______ _ _______ _
|__ __(_) |__ __| | |
| | _ _ __ _ _ | | _____ _| |_
| | | | '_ \| | | | | |/ _ \ \/ / __|
| | | | | | | |_| | | | __/> <| |_
|_| |_|_| |_|\__, | |_|\___/_/\_\\__|
__/ |
|___/ 🗿 🪐 🔨
*/
let aTiny = {"a":"ᵃ","b":"ᵇ","c":"ᶜ","d":"ᵈ","e":"ᵉ","f":"ᶠ","g":"ᵍ","h":"ʰ","i":"ᶦ","j":"ʲ","k":"ᵏ","l":"ᶫ","m":"ᵐ","n":"ᶰ","o":"ᵒ","p":"ᵖ","q":"ᑫ","r":"ʳ","s":"ˢ","t":"ᵗ","u":"ᵘ","v":"ᵛ","w":"ʷ","x":"ˣ","y":"ʸ","z":"ᶻ","A":"ᴬ","B":"ᴮ","C":"ᶜ","D":"ᴰ","E":"ᴱ","F":"ᶠ","G":"ᴳ","H":"ᴴ","I":"ᴵ","J":"ᴶ","K":"ᴷ","L":"ᴸ","M":"ᴹ","N":"ᴺ","O":"ᴼ","P":"ᴾ","Q":"ᑫ","R":"ᴿ","S":"ˢ","T":"ᵀ","U":"ᵁ","V":"ⱽ","W":"ᵂ","X":"ˣ","Y":"ʸ","Z":"ᶻ","`":"`","~":"~","!":"﹗","@":"@","#":"#","$":"﹩","%":"﹪","^":"^","&":"﹠","*":"﹡","(":"⁽",")":"⁾","_":"⁻","-":"⁻","=":"⁼","+":"+","{":"{","[":"[","}":"}","]":"]",":":"﹕",";":"﹔","?":"﹖"};
let doTinyCaption = ( (_d, _q, _t) => { // Inject Tiny type
let aTinyCnt = [..._d.querySelectorAll( _q )];
if( aTinyCnt ){ setTimeout( ()=>{ doTinyCaption.tick(); }, 32); }
return {
"tick": ()=>{
let sMU = "";
aTinyCnt.forEach( (eVivCnt) => {
if(eVivCnt.atTiny !== eVivCnt.dataset.atTiny){ // Data atr changed
Array.from( eVivCnt.dataset.atTiny ).filter(( sChr )=>{
sMU += ( sChr == " ") ? " " : aTiny[ sChr ];
});
eVivCnt.innerHTML = sMU;
eVivCnt.atTiny = eVivCnt.dataset.atTiny;
}
} );
setTimeout( ()=>{ doTinyCaption.tick(); }, _t);
}
};
})(document, "[data-at-tiny]", 13664 );
2023-08-20
![Javascript GA4 Intersection Observer](/patterns/javascript_tn.webp)
Javascript GA4 Intersection Observer
Heatmap: Track content visibility time in Google Analytics or Adobe Analytics
// Track content visibility time with Intersection Observer in Adobe Analytics or Google Analytics
/*
/\ /\___ __ _| |_ _ __ ___ __ _ _ __
/ /_/ / _ \/ _` | __| '_ ` _ \ / _` | '_ \
/ __ / __/ (_| | |_| | | | | | (_| | |_) |
\/ /_/ \___|\__,_|\__|_| |_| |_|\__,_| .__/
|_| 🌶️ 🔥
*/
class SyHeatmap { // Neodigm 55 Heatmap Begin
static oObserved = {}; static aObservedEl = []; static aQryContext = []
static oIntObserver = null; static NTHRESH_SECS = 3; static bIsInit = false;
static reInit ( _q, _c = document ){ // DOM bind to context element
if( _q && _c ){
this.aQryContext = [ _q, _c ]
this.oObserved = {};
this.aObservedEl = [ ... _c.querySelectorAll( _q[ 0 ] ) ];
this.aObservedEl.forEach( ( elO )=>{
let elOsib = elO.nextElementSibling
const sCap = elOsib.heatmapCaption = elO.innerHTML
this.oObserved[ sCap ] = elOsib
this.oObserved[ sCap ].heatmapTime = []
} )
this.oIntObserver = new IntersectionObserver( ( entries )=>{
entries.forEach( ( oEnt )=>{
if( oEnt.target?.heatmapCaption ){
const sCap = oEnt.target.heatmapCaption
if( this.oObserved[ sCap ].heatmapTime.length ){
this.oObserved[ sCap ].heatmapTime.push( {"state": oEnt.isIntersecting, "ts": new Date().getTime() })
}else{ // No first time false (vis when page loads)
if( oEnt.isIntersecting ){
this.oObserved[ sCap ].heatmapTime.push( {"state": oEnt.isIntersecting, "ts": new Date().getTime() })
}
}
if( oEnt.isIntersecting ){
oEnt.heatmapTotal = SyHeatmap.totalHeatmapTime( this.oObserved[ sCap ].heatmapTime ); // Sum and dif array vals
console.log( " ~~~ tot | " + oEnt.target.heatmapCaption + " | " + oEnt.heatmapTotal )
}
}
} )
} )
//SyHeatmap.resetHeatMap()
this.aObservedEl.forEach( ( elObs )=>{
let sCap = this.oObserved[ elObs?.innerHTML ]
if( sCap ) this.oIntObserver.observe( sCap )
} )
if( !this.bIsInit ){
this.bIsInit = true;
setInterval( ()=>{ SyHeatmap.tick() }, 3e3 )
}
return this;
}
}
static totalHeatmapTime ( aHeatmapTime ){ // Return total time on component in secs
let nTotStart = 0; let nTotEnd = 0; // Note: IntrSec Observ will fire FALSE once upon page load for each entry not visible
if( aHeatmapTime.length ){ // Append a FALSE as NOW if the last item is not FALSE (currently in viewport)
let aDTO = [ ... aHeatmapTime ]
if( aDTO[ aDTO.length - 1 ].state == true ) aDTO.push( {"state": false, "ts": new Date().getTime() } )
aDTO.forEach( ( oHMTimes )=>{
if( oHMTimes.state ) nTotStart = nTotStart + oHMTimes.ts
if( !oHMTimes.state ) nTotEnd = nTotEnd + oHMTimes.ts
} )
}
return ( nTotEnd - nTotStart ) / 1000; // in seconds
}
static genHeatmap ( nThresh = this.NTHRESH_SECS ){ // Return a simple arry of current hm usage filt threshold
let aCurHM = []
if( this.aObservedEl.length ){
for ( const sCap in this.oObserved ) {
let nTotal = SyHeatmap.totalHeatmapTime( this.oObserved[ sCap ].heatmapTime )
if( nTotal && ( nTotal >= nThresh ) ) aCurHM.push( {"caption": sCap, "secs": nTotal } )
}
}
return aCurHM;
}
static resetHeatMap(){
this.aObservedEl.forEach( ( elObs )=>{
let sCap = this.oObserved[ elObs?.innerHTML ]
if( sCap ) this.oIntObserver.unobserve( sCap )
} )
}
static appendDataLayer (){ // Iterate filtered heatmap and add to DL - return count
let iCnt = 0
if( window.dataLayer ) {
SyHeatmap.genHeatmap().forEach( ( oHMSum )=>{
let sMsg = oHMSum.caption + " | " + oHMSum.secs + " | " + Neodigm 55.salesforceGlobal.franchiseconfig.Name + " | " + Neodigm 55.salesforceGlobal.loginuser.UserRole.Name
window.dataLayer.push( { "event": "Neodigm 55_heatmap", "msg": sMsg, "hm_secs": oHMSum.secs, "hm_fran": Neodigm 55.salesforceGlobal.franchiseconfig.Name, "hm_role": Neodigm 55.salesforceGlobal.loginuser.UserRole.Name } )
iCnt++;
})
}
SyHeatmap.resetHeatMap() // Reset and Rebind
SyHeatmap.reInit( this.aQryContext[ 0 ], this.aQryContext[ 1 ])
return iCnt;
}
static tick (){
if( this.aQryContext.length ){ // Must have already been fired
let elSame = this.aQryContext[ 1 ].querySelector( this.aQryContext[ 0 ] );
if( elSame ){
if( elSame.innerHTML != this.aObservedEl[0]?.innerHTML ){ SyHeatmap.appendDataLayer() }
}else{ SyHeatmap.appendDataLayer() }
}
}
} // Neodigm 55 Heatmap End
document.addEventListener("DOMContentLoaded", (ev)=>{
setTimeout( ()=>{
SyHeatmap.reInit( [ "DIV>H2" ], document.querySelector("#app > div.v-application--wrap > div.container.Neodigm 55-main-container.pa-0.ma-0.pt-0 > div > div") )
}, 3e3 )
})
The Neodigm 55 Heatmap component captures the amount of time that each card is visible to an end user on a laptop or mobile device.
The summation of card activity is then packaged into the analytics data layer to be consumed by Adobe Analytics or Google Analytics. Activity that occurs while offline will be updated upon reconnection to the network if the app has not been closed.
The Neodigm 55 Heatmap component utilized the Intersection Observer pattern to track when and for how long block elements (cards) are visible in the user agent viewport.
The component is configured to only report if a card is visible for greater than 3 seconds. This threshold is configurable. There are some edge cases wherein data may lose fidelity. For example if the user opens another tab or abruptly closes the browser while a card is within the viewport.
The component captures the existence of cards within the browser's viewport, however the application may partially obstruct the viewport with an overlaying menu. Meaning that the Heatmap may report that a particular card is in view a few microseconds before it is actually visible. This discrepancy is so small that it is not statistically significant.
The data layer entries that the heatmaps create are additive, meaning that there may be more than one for a single component. This is because the end user viewed a particular card component, exited the card, then reentered the card.
2023-08-14
![Get all products from Any Shopify Site](/patterns/javascript_tn.webp)
Get all products from Any Shopify Site
Fetch all Products and Images from any Shopify site.
// Fetch all Products and Images from any Shopify site.
/* _____ __ __ ___ ____ ____ _____ __ __
/ ___/| | | / \ | \ || || | |
( \_ | | || || o ) | | __|| | |
\__ || _ || O || _/| | | |_ | ~ |
/ \ || | || || | | | | _] |___, |
\ || | || || | | | | | | |
\___||__|__| \___/ |__| |____||__| |____/ 😎 */
var aP = [];
const neodigmMU = `
<textarea id="elT" rows=8></textarea>`; // Universal Templs
let eMU = document.createElement("textarea");
document.body.appendChild(eMU);
eMU.outerHTML = neodigmMU;
(function getProducts(url = 'https://SHOPIFY-SITE.com/admin/api/2019-07/products.json?limit=250&fields=id,images') {
fetch(url).then(res => {
const headerLink = res.headers.get('link');
const match = headerLink?.match(/<[^;]+\/(\w+\.json[^;]+)>;\srel="next"/);
const url = match ? match[1] : false;
if(url){
res.json().then((data) => {
data.products.map((el) => {
//console.log( JSON.stringify( el ) )
aP.push( JSON.stringify( el ) )
})
})
getProducts(url)
} else {
res.json().then((data) => {
data.products.map((el) => {
//console.log( JSON.stringify( el ) )
aP.push( JSON.stringify( el ) )
})
doTextArea()
})
}
})
})()
function doTextArea(){
let elTA = document.getElementById("elT")
aP.forEach( (p) =>{
// console.log( " ~~~ | " + p );
elTA.textContent = elTA.textContent + p + "\n" }
)
}
2023-08-10
![JS Airport Geo-Proximity Radius](/patterns/javascript_tn.webp)
JS Airport Geo-Proximity Radius
Airport geo-proximity logic that answers questions, like What are the three closest airports to me right now?
// Desc: Get the closest airports by geolocation radius
// Usage: closestAirports.find(-99, -99, oAirports, 4); // 4 miles
/* ___ _ _ _
* / _ \___ ___ | | ___ ___ __ _| |_(_) ___ _ __
* / /_\/ _ \/ _ \| |/ _ \ / __/ _` | __| |/ _ \| '_ \
* / /_\\ __/ (_) | | (_) | (_| (_| | |_| | (_) | | | |
* \____/\___|\___/|_|\___/ \___\__,_|\__|_|\___/|_| |_| ✈️ */
function getDistance(lat1, lon1, lat2, lon2) {
let radlat1 = Math.PI * lat1/180;
let radlat2 = Math.PI * lat2/180;
let theta = lon1-lon2;
let radtheta = Math.PI * theta/180;
let dst = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
if (dst > 1) dst = 1;
dst = Math.acos(dst);
dst = dst * (180/Math.PI) * (60 * 1.1515); // miles
return dst;
}
let closestAirports = ((_d) => {
return {
"find": function(nLat, nLon, objPorts, nRadius){
if(nLat && nLon && objPorts){
let arrPorts = [];
for (let prop in objPorts) { // Sort Object
if (objPorts.hasOwnProperty(prop)) {
let lat = objPorts[prop].geoCode.split(",")[0];
let lon = objPorts[prop].geoCode.split(",")[1];
arrPorts.push({
'key': prop, 'lat': lat, 'lon': lon,
"dist": getDistance(lat, lon, nLat, nLon),
"formattedAirport": objPorts[prop].formattedAirport
});
}
}
arrPorts.sort(function(a, b){
// Sort by Distance
return a.dist - b.dist;
});
return arrPorts.filter(function(aP){
return (aP.dist <= nRadius);
});
}
}
};
})(document);
2022-12-13
![Calculate Aspect Ratio of Viewport](/patterns/javascript_tn.webp)
Calculate Aspect Ratio of Viewport
Calculate Aspect Ratio of Viewport
// Desc: Calculate Aspect Ratio of Viewport
// Usage: Console log getDims() onresize event of body
/* _ _____ _ _
/\ | | | __ \ | | (_)
/ \ ___ _ __ ___ ___| |_ | |__) |__ _| |_ _ ___
/ /\ \ / __| '_ \ / _ \/ __| __| | _ // _` | __| |/ _ \
/ ____ \\__ \ |_) | __/ (__| |_ | | \ \ (_| | |_| | (_) |
/_/ \_\___/ .__/ \___|\___|\__| |_| \_\__,_|\__|_|\___/
| |
|_| 🎯 */
const gcd = (a, b) => {
return b
? gcd(b, a % b)
: a;
};
const aspectRatio = (width, height) => {
const divisor = gcd(width, height);
return `${width / divisor}:${height / divisor}`;
};
const getDims = function(){
if(window.innerWidth !== undefined && window.innerHeight !== undefined) {
var w = Number( window.innerWidth )
var h = Number( window.innerHeight )
var a = aspectRatio( w, h )
} else {
var w = Number( document.documentElement.clientWidth )
var h = Number( document.documentElement.clientHeight )
var a = aspectRatio( w, h )
}
return {"ratio": a, "h": h, "w": w};
}
2021-04-16
![Javascript Generate and Download CSV](/patterns/javascript_tn.webp)
Javascript Generate and Download CSV
Produce CSV with client-side JS. Construct Blog and Download as CSV file.
// Desc: Produce CSV with client-side JS. Contruct Blob and Download as CSV file
/* _________ _____________ ____ __________.__ ___.
* \_ ___ \ / _____/\ \ / / \______ \ | ____\_ |__
* / \ \/ \_____ \ \ Y / | | _/ | / _ \| __ \
* \ \____/ \ \ / | | \ |_( <_> ) \_\ \
* \______ /_______ / \___/ |______ /____/\____/|___ /
* \/ \/ \/ \/ CSV Report */
✅ The resulting CSV files will contain a header row deterministic column names
✅ The resulting CSV files will be quoted
✅ The file name is auto-generated timestamp
✅ Cell string data may contain a comma “,” however quotes will be removed
✅ Cell string data may contain only utf-8 characters
let nativeCSV = ( ( _d )=>{
let oCnt, jnCSV, sCSV, blCSV, elCSV; // config, json, array, blob, and element
let retObj = {
"init": ( _oCnt )=>{
oCnt = _oCnt;
if( oCnt.fileName.indexOf("####") !== -1) {
oCnt.fileName = oCnt.fileName.replace("####", Date.now() );}
jnCSV = sCSV = blCSV = elCSV = "";
return retObj;
},
"setArray": ( _jnCSV )=>{ // An array (rows) of arrays (cols) !jagged
jnCSV = _jnCSV;
if( oCnt.header ) jnCSV.unshift( oCnt.header );
jnCSV.forEach(( aRow )=>{
aRow.forEach(( sCol )=>{
if( typeof sCol === "string"){
sCSV += oCnt.delimQuote + sCol
.split( oCnt.delimQuote ).join("");
sCSV += oCnt.delimQuote + oCnt.delimCol;
}
});
sCSV = sCSV.slice(0, -1) + oCnt.delimLine;
});
return retObj;
},
"getBlob": ()=>{
blCSV = new Blob([ sCSV ], { type: "text/csv;charset=utf-8;" });
return retObj;
},
"createLink": ()=>{
elCSV = _d.createElement("a");
elCSV.setAttribute("href", URL.createObjectURL( blCSV ));
elCSV.setAttribute("download", oCnt.fileName );
elCSV.style.visibility = 'hidden';
_d.body.appendChild( elCSV );
return retObj;
},
"clickLink": ()=>{
elCSV.click();
return retObj;
},
"removeLink": ()=>{
_d.body.removeChild( elCSV );
return retObj;
}
};
return retObj;
})( document );
console.log( nativeCSV.init({ // Usage:
"delimCol": ",",
"delimQuote": '"',
"delimLine": "\n",
"fileName": "graph_nodes_####.csv",
"header": ["id","name", "FQDN"]})
.setArray( currentGraph2Array(jCurrentGraph) )
.getBlob()
.createLink()
.clickLink()
.removeLink()
);
2021-02-27
![PWA Add to Home Screen](/patterns/pwa_tn.webp)
PWA Add to Home Screen
Progressive Web App ⚡ Advanced Cache && Notification Patterns
/* ______ __ __ ______
/\ == \ /\ \ _ \ \ /\ __ \
\ \ _-/ \ \ \/ ".\ \ \ \ __ \
\ \_\ \ \__/".~\_\ \ \_\ \_\
\/_/ \/_/ \/_/ \/_/\/_/ ✨ Add to Home Screen
chrome://serviceworker-internals/
*/
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("sw.js");
});
}
let eA2hs = oD.getElementsByClassName("js-a2hs")[0];
let eA2hsP = oD.getElementsByClassName("js-a2hs--post")[0];
eA2hs.addEventListener("click", (e) => {
eA2hs.style.display = "none";
eA2hsP.style.display = "block";
evDefPrompt.prompt();
evDefPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === "accepted") {
if( snck ) neodigmToast.q("Wow, Now I'm an App on your Desktop|How Convenient!");
playAudioFile( 7 ); // ggl tag event | User accepted the A2HS prompt
} else {
playAudioFile( 3 ); // ggl tag event | User dismissed the A2HS prompt
}
evDefPrompt = null;
});
});
function displayMsg( sMsg ){
// System Tray Notification
if (!("Notification" in window)) {
console.log('Notification API not supported.');
return;
} else if (Notification.permission === "granted") {
// If it's okay let's create a notification
var notification = new Notification( Nowish(), {icon: "https://repository-images.githubusercontent.com/178555357/2b6ad880-7aa0-11ea-8dde-63e70187e3e9", body: sMsg} );
} else if (Notification.permission !== "denied") {
// Otherwise, we need to ask the user for permission
Notification.requestPermission(function (permission) {
// If the user accepts, let's create a notification
if (permission === "granted") {
var notification = new Notification( Nowish(), {icon: "https://repository-images.githubusercontent.com/178555357/2b6ad880-7aa0-11ea-8dde-63e70187e3e9", body: sMsg} );
}
});
}
}
/* ╔═╗┌─┐┬─┐┬ ┬┬┌─┐┌─┐
* ╚═╗├┤ ├┬┘└┐┌┘││ ├┤
* ╚═╝└─┘┴└─ └┘ ┴└─┘└─┘
* ╦ ╦┌─┐┬─┐┬┌─┌─┐┬─┐
* ║║║│ │├┬┘├┴┐├┤ ├┬┘
* ╚╩╝└─┘┴└─┴ ┴└─┘┴└─ Advanced Cache ⚡ Notifications
*/
importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.0.0/workbox-sw.js');
workbox.LOG_LEVEL = "debug";
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request);
})
);
});
workbox.routing.registerRoute(
// Cache CSS files
/.*\.css/,
// Use cache but update in the background ASAP
workbox.strategies.staleWhileRevalidate({
cacheName: 'css-cache',
})
);
workbox.routing.registerRoute(
// Cache image files
/\.(?:png|gif|jpg|jpeg|webp|avif|svg|mp3|mp4|json|html|js)$/,
// Use the cache if it's available
workbox.strategies.cacheFirst({
cacheName: 'image-cache',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 256, maxAgeSeconds: 172800,
})
],
})
);
class NeodigmPWA {
constructor(){
}
init () {
window.addEventListener('appinstalled', () => {
setTimeout(function(){
neodigmToast.q("##Application Installed|Neodigm UX ✨ Scott C. Krause")
neodigmWired4Sound.play( 8 )
if( dataLayer ) dataLayer.push({'event': 'appinstalled'})
}, 1200)
});
}
}
let neodigmPWA = new NeodigmPWA()
neodigmPWA.init()
2020-12-21
![HTML data attrib to JavaScript camel-case dataset](/patterns/javascript_tn.webp)
HTML data attrib to JavaScript camel-case dataset
Convert an HTML formatted data attrib name to a JS formatted name.
// Desc: data-is-whatever will be converted to isWhatever
// Usage: element.dataset[ data2prop("data-is-whatever") ]
/*______ _____ __ __ _____ _ _
| ____/ ____| \/ | /\ / ____| (_) | |
| |__ | | | \ / | / \ | (___ ___ _ __ _ _ __ | |_
| __|| | | |\/| | / /\ \ \___ \ / __| '__| | '_ \| __|
| |___| |____| | | |/ ____ \ ____) | (__| | | | |_) | |_
|______\_____|_| |_/_/ \_\_____/ \___|_| |_| .__/ \__|
| |
|_| ES2021*/
function data2prop( sDset ){ // Convert HTML data attrib name to JS dataset name
sDset = sDset.replace("data-", "").toLowerCase();
let aDset = sDset.split(""), aDret = [], bUpper = false;
aDset.forEach( ( sChar ) => {
if( sChar == "-" ){
bUpper = true;
}else{
aDret.push( ( bUpper ) ? sChar.toUpperCase() : sChar );
bUpper = false;
}
});
return aDret.join("");
}
2020-12-19
![Oracle PL/SQL Stored Procedure](/patterns/sql_tn.webp)
Oracle PL/SQL Stored Procedure
Vintage Stored Procedure to denormalize department codes
-- ███████ ██████ ██
-- ██ ██ ██ ██
-- ███████ ██ ██ ██
-- ██ ██ ▄▄ ██ ██
-- ███████ ██████ ███████ Relational ⚡ Transactional
-- ▀▀
PROCEDURE post_stage
(
in_rowid_job cmxlb.cmx_rowid,
in_ldg_table_name cmxlb.cmx_table_name,
in_stg_table_name cmxlb.cmx_table_name,
out_error_msg OUT cmxlb.cmx_message,
out_return_code OUT int
)
AS
sql_stmt varchar2(2000);
t_party_acct_id varchar2(14);
t_txn_div_cd varchar2(20);
t_txn_div_display varchar2(50);
commit_count NUMBER := 0;
commit_inc NUMBER := 1000;
--
CURSOR C_PTAC_TXN IS
SELECT PARTY_ACCT_ID, TXN_DIV_CD, TXN_DIV_DISPLAY
FROM C_STG_PTAC_TXN_DIV;
--
BEGIN
--
commit_inc := to_number(GET_PARAMETER('post_stage_commit', commit_inc));
IF in_ldg_table_name = 'C_LDG_PTAC_TXN_DIV' AND in_stg_table_name = 'C_STG_PTAC_TXN_DIV' THEN
-- 20130225 SCK Update the stage txn_div_display col with a denormalized string derived
-- from an aggregate of both staging and base object.
-- 🏄 SQL ⚡ ETL MDM ⚡ PL/SQL ORM
cmxlog.debug ('ADDUE: Landing table name is ' || in_ldg_table_name || ' Staging table name is ' || in_stg_table_name);
BEGIN
FOR R_PTAC_TXN in C_PTAC_TXN LOOP
post_stage_concat(R_PTAC_TXN.PARTY_ACCT_ID, t_txn_div_display);
UPDATE C_STG_PTAC_TXN_DIV
SET txn_div_display = t_txn_div_display, create_date = sysdate WHERE TXN_DIV_CD = R_PTAC_TXN.TXN_DIV_CD AND
PARTY_ACCT_ID = R_PTAC_TXN.PARTY_ACCT_ID; -- CURRENT OF C_PTAC_TXN;
commit_count := commit_count + commit_inc;
IF MOD(commit_count, 1000) = 0 THEN
cmxlog.debug ('ADDUE: post_stage_concat is: ' || commit_count || ':' || R_PTAC_TXN.PARTY_ACCT_ID || ' : ' || t_txn_div_display);
COMMIT;
END IF;
END LOOP;
COMMIT;
END;
ELSE
CMXlog.debug ('ADDUE Post Stage - no action taken');
END IF;
END post_stage;
END ADD_UE;
2020-12-19
![Dark Mode and Reduced Motion](/patterns/javascript_tn.webp)
Dark Mode and Reduced Motion
Making Dark Mode work with both a UI switch && the OS preference.
// Desc: Listen to the OS for user preference
// but override with a UI toggle.
/* ______ __ ____ ____ __
|_ _ `. [ | _ |_ \ / _| | ]
| | `. \ ,--. _ .--. | | / ] | \/ | .--. .--.| | .---.
| | | |`'_\ : [ `/'`\]| '' < | |\ /| | / .'`\ \/ /'`\' |/ /__\\
_| |_.' /// | |, | | | |`\ \ _| |_\/_| |_| \__. || \__/ || \__.,
|______.' \'-;__/[___] [__| \_] |_____||_____|'.__.' '.__.;__]'.__.' User Prefs */
let doPrefersReducedMotion = function( bMotion ){// Stop 3D rotation
o3Config.controls.autoRotate = !bMotion;
}
let doPrefersColorScheme = function( bScheme ){ // UI | OS Semaphore
document.body.dataset.n55AmpmTheme = ((bScheme) ? "dark" : "light"); // 🌙 / ☀️
}
// Capture the prefers media queries
const mqPrefReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
const mqPrefColorScheme = window.matchMedia("(prefers-color-scheme: dark)");
doPrefersReducedMotion( (mqPrefReducedMotion && mqPrefReducedMotion.matches) );
doPrefersColorScheme( (mqPrefColorScheme && mqPrefColorScheme.matches) );
// listen to changes in the media query's value
mqPrefReducedMotion.addEventListener("change", () => {
doPrefersReducedMotion( mqPrefReducedMotion.matches );
});
mqPrefColorScheme.addEventListener("change", () => {
doPrefersColorScheme( mqPrefColorScheme.matches );
});
/* Dark Mode begin */
/*@media (prefers-color-scheme: dark) {*/
body[data-n55-ampm-theme='dark'] [role='main'] {
background: linear-gradient(to right, #5A5852, #c2c2c2, #5A5852)
}
body[data-n55-ampm-theme='dark'] .h-bg__stripe, body[data-n55-ampm-theme='dark'] .l-caro-design > article, body[data-n55-ampm-theme='dark'] article.l-caro-design {
background: repeating-linear-gradient(45deg,#242424,#242424 24px,#444 24px,#444 48px);
}
body[data-n55-ampm-theme='dark'] section.pfmf-grid > div > article {
border: solid 1px #888;
border-top: solid 2px #888;
box-shadow: 0px 2px 6px -2px rgba(164,164,164,0.6);
background-color: #242424;
}
body[data-n55-ampm-theme='dark'] .readable__doc { color: #fff; }
body[data-n55-ampm-theme='dark'] .readable__caption { color: #fff; }
body[data-n55-ampm-theme='dark'] .h-vect-line-art { stroke: #fff;}
/*}*/
/* Dark Mode end */
2020-12-19
![Vanilla JS Popover Microinteraction](/patterns/javascript_tn.webp)
Vanilla JS Popover Microinteraction
A popover is a transient view that shows on a content screen when a user clicks on a control button or within a defined area.
// A popover is a transient view that shows on a content screen when
// a user clicks on a control button or within a defined area.
/* __ __ __ __ __ __ __
/\ \/\ \ /\ \ /\ \/\ \ /\_\_\_\
\ \ \_\ \ \ \ \ \ \ \_\ \ \/_/\_\/_
\ \_____\ \ \_\ \ \_____\ /\_\/\_\
\/_____/ \/_/ \/_____/ \/_/\/_/ */
let oPopOver = ( ( _win, _doc, _qry ) => {
let arPops = [], ePos, iOffTop=0, iOffLft=0;
return { // Popover UX pattern
"init": function(){ // wire DOM events
arPops= [].slice.call(_doc.querySelectorAll( _qry ));
_win.addEventListener("resize", oPopOver.closeAll);
_win.addEventListener("scroll", oPopOver.closeAll);
_doc.body.addEventListener("click", function( e ){ // 👁️ Outside Click
let eTarget = e.target, bInside = false;
while( eTarget.tagName !== "HTML" ){
if( eTarget.dataset.popover ){ bInside = true; break; }
eTarget = eTarget.parentNode;
}
if( !bInside ){ // Tapped Outside of Popover
oPopOver.closeAll();
}
}, true);
},
"open": function(id, evPos){ // Open a single Popover
if( arPops.length == 0) return false;
oPopOver.closeAll();
ePos = evPos.currentTarget;
let elPop = arPops.filter(function(el){
return ( el.id == id );
})[0];
iOffTop = Number(elPop.dataset.popoverPos.split("|")[0]);
iOffLft = Number(elPop.dataset.popoverPos.split("|")[1]);
elPop.dataset.popover = "true"; // Open and Active
elPop.style.left = oPopOver.position().left+"px";
elPop.style.top = oPopOver.position().top+"px";
},
"closeAll": function(){ // Close all Popovers
if( arPops.length == 0) return false;
arPops.map(function(el){
el.dataset.popover = "false";
});
},
"position": function(){ // Determine Popover position
let rec = ePos.getBoundingClientRect(),
pxLft = _win.pageXOffset || _doc.documentElement.scrollLeft,
pxTop = _win.pageYOffset || _doc.documentElement.scrollTop;
return { top: (rec.top + pxTop + iOffTop), left: (rec.left + pxLft + iOffLft) }
}
}
})(window, document, "[data-popover]"); // Declarative implementation
2020-12-16
![Vue.js double tap Microinteraction](/patterns/vue_tn.webp)
Vue.js double tap Microinteraction
Firing both a tap and a double-tap on the same element
// A Vue.js snippet that shows how to capture both a tap and
// a double-tap on the same element within the template.
//
// Canonical Use Case: Double-Tap to zoom into a hero image
// and single-tap to zoom out.
/* ____ ____ __
* \ \ / /_ __ ____ |__| ______
* \ Y / | \_/ __ \ | |/ ___/
* \ /| | /\ ___/ | |\___ \
* \___/ |____/ \___ > /\ /\__| /____ >
* \/ \/ \______| \/ */
methods: {
"doHeroMobMouseUp": function( ev ){ // Double Tap
var oHro = this.oHeroZmMob;
if( oHro.isInit ){
if( oHro.doubleTap ){ // Zoom In
oHro.doubleTap = false;
this.doHeroMobScale( .5 ); // Double Tap
}else{
oHro.doubleTap = true;
setTimeout(function(){ this.doHeroMobMouseUp_expire() }, 380);
}
}
this.oHeroZmMob.isDown = false;
},
"doHeroMobMouseUp_expire": function(){ // Single Tap
var oHro = this.oHeroZmMob;
if( oHro.isInit ){ // Zoom Out
if( oHro.doubleTap ) this.doHeroMobScale( -.5 ); // Single Tap
oHro.doubleTap = false;
}
}
}
/*
This is only part of a larger Vue gesture implementation supporting
Pinch 🤏, Zoom, Pan, and Swipe. Reach out to me if you want to learn more.
*/
2020-12-15
![CSS Advanced Accessibility](/patterns/css_tn.webp)
CSS Advanced Accessibility
Motion, theme, and skip A11Y CSS solutions
/* Skip to Main Content - CSS Focus rules that make the
link visible when focused from the omnibox.
========================================
==== ======== ======== ===== ==== =
=== ===== ====== ===== == =
== == ====== ======== ====== == ==
= ==== ===== ======== ====== == ==
= ==== ===== ======== ======= ===
= ===== ======== ======== ====
= ==== ===== ======== ======== ====
= ==== ===== ======== ======== ====
= ==== === ==== ====== ====
======================================== */
a.skip__main:active, a.skip__main:focus {
background-color: #fff;
border-radius: 4px;
border: 2px solid #000;
color: #000;
font-size: 1em;
height: auto; width: 16%;
left: auto;
margin: 8px 42%;
overflow: auto;
padding: 4px;
text-align: center;
top: auto;
z-index: 1024;
}
a.skip__main {
left: -1024px;
overflow: hidden;
position: absolute;
top: auto;
width: 1px; height: 1px;
z-index: -1024;
}
/* Dark Mode begin */
@media (prefers-color-scheme: dark) {
body, [role='main'] {
background: linear-gradient(to right, #5A5852, #c2c2c2, #5A5852)
}
.h-bg__stripe, .l-caro-design > article, article.l-caro-design {
background: repeating-linear-gradient(45deg,#bbb,#bbb 24px,#ddd 24px,#ddd 48px);
}
}
/* Dark Mode end */
/* Reduced Motion begin*/
@media (prefers-reduced-motion: reduce) {
.hero__vect { animation: none; }
}
/* Reduced Motion end*/
<a class="js-skip__main--id skip__main"
href="#a11y-skipmain">Skip to Main Content</a>
2020-12-13
![Cypress E2E Quality Assurance](/patterns/e2e_tn.webp)
Cypress E2E Quality Assurance
End to End testing 🚀 Headless browser automation
/* _____
/ __ \
| / \/_ _ _ __ _ __ ___ ___ ___
| | | | | | '_ \| '__/ _ \/ __/ __|
| \__/\ |_| | |_) | | | __/\__ \__ \
\____/\__, | .__/|_| \___||___/___/.io
__/ | |
|___/|_| E2E
✅ Automatically capture a video when a test fails
✅ Test values persisted in the Vuex (Vue.js Vuex specific) store
✅ Apply optional configuration files via the command line
✅ Test the uploading of images
✅ Create custom reusable, and chainable commands, such as cy.signIn() or cy.turnOnFeature()
✅ Test responsive layout & Local Storage
✅ Test A11y WCAG success criteria */
describe('E2E test | Hotel navigation, selection, and discovery', () => {
context('Admin Add Hotel to Event', function () { // ignore CORS
Cypress.on('uncaught:exception', (err, runnable) => { return false });
it('Success Login then Save Event', () => {
cy.viewport(1066, 600) // large laptop 66.563em
cy.log( JSON.stringify(Cypress.env()) )
let event_url; // The URL of the first event (default)
let dMessage = new Date(); // Now
dMessage = dMessage.toDateString() + " " + dMessage.toLocaleTimeString();
cy.tt_SignIn(Cypress.env( "mock_email" ), Cypress.env( "mock_password" ))
.then(() => {
cy.window().then( $win => {
cy.wrap( $win.system_output ).should("exist")
})
})
cy.url().should('not.include', 'login.')
cy.visit( Cypress.env( "e2e_url_events" ) )
cy.url().should('include', 'events.')
Cypress.Cookies.debug(true, { verbose: false })
cy.getCookie('logintoken').should('exist')
cy.getCookie('role_id').should('exist')
cy.getCookie('username').should('exist')
cy.getCookie('otapi_token').should('exist')
cy.get("a[href*='event-edit']" ).first().click() // Find the first matching link in the table.
cy.get("#messages" ).type("{selectall}{backspace}E2E Test: " + dMessage )
cy.get("#eventForm > div.border-top.d-flex.pt-3.row > div > input" ).first().click() // Save change
cy.get("#airTab" ).click() // select tab
cy.get("#activate_flights" ).check();
cy.get("#flightForm > div.border-top.d-flex.pt-3.row > div > input" ).click();
cy.get("#vehicleTab" ).click() // select tab
cy.get("#activate_vehicle" ).uncheck();
cy.get("#vehicleForm > div.border-top.d-flex.pt-3.row > div > input" ).click();
cy.get("#hotelTab" ).click() // select tab
cy.get("#activate_hotels" ).check();
cy.get("#hotelForm > div.border-top.d-flex.pt-3.row > div > input" ).click();
// Extract URL from INPUT
cy.get('#siteURL').invoke('val')
.then( value => { event_url = value; });
cy.then(() => { return cy.visit(event_url); });
})
})
context('Choose Flight', function () {
Cypress.on('uncaught:exception', (err, runnable) => { return false }); // ignore CORS
it('Success Flight added to cart', () => {
cy.viewport(1066, 600) // large laptop 66.563em
cy.get("#from_airport" ).type( "ORD" )
cy.get("#to_airport" ).type( "LGA" )
cy.get("input[name='from_date']" ).click({ force: true })
cy.server()
cy.route("*").as( "checkout" )
cy.get("div.vdp-datepicker.flex-fill > div:nth-child(2) > div > span:nth-child(39)" ).first().click()
cy.get("#search-widget-btn" ).click()
cy.wait("@checkout" ).its('status').should('eq', 200)
cy.get("h5.modal-title").should("not.be.visible")
.then( ($ModalMsg) => {
cy.get("div.align-self-center.col-6.col-md.col-sm.col-xl.order-12.p-xs-1.text-right > button" ).first().click()
} )
})
})
context('Hotel LightBox', function () {
Cypress.on('uncaught:exception', (err, runnable) => { return false }); // ignore CORS
it('Success Hotel added to cart', () => {
cy.viewport(1066, 600) // large laptop 66.563em
cy.wait(2000)
cy.get("picture > img" ).first()
.then( ( $picture )=>{
cy.wrap( $picture ).click()
cy.wait( 6000 )
})
cy.get(".l-ltbx__image" ).first().click() // Cycle photos forward
cy.get(".l-ltbx__vect--right" )
.then( ( $arrow_right ) => {
cy.wait( 1000 )
cy.wrap( $arrow_right ).click()
cy.wait( 1000 )
cy.wrap( $arrow_right ).click()
cy.wait( 1000 )
cy.wrap( $arrow_right ).click()
})
cy.wait( 1000 )
cy.get(".l-ltbx__btn" ).first() // Cycle photos backward
.then( ( $arrow_left ) => {
cy.wrap( $arrow_left ).click()
cy.wait( 1000 )
})
cy.get(".l-ltbx__figcap").invoke("text").should("include", "4 of")
.then( () => {
cy.get(".l-ltbx__vect" ).first().click() // Close Modal
cy.get("OUTPUT BUTTON.l-button" ).first().click() // Book Room
.then( () => {
cy.get( "A.ttfont-semibold.tttext-gray-700").first().click() // Change Tab
cy.wait( 1000 )
cy.get( "A.ttfont-semibold.tttext-gray-700").first().click() // Change Tab
cy.wait( 1000 )
cy.get( "ARTICLE SECTION BUTTON.l-button").first().click() // Book Room
.then( ()=>{
cy.wait( 4000 )
cy.url().should('include', '/checkout')
})
})
})
})
})
})
2020-12-07
![Asynchronous eCom Nav Category Count](/patterns/javascript_tn.webp)
Asynchronous eCom Nav Category Count
Asynchronous recursive crawl reports the total number of products by category.
// Desc: Asynchronous recursive crawl report the total number of products by category
// Usage: Console SNIPPET catCount.init();
/* @@@@@@ @@@ @@@ @@@ @@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@
!@@ @@!@!@@@ @@! @@! @@@ @@! @@@ @@! @!!
!@@!! @!@@!!@! !!@ @!@@!@! @!@@!@! @!!!:! @!!
!:! !!: !!! !!: !!: !!: !!: !!:
::.: : :: : : : : : :: :: : run in console */
var catCount = (function(_d,_q){
"use strict";
let aSub = [];
console.clear();
return {
init: function(){
// Get ref to all product categories in the left nav 🛒
aSub = [ ... _d.querySelectorAll( _q ) ].filter( ( el ) => {
return (( el.firstChild.nodeValue ) && ( el.href ));
} );
aSub.forEach( ( elLink ) => {
if( elLink ) catCount.asyncTotal( elLink );
} );
},
parse: function( _Name, _Contents ){
let aTotl = _Contents.split("sizeTotalNumRecs");
if( aTotl[1].split('"')[2] ){
console.log( _Name, aTotl[1].split('"')[2]);
}
return true;
},
asyncTotal: function( _elLink ){
let oXhr = new XMLHttpRequest();
oXhr.open("GET", _elLink.href, true);
oXhr.onreadystatechange = () => {
if( this.readyState!==4 || this.status!==200 ) return;
catCount.parse( _elLink.firstChild.nodeValue, this.responseText );
};
oXhr.send();
}
}
})(document, "LI.item nav > a" );
2020-12-07
![Color of the Year CSS Styles](/patterns/css_tn.webp)
Color of the Year CSS Styles
Color of the Year 2000 thru 2021 CSS Utility classes
/* Tailwind like CSS Utility classes for the
Pantone Color of the Years from 2000 thru 2021
/* ____ _
* | _ \ __ _ _ __ | |_ ___ _ __ ___
* | |_) / _` | '_ \| __/ _ \| '_ \ / _ \ 2000- 2021
* | __/ (_| | | | | || (_) | | | | __/
* |_| \__,_|_| |_|\__\___/|_| |_|\___| 🟥 🟩 🟦 🟪 🟨 */
/* Color of the Year begin */
.bg-coy_2000 {background-color: #9BB7D4;} /* Cerulean */
.bg-coy_2001 {background-color: #C74375;} /* Fuchsia Rose */
.bg-coy_2002 {background-color: #BF1932;} /* True Red */
.bg-coy_2003 {background-color: #7BC4C4;} /* Aqua Sky */
.bg-coy_2004 {background-color: #E2583E;} /* Tigerlily */
.bg-coy_2005 {background-color: #53B0AE;} /* Blue Turquoise */
.bg-coy_2006 {background-color: #DECDBE;} /* Sand Dollar */
.bg-coy_2007 {background-color: #9B1B30;} /* Chili Pepper */
.bg-coy_2008 {background-color: #5A5B9F;} /* Blue Iris */
.bg-coy_2009 {background-color: #F0C05A;} /* Mimosa */
.bg-coy_2010 {background-color: #45B5AA;} /* Turquoise */
.bg-coy_2011 {background-color: #D94F70;} /* Honeysuckle */
.bg-coy_2012 {background-color: #DD4124;} /* Tangerine Tango */
.bg-coy_2013 {background-color: #009473;} /* Emerald */
.bg-coy_2014 {background-color: #B163A3;} /* Radiant Orchid */
.bg-coy_2015 {background-color: #955251;} /* Marsala */
.bg-coy_2016 {background-color: #F7CAC9;} /* Rose Quartz */
.bg-coy_2016b {background-color: #92A8D1;} /* Serenity */
.bg-coy_2017 {background-color: #88B04B;} /* Greenery */
.bg-coy_2018 {background-color: #5F4B8B;} /* Ultra Violet */
.bg-coy_2019 {background-color: #FF6F61;} /* Living Coral */
.bg-coy_2020 {background-color: #0F4C81;} /* Classic Blue */
.bg-coy_2021 {background-color: #939597;} /* Ultimate Gray */
.bg-coy_2021b {background-color: #F5DF4D;} /* Illuminating */
.bg-coy_2024 {background-color: #FFBE98;} /* Peach Fuzz */
.text-coy_2000 {color: #9BB7D4;} /* Cerulean */
.text-coy_2001 {color: #C74375;} /* Fuchsia Rose */
.text-coy_2002 {color: #BF1932;} /* True Red */
.text-coy_2003 {color: #7BC4C4;} /* Aqua Sky */
.text-coy_2004 {color: #E2583E;} /* Tigerlily */
.text-coy_2005 {color: #53B0AE;} /* Blue Turquoise */
.text-coy_2006 {color: #DECDBE;} /* Sand Dollar */
.text-coy_2007 {color: #9B1B30;} /* Chili Pepper */
.text-coy_2008 {color: #5A5B9F;} /* Blue Iris */
.text-coy_2009 {color: #F0C05A;} /* Mimosa */
.text-coy_2010 {color: #45B5AA;} /* Turquoise */
.text-coy_2011 {color: #D94F70;} /* Honeysuckle */
.text-coy_2012 {color: #DD4124;} /* Tangerine Tango */
.text-coy_2013 {color: #009473;} /* Emerald */
.text-coy_2014 {color: #B163A3;} /* Radiant Orchid */
.text-coy_2015 {color: #955251;} /* Marsala */
.text-coy_2016 {color: #F7CAC9;} /* Rose Quartz */
.text-coy_2016b {color: #92A8D1;} /* Serenity */
.text-coy_2017 {color: #88B04B;} /* Greenery */
.text-coy_2018 {color: #5F4B8B;} /* Ultra Violet */
.text-coy_2019 {color: #FF6F61;} /* Living Coral */
.text-coy_2020 {color: #0F4C81;} /* Classic Blue */
.text-coy_2021 {color: #939597;} /* Ultimate Gray */
.text-coy_2021b {color: #F5DF4D;} /* Illuminating */
.text-coy_2022 {color: #6667AB;} /* Very Peri */
.text-coy_2023 {color: #BE3455;} /* Viva Magenta */
.text-coy_2024 {color: #FFBE98;} /* Peach Fuzz */
/* Color of the Year end */
2020-12-07
![Solve Anagram Puzzle](/patterns/javascript_tn.webp)
Solve Anagram Puzzle
Do two strings contain the exact amount of letters to form two words?
/* An anagram is a word or phrase formed by rearranging the letters
of a different word or phrase, typically using all the original
letters exactly once. For example, the word anagram itself can be
rearranged into nag a ram, also the word binary into brainy. 🎯 🍰 🔥
_
/_\ _ __ __ _ __ _ _ __ __ _ _ __ ___
//_\\| '_ \ / _` |/ _` | '__/ _` | '_ ` _ \
/ _ \ | | | (_| | (_| | | | (_| | | | | | |
\_/ \_/_| |_|\__,_|\__, |_| \__,_|_| |_| |_|
|___/ */
// Determine if two strings are Anagrams
function isAnagram( word1 = "DOCTORWHO", word2 = "TORCHWOOD"){
let uc1 = word1.toUpperCase(), uc2 = word2.toUpperCase()
return ([ ... uc1 ].filter(( c )=>{
if( uc2.indexOf( c ) != -1 ){
uc2 = uc2.replace( c, "" ) // Replace First Occurrence
return true;
}
}).length === uc1.length && (!uc2))
}
console.warn( isAnagram("neodigm", "dogimen") );
// Palindromes | They can be read the same backwards and forwards!
// Is TACOCAT spelled backward still TACOCAT?
// People have been asking this question for thousands of years until...
// I wrote a function in JavaScript to prove it and end the debate. Palindrome in JavaScript
let isPalindrome = ( sIn = "tacocat" ) => ( sIn.split("").reverse().join("") === sIn );
/*🐈🐱
_._ _,-'""`-._
(,-.`._,'( |\`-/|
`-.-' \ )-`( , o o)
`- \`_`"'- My name is Taco! ^_^
*/
2020-12-07
![Virtual Keyboard Extention Configuration](/patterns/typescript_tn.webp)
Virtual Keyboard Extention Configuration
TS Virtual Keyboard Chrome Extention
// TS Virtual Keyboard ⌨️ Chrome Extention | Configuration Class
/***
* _______ _ _
* (_______) | | (_) _
* _ _ _ ____ ____ \ \ ____ ____ _ ____ | |_
* | | | | | | _ \ / _ ) \ \ / ___)/ ___) | _ \| _)
* | |____| |_| | | | ( (/ / _____) | (___| | | | | | | |__
* \______)__ | ||_/ \____|______/ \____)_| |_| ||_/ \___)
* (____/|_| |_|
npm install --save @types/chrome
*/
class AVKOptions {
aOpts : Array<any>;
constructor ( pAr : Array<any> = [] ) {
this.aOpts = pAr;
}
setState ( sOpt : string, bState : boolean ) : boolean{
this.aOpts = this.aOpts.filter( (e) => {
if (e[0] === sOpt) e[1] = bState;
return true;
} );
return bState;
}
getState ( sOpt : string ) : boolean {
return this.aOpts.filter( (e) => {
if (e[0] === sOpt) {
return true;
}
})[0][1];
}
getFeedback ( sOpt : string ) : string {
return this.aOpts.filter( (e) => {
if (e[0] === sOpt) {
return true;
}
})[0][2];
}
}
export let options = new AVKOptions([["audio", false, "Click Sounds"],
["autohide", false, "Hide if not in use"], ["blur", false, "Blur Text"],
["hover", false, "Hover No Click"], ["opaque", false, "Cannot See Through"],
["scramble", false, "Rearrange Keys"], ["theme", false, "Daytime theme"]]);
2020-12-07
![Web Music Ad Blocker Snippet](/patterns/javascript_tn.webp)
Web Music Ad Blocker Snippet
Automatically mute the Music player when Ads are playing and unmute when they are done (in Chrome).
/* Install: Open Chrome Dev Tools (Command+option+I on Mac). Menu > Sources > Snippets
Install: Create a new Snippet named musicADify.js, Paste this script, Save (Command+S).
Usage: Run the Snippet once each time you start the Music Web Player.
Usage: Right-Click the snippet named musicADify.js and choose Run from the drop-down.
Usage: Close Chrome Dev Tools. 🏖️ Play your Jams! 🎶
╔═╗┌─┐┌─┐┌┬┐┬┌─┐┬ ┬ ╔═╗┌┬┐┌─┐
╚═╗├─┘│ │ │ │├┤ └┬┘ ╠═╣ ││└─┐
╚═╝┴ └─┘ ┴ ┴└ ┴ ╩ ╩─┴┘└─┘ */
let spotADify = ( (_d, _q, _t) => {
let eS = _d.querySelector( _q ), bS = true;
if( eS ){ // 🏖️ Play your Jams! 🎶
bS = ( eS.getAttribute("aria-label") == "Mute" );
setInterval( () => {spotADify.tick();}, _t);
return {
"tick": () => {
if((_d.title.indexOf("Adve") != -1) || (_d.title.indexOf("Spoti") != -1)){
if( bS ){ eS.click(); bS=!true; }
}else{
if( !bS ){ eS.click(); bS=true; }
}
}
}
}
})( document, "[aria-label='Mute'],[aria-label='Unmute']", 256);
2020-12-07
![Capture Entire DOM State into Inline CSS Snapshot](/patterns/javascript_tn.webp)
Capture Entire DOM State into Inline CSS Snapshot
Save As HTML a snapshot capture of entire DOM State with inline CSS
// Desc: Save As HTML a snapshot capture of entire DOM State with inline CSS
// Usage: Just paste this code into the console 🌴
/* _________ __ __ ____ __.
/ _____/ ____ _____/ |__/ |_ | |/ _|___________ __ __ ______ ____
\_____ \_/ ___\/ _ \ __\ __\ | < \_ __ \__ \ | | \/ ___// __ \
/ \ \__( <_> ) | | | | | \ | | \// __ \| | /\___ \\ ___/
/_______ /\___ >____/|__| |__| |____|__ \|__| (____ /____//____ >\___ > ES2022*/
function computedCSS2inline(element, options = {}) {
if (!element) {
throw new Error("No element specified.");
}
if (options.recursive) {
Array.from( element.children ).forEach(child => {
computedCSS2inline(child, options);
});
}
const computedStyle = getComputedStyle(element);
//(options.properties || computedStyle)::each(property => {
Array.from( computedStyle ).forEach(property => {
element.style[property] = computedStyle.getPropertyValue(property);
//element.setAttribute("class", "")
});
}
computedCSS2inline(document.body, {recursive: true});
[ ... document.querySelectorAll("script, link, style")].forEach(function(s){ s.outerHTML = ""})
async function saveToFile() {
const handle = await showSaveFilePicker({
suggestedName: 'grabbed.html',
types: [{
description: 'HTML',
accept: {'text/html': ['.html']},
}]
});
const writable = await handle.createWritable();
await writable.write(document.body.parentNode.innerHTML);
writable.close();
};
console.log("NOTE: Run saveToFile() in console!")
2020-09-16
Real-world Vetted Snippets