Skip to content

Instantly share code, notes, and snippets.

View color-helpers.ts
type RGB = [number, number, number];
export function parseRgb(color: RGB | string): RGB {
if (Array.isArray(color)) {
return color;
let c = color;
let m;
if (
mrclay /
Created May 14, 2020
Flush IP tables and restart docker
# Script is needed because my default firewall rules are messed up and after
# every restart, docker containers can't make connections to the host, notably
# preventing debuggers like xdebug from attaching.
set -euo pipefail
# Unless docker is stopped with no containers running, docker will leave zombie
# proxy processes that hold the ports open preventing the start of new containers.
mrclay / usePrevious.ts
Created May 5, 2020
usePrevious in TypeScript with init value
View usePrevious.ts
import { useEffect, useRef } from 'react';
export default function usePrevious<T, U>(value: T, init: U): T | U {
const ref = useRef<T | U>(init);
useEffect(() => {
ref.current = value;
return ref.current;
mrclay / exceljs-extras.js
Last active Nov 20, 2019
Convert Exceljs rich text values to HTML or plaintext strings
View exceljs-extras.js
function isRichValue(value) {
return Boolean(value && Array.isArray(value.richText));
function richToString(rich) {
return{ text }) => text).join('');
function richToHtml(rich) {
let str ={ font = {}, text }) => {
mrclay /
Created Aug 21, 2019
docker-compose logs -f but auto restarting
function dclogs {
while :
docker-compose logs -f --tail=3
[ $? -eq 0 ] || break
echo "Awaiting a container to restart"
sleep 5
# wait around for startup
mrclay / fewest-meetings.js
Last active Aug 13, 2019
Exercise: From given list of meetings of varying lengths, find the shortest number of meetings that can fit in the day
View fewest-meetings.js
// Recursively create all combinations of the remaining items--at least those
// that are valid and aren't longer than those we've tested so far.
function buildCombinations(remaining, combo, process) {
for (let i = 0; i < remaining.length; i++) {
if (process.isDone()) {
// global short-circuit
mrclay / ClassList.php
Created Dec 9, 2017
HTML class list based on DOMTokenList
View ClassList.php
namespace MrClay;
* HTML class list based on DOMTokenList
* @link
class ClassList
mrclay / AjaxForm.js
Created Nov 11, 2015
Sets up Drupal so that any form can be fetched and submitted over Ajax. Unlike Drupal's ajax module, there are no modifications required to the form at all. Even redirects are captured and sent back to the client.
View AjaxForm.js
* Notes:
* - This is not the complete JS module
* An object to simplify fetching Drupal forms via Ajax
* @param {object} spec Object with keys:
mrclay / elgg-profile-queries.php
Last active Oct 21, 2015
Elgg: profile MySQL queries on Elgg 1.10 - 1.x
View elgg-profile-queries.php
* Query profiler for Elgg 1.10-1.12
* Require this script inside settings.php and the JavaScript console will report all
* queries with their time in seconds, and the total time spent in mysql_query().
* This will not include queries performed after the "output", "page" hook.
* On production you could include this only if a particular query string is set:

More details on the WordPress XSS vulnerability found by Klikki. Both real exploits include a style attribute to widen the mouseover area to the whole viewport; I've left it out here to keep it simple.

The exploit comment is valid HTML and won't be altered by an HTML santizer:

<a title='x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAAA...[64 kb]..AAA'></a>

But once truncated by MySQL, the comment will become malformed HTML (note the attribute is left open):

<a title='x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAA
You can’t perform that action at this time.