Tuesday, 30 June 2020

The 10-Day JavaScript Challenge

Yay! There's another one started, such fun!

I'll add to this is I go along.

Day 1

const add = (...a) => a.reduce((t, v) => t + v)

Day 2

const allLongestStrings = a => a.filter(i => i.length === Math.max(...(a.map(a => a.length))))

This is not overly efficient, but it works. Then I got to remembering about default values and altered it to this:

const allLongestStrings = (a, i = Math.max(...(a.map(a => a.length)))) => a.filter(e => e.length === i)

Longer, but we only check for the longest string the once, rather than over each iteration.

Day 3

const allLongestStrings = (a, i = Math.max(...(a.map(a => a.length)))) => a.filter(e => e.length === i)

Day 4

const arrayReplace = (arr, o, n) => arr.map(e => e === o ? n : e)

Day 5

const caseInsensitivePalindrome = (s) => s.toLowerCase().replace(/[\W_]/g, '') === s.toLowerCase().replace(/[\W_]/g, '').split('').reverse().join('')

I like this way because it deals with "A man, a plan, a canal, Panama!". But I really like this one (not mine):

const caseInsensitivePalindrome = s => [...s = s.toLowerCase()].reverse().join`` === s;

Day 6

This was a fun one as we needed to produce three different approaches, here are mine:

const encloseInBrackets = s => `(${s})`
const encloseInBrackets = s => ['(', s, ')'].join('')
const encloseInBrackets = s => ['('].concat(s).concat(')').join('')

This can be improved to this though:

const encloseInBrackets = s => '('.concat(s, ')')
const encloseInBrackets = s => [...s.split('')].map((c, i, a) => i === 0 ? `(${c}` : i === a.length - 1 ? `${c})` : c).join('')

This isn't mine (it's from musa#0677), but I love it:

const encloseInBrackets = s => String.fromCharCode(40, ...s.split('').map((_, i) => s.charCodeAt(i)), 41);

Day 7

const factorialNumber = n => n ? n * factorialNumber(n - 1) : 1

Day 8

const firstDigit = s => s.match(/\d/)[0]

Day 9

const largestNumber = n => ~~[...Array(n)].fill(9).join``

Tuesday, 23 June 2020

ISBN-10: 1780174764 & ASIN: B08BK5TGKS

I thought I'd spend a few minutes this morning trying to figure out what categories my book belongs to on Amazon. It was an interesting exploration.

  • Books
    • Business, Finance & Law
      • Careers
    • Computing & Internet
      • Computer Science
        • Programming
        • Software Design, Testing & Engineering
      • Professionals
        • Software Design, Testing & Engineering
      • Programming
        • Introduction to Programming
        • Software Design, Testing & Engineering
          • Functional Programming
          • Software Architecture
      • Software & Graphics
        • Software Design & Development
          • Software Design, Testing & Engineering
      • Web Development
        • E-commerce
          • Web Administration
          • Web Design
          • Web Scripting & Programming
        • Web Administration
          • Web Scripting & Programming
        • Web Design
          • Web Scripting & Programming
        • Web Scripting & Programming
    • Health, Family & Lifestyle
      • Self Help
    • Mind, Body & Spirit
      • Self Help
    • University Textbooks
      • Computer Science
        • Software Design, Testing & Engineering
          • Software Development
  • Kindle eBooks
    • Computing
      • Programming
        • Software Design, Testing & Engineering
    • Nonfiction
      • Computing

Red, Amber and Green progress bar

I've been doing some Moodle theming over the past few weeks and ended up discussing progress bars with the designer involved. Little progress is bad whereas finishing a Moodle course is good so I, of course, started waxing lyrical about colours. If red means bad and green means good, what about the middle ground? Then I remembered a traffic light visualisation I was working on ages ago and the joys of amber. Interestingly that post is ten years old! I must've been thinking about colours all these years - guess that's a good (green?) thing for a front-end developer!

Anyway, the project I'm working on, as I said, it based within Moodle and writing JS for that is something of a challenge. I can't get my head around the module system they use, and I don't want to learn it enough to get access to jQuery within a theme's script, so I wrote it in Vanilla JS.

I had CSS variables for the colours, so first I clocked I needed those within the script. Still, I also needed them in terms of the RGB values - as an array would work fine, after some research I came up with hexToRGB, which produces a simple collection with the RGB values as three distinct integer values in an array. One possible issue with using JS to get the computed style from a CSS variable is that it returns everything from the colon to the semi-colon as a string. That's good and all, but that left an extraneous space which needed trimming.

const hexToRGB = hex => hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => '#' + r + r + g + g + b + b).substring(1).match(/.{2}/g).map(x => parseInt(x, 16))
const red = hexToRGB(getComputedStyle(document.documentElement).getPropertyValue('--under-third').trim())
const amber = hexToRGB(getComputedStyle(document.documentElement).getPropertyValue('--third-to-two-thirds').trim())
const green = hexToRGB(getComputedStyle(document.documentElement).getPropertyValue('--over-two-thirds').trim())

I also needed to mix the colours, which wasn't much of a bother. Though I did discover there were multiple ways of mixing colours, I chose to go the most straightforward route with mixColour (hey, what can I say? I know I'm supposed to use `color` when I code HTML and CSS, but I much prefer `colour`). mixColour uses a further function, imaginatively called mix, where the heavy lifting occurs and takes into account the percentage of each colour needing mixing, we pass this as a float between 0 and 1.

const mixColour = (c1, c2, pc) => RGBToHex(Math.round(mix(c1[0], c2[0], pc)), Math.round(mix(c1[1], c2[1], pc)), Math.round(mix(c1[2], c2[2], pc)))
const mix = (s, e, pc) => s + ((pc) * (e - s))

Once mixed, I needed to produce RGB values again, so I worked up RGBToHex which uses some smart bit-wise operators - I wish I knew what they did. Still, I use bit-wise operators so infrequently that I can't seem to work up the enthusiasm to learn them properly.

const RGBToHex = (r, g, b) => `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`

We had values between 0 and 100 for the progress bar, so working out the float value was the only thing that caused me any head-scratching. If the amount was less than 50, then red and amber needed to be mixed by the value divided by 50; if the value was over 50 then we use amber and green: we need to subtract 50 from the value and then, again, divide the result by 50.

let value = 0;
const container = document.getElementById('bar');
const bar = container.querySelector('.progress-bar-filler');
const interval = setInterval(
  () => {
    value = value === 100 ? 0 : value + 1;
    container.setAttribute('data-value', value);
    bar.style.width = value + "%";
    bar.style.backgroundColor = value < 50
      ? mixColour(red, amber, (value / 50))
      : mixColour(amber, green, ((value - 50) / 50))
  }, 100);

Simples eh? Once it's all broken down, then it's just a collection of function, and it works a treat too. There are again some dodgy colours produced between amber and green - but that's only for a very little time, something like 5% I think.

All that was left was to work up a demo.

Wednesday, 3 June 2020

Free course: The 7-Day JavaScript Challenge

Scrimba are running a week-long JavaScript challenge, so I jumped in.

Day 1

Initial attempt:

function addBorder(array) {
    const l = Array.from({length: array[0].length + 2 }, () => '*').join('');
    array = array.map((x) => x.padStart(x.length + 1, '*').padEnd(x.length + 2, '*'))
    array.push(l)
    array.unshift(l)
    return array;
}

After I'd thought about it:

const addBorder = a => ['*'.repeat(a[0].length), ...a, '*'.repeat(a[0].length)].map(e => `*${e}*`)

Day 2

const addTwoDigits = n => (''+n).split('').reduce((a,c) => ~~a + ~~c)

Day 3

const firstDuplicate = ns => {
  const s = new Set();
  for(n of ns){
    if(s.size === s.add(n).size) return n
  }
  return -1
}

Day 4

const sumAllPrimes = n => Array.from({length: n-1}, (_, k) => k+2).reduce((a, c) => a += !'1'.repeat(c).match(/^1?$|^(11+?)\1+$/) ? c : 0)

Day 5

const evenDigitsOnly = n => (''+n).split('').every(n => !(~~n % 2))

Day 6

const makeArrayConsecutive = n => Math.max(...n) - Math.min(...n) - [...new Set(n)].sort().reduce((a, c) => c > Math.min(...n) && c < Math.max(...n) ? a + 1 : a, 0) - 1

Alternatively:

const makeArrayConsecutive = n => ([...new Set(n)].sort()[[...new Set(n)].length - 1] - [...new Set(n)].sort()[0] + 1) - [...new Set(n)].length

Day 7

const properNounCorrection = s => s === s + '' && s.length ? String.fromCharCode(...[...s].map((_, i) => !i ? s.charCodeAt(i) - (s.charCodeAt(i) >= 97 && s.charCodeAt(i) <= 122 ? 32 : 0) : s.charCodeAt(i) + (s.charCodeAt(i) >= 65 && s.charCodeAt(i) <= 90 ? 32 : 0))) : ''

Think that's all of them now.

Thursday, 21 May 2020

SVG background image workflow

I've done a fair bit of work recently using SVGs as background images, so I thought I'd better document the sites I use (thanks again Daniel). I put the original SVG image through SVGOMG! I then paste the resulting, clean, markup into URL-encoder for SVG. Quite a nice and simple process:

  1. SVGOMG!
  2. URL-encoder for SVG

Sunday, 10 May 2020

Layout techniques

You're all lucky bleeders! You are! These are the days of miracle and wonder (And don't cry baby, don't cry). When I started, I was using tables for layouts and thinking they were the best thing since sliced bread - how could they ever be beat? I even, and this shames me significantly, spent a fair amount of time figuring out how to do rounded corners using tables. That's from 2005, don't you know! Don't blame me for the errors in my youth; even then I had to code things for IE.

I did make a foray into float-based layouts, but since that time I stopped worrying and offloaded the heavy lifting to frameworks for a few years. Until, that is, that last few years when CSS frameworks started to feel a little constraining.

So, in my first book, I took a wander through the different ways of creating a layout. And let me tell you, there are ever-so-many options now!

These are the methods I discuss:

Table based

Float based

Float based with wrapper

Position based

CSS table based

Flexbox based

Grid based

The examples above are different from those in the book because... reasons. I guess that after spending hours and hours staring at them, I've clocked that they could be better, and I've refactored them as a result. The same happened yesterday when I spent hours looking at an experiment from the book.

I've been doing this professionally for something like a decade now, so if I'm not allowed to go back and look at old code and scream like a little girl then Yah, boo, hiss! Of course, the opposite is true, and rarely I'll come across some old code and be blown away at its elegance.

I guess the point of this post though is to remind older, fusty developers (hands-up!) that they don't need to deal with the vagueries of IE6 anymore and can start to embrace some of this new-fangled CSS that's, actually, been around for quite a while! That's not to say that the above will work out-of-the-box on IE11. It's got a dodgy implementation of CSS Grid - but there are ways around that.

Saturday, 9 May 2020

User test, updated

In my forthcoming book, I make significant use of a piece of research I carried out some years back on how users could enter data about their family. The example was contrived, but it served its purpose. I talk about how I could've used a SPA but that at that time I wasn't comfortable building them. I also wax lyrical about Native JavaScript Modules along with various other technologies, so last weekend and this I spent some time putting my money where my mouth is.

I set out to replicate the original statically hosted application using Vue, Vuex and Vue-Router, and I'm quite pleased with the result. Then I got a little carried away and updated Bootstrap from v3 to v4. I also dusted out my old Font-awesome account and threw taht into the mix, along with some SCSS, just for fun!

It's nigh on where I want it to be TBH, except I can't get the router to work correctly; going to any page other than the home page throws an error. Scratch that: I'm hosting on repl.it so I need to use the "hash" mode for the router rather than "history". Simples, eh?

I also ran the text through Grammarly.