Friday, 18 September 2020

Day's until Xmas

Last night there was a question on Jay's Virtual Pub Quiz asking how many days there were until Xmas. I worked something up years ago but decided to revisit it today while waiting for Visual Studio to build my solution, this is the result:

const days2Xmas = Math.ceil(
  (
    new Date(
      new Date().getMonth() == 11 && new Date().getDate() > 25 ?
      new Date().getFullYear() + 1 :
      new Date().getFullYear(),
      11,
      25
    )
    .getTime() -
    new Date().getTime()
  ) /
  (1000 * 60 * 60 * 24)
)
console.log(days2Xmas)

Tuesday, 15 September 2020

Frontend vs backend developers (well, not really)

@backend1: @frontend1, @frontend2 - a bug here
@frontend1: @backend1 OMW
@frontend2: @backend1 Sure thing. @frontend1, let me know if you need a hand.

...

@frontend2: Not sure if this is a frontend or backend issue
@backend2: Frontend! Set the flux capacitor to true

...

@frontend2: Flux capacitor set to true, will look some more
@backend2: Are you sure?
@frontend2: @backend2, yep, just double-checked, shall I call you and show you what's happening?
@backend2: Sure

...

@backend2: @frontend2 backend issue.

Frontend and backend (or, for that matter, fullstack) developers are a funny lot, collectively. They're funny individually as well, but communication between them can be hilarious. I work with a brilliant team, and I've noticed a specific trend when it comes to the allocation of bugs to fix. I'm working with a backend technology I'm not overly familiar with, so I know I can be a pain when it comes to making requests or asking questions. I also acknowledge, wholeheartedly, that I'm guilty of making prototypes for the frontend with absolutely no regard to the information I'm likely to receive from the server - but I live in hope.

I have, however, noticed a specific trend with bugs that fall in the grey area between the responsibilities. Or those bugs where the cause is unclear. Generally speaking, it's obvious, but now and again much less so.

When a bug is a frontend issue, we (my fellow frontend developer and I) rush to take the issue and solve it just as soon as we can. Any delay is incurred only as a result of testing the solution in Chrome, Firefox, Edge and IE11. We make the PR and move along, avidly waiting for the next thing to fix, all the while searching for issues ourselves.

It's those grey area bugs that cause issues though. I'll take a look at something, clock that it's wrong and investigate. We're using Vue, so I'll open up the Developer Tools and check what we're sending back to the server using the Vue.js devtools and think about what I'm expecting back. If it's wrong, I'll tentatively suggest that the backend developers take a look and check to see if it's the responsibility of the frontend or backend developers to fix. The majority of the time the response will be that it's a frontend issue so I'll take another, more exhaustive, look and document the process. I'll perhaps prepare screenshots and videos to illustrate the process to go along with the documentation. Only then will I ask my teammates to check again and see if it's the responsibility of the frontend or backend developers to fix the bug. 9 out of 10 times I'll be right, and it will probably be a simple matter for them to fix. It's good for me to investigate though, I gain a far greater understanding of the working methods of my fellow developers, but it would be nice to be trusted now and again.

I wrote this the previous weekend, today I clocked that I'd argued with a backend developer over a very similar issue, which was eventually discovered to be a frontend issue!

Doh! I've also just clocked this is my 500th post!

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 All done now - such fun.

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``

Day 10

A few days late getting this uploaded but I was thinking... This is my attempt but, as was pointed out, it fails when the numbers get too large!

const maxMultiple = (d, b) => ~~(b/d) * d

After my ponder I clocked that this is much better:

const maxMultiple = (d, b) => b - (b % d)

I also played with some other methods:

const maxMultiple = (d, b) => Math.floor(b/d) * d // works
const maxMultiple = (d, b) => ((b/d) >> 0) * d // fails
const maxMultiple = (d, b) => parseInt(b/d, 10) * d // works

Every day really is a school day!

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