Bookmarklets

This is a React port of a CodePen I created a few years ago to help me with repetitive tasks I have to do on a regular basis as a developer – such as filling out forms and creating new logins, etc. Who knows – you may find it useful too. Once I clean it up a bit more and add some tests, I’ll share the code .

Some functional stuff

Screen capture

function buildAttrs(obj) {
    let result = [];
    for (prop in obj) {
        result.push(`${prop}="${obj[prop]}"`);
    }
    return result.join(" ");
}

function createOutput(tag, attrs = {}) {
    return function outputTag(content = "") {
        return `<${tag || "div"} ${buildAttrs(attrs)}>${content}</${tag || "div"}>`;
    };
}

function outputToDom(el, tag, attrs = {}) {
    return function outputToContainer(content = "") {
        getTargetContainer(el).innerHTML += createOutput(tag, attrs)(content);
    };

    function getTargetContainer(id) {
        return document.getElementById(id) || addDiv(id);
    }

    function addDiv(id) {
        const targetDiv = document.createElement("div");
        targetDiv.id = id;
        return document.body.appendChild(targetDiv);
    }
}

function pipe(...fns) {
    return function piped(result) {
        for (let i = 0; i < fns.length; i++) {
            result = fns[i](result);
        }
        return result;
    };
}

const heading = createOutput("h1", { style: "margin: 0" });

const paragraph = createOutput("p");

const article = createOutput("article");

const output = outputToDom("js-output", "section", {
    style: "outline: 1px dotted red; padding: 10px; margin: 10px"
});

const capitalize = i => i.replace(i.charAt(0), i.charAt(0).toUpperCase());

const capitalizeHeading = pipe(capitalize, heading);

const capitalizeParagraph = pipe(capitalize, paragraph);

const buildArticle = i =>
    article(`${capitalizeHeading(i.heading)} ${capitalizeParagraph(i.body)}`);

const buildArticles = i => i.map(buildArticle);

const joinArticles = arr => arr.join("");

const content = [
    {
        heading:
            "A lot of people cry when they cut onions. The trick is not to form an emotional bond.",
        body:
            "Go Speed Racer. Go Speed Racer. Go Speed Racer go. we might as well say... Would you be mine? Could you be mine? Won't you be my neighbor? Baby if you've ever wondered - wondered whatever became of me. I'm living on the air in Cincinnati. Cincinnati WKRP. It's time to put on makeup. It's time to dress up right. It's time to raise the curtain on the Muppet Show tonight."
    },
    {
        heading:
            "I wrote a song about a tortilla. Well actually, it’s more of a wrap.",
        body:
            "Boy the way Glen Miller played. Songs that made the hit parade. Guys like us we had it made. Those were the days. Then one day he was shootin' at some food and up through the ground came a bubblin' crude. Oil that is. They're creepy and they're kooky mysterious and spooky. They're all together ooky the Addams Family."
    },
    {
        heading:
            "Some people just have a way with words, and other people ... oh ... not have way.",
        body:
            "In 1972 a crack commando unit was sent to prison by a military court for a crime they didn't commit. These men promptly escaped from a maximum security stockade to the Los Angeles underground. Sunday Monday Happy Days. Tuesday Wednesday Happy Days. Thursday Friday Happy Days.Saturday what a day. Groovin' all week with you. If you have a problem if no one else can help and if you can find them maybe you can hire The A-Team. And when the odds are against him and their dangers work to do. You bet your life Speed Racer he will see it through. The mate was a mighty sailin' man the Skipper brave and sure. Five passengers set sail that day for a three hour tour a three hour tour. Now were up in the big leagues getting' our turn at bat. In a freak mishap Ranger 3 and its pilot Captain William Buck Rogers are blown out of their trajectory into an orbit which freezes his life support systems and returns Buck Rogers to Earth five-hundred years later."
    }
];

const headingStyles = { style: "font-family: sans-serif" };

outputToDom("js-headings", "h1", headingStyles)(
    "Functional JS"
);

outputToDom("js-headings", "h2", headingStyles)(
    "AKA This is wickedly cool shit!"
);

pipe(buildArticles, joinArticles, output)(content);

Iterator Example

Iterator Example

This is a poor example as you could certainly accomplish this more easily with .map(), however, this demonstrates that the iterator ‘remembers’ its position and picks up where it left off. A better example would be where other procedures or logic are executed between calls.

function makeIterator(array) {
    var nextIndex = 0;
    return {
        next: ()=>(nextIndex < array.length) && array[nextIndex++],
        remain: ()=>array.length - nextIndex
    }
}
var fun = makeIterator(['fe', 'fi', 'fo', 'fum', 'I', 'smell']);

while(fun.remain()){
    console.log(fun.next());
}

Array Iteration

Two quick ways to iterate through an array (from .length to 0).

What’s the point?

  • It’s fast to compare something to zero
  • Less code overall

Why it’s weird…

We’re used to counting from zero to length – not vice-versa

When not to use

The only place I can think of that this wouldn’t be a good idea would be when you need to iterate over the array in ascending key order.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
var b = a.length;

// quickly iterate through array
while (b--) {
console.log(a[b]);
}

// declaring var in same line
for (var c = a.length; c--; ) {
console.log(a[c]);
}

// of course THIS is best
a.forEach(i => console.log(i));

Thoughts on coercion

Thoughts on coercion

Here’s an interesting tidbit. If you’ve ever used indexOf to determine if a string contains another string (perhaps I could word this better…), then you’ve probably used something like:

var a = 'Jim';
a.indexOf('i') !== -1; // true

As you’re probably aware, if the string is not found, –1 is the return value.

A simpler and more efficient way to accomplish this test is:

var a = 'Jim';
~a.indexOf('i'); // true (coerced)

or

Boolean(~a.indexOf('i')); // true

A lot of people will state that coercion in JS is evil. I’d state that what is really evil is when people don’t know what that coercion is doing. I recently watched a video series by Kyle Simpson where he illustrated a few places where using coercion to your advantage will create simpler, easier to understand, less error-prone code. For example:

var foo; // undefined

Let’s say that foo, in this case could be undefined or null.
Using explicit coercion, our check for this value would be:

if ( foo===undefined || foo===null ) {
    // should work, all bases are covered
}

A simpler, more readable check:

if ( foo == null ) {
    // works for null or undefined
}

Partially-Applied Function – Practical Example

This example demonstrates several concepts:
  • modules
  • partially applied functions
  • functional programming techniques
The partialGetElID function creates a partially applied function that replaces hyphenated, underscored, or dotted names with their camelCased variant.
module.exports = {
    partialGetElID: (wf, pg) =>el=>`${sepsToCamelCase(wf)}-${sepsToCamelCase(pg)}-${sepsToCamelCase(el)}`
};

var sepsToCamelCase = function(arg) {

    var upperFirst = s => s.replace(s.charAt(0), s.charAt(0).toUpperCase());
    var lowerFirst = s => s.replace(s.charAt(0), s.charAt(0).toLowerCase());

    return (
        R.pipe(
            R.split(/[._-]/g),
            R.map(upperFirst),
            R.join(''),
            lowerFirst
        )(arg)
    );
};
Usage:

var X = require('path/to/module');
var getID = X.partialGetElId('workflow.name', 'step_name');

getID('element-name') // returns workflowName-stepName-elementName

Functional Programming Example – using Ramda

Functional Programming Example – using Ramda

Copy and paste this code into your console. It automatically loads Ramda and uses that to process the beerData.

Two concepts covered in this example:

  • Loading external scripts into the console (which works in chrome developer tools snippets at minimum). I can’t seem to find a way to make it work in firefox.
  • Functional programming.

#functional, #loadingScripts

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '//cdnjs.cloudflare.com/ajax/libs/ramda/0.21.0/ramda.min.js';
document.head.appendChild(script);

beerData = [
    {"name": "Bitter Hop", "type": "ipa", "locale": "domestic", "abv": 7.9},
    {"name": "Dragonblood Black IPA", "type": "ipa", "locale": "domestic", "abv": 6.5},
    {"name": "Good Dog Lager", "type": "lager", "locale": "domestic", "abv": 4.8},
    {"name": "Good Dog Snowpants", "type": "stout", "locale": "domestic", "abv": 8.1},
    {"name": "Lake Erie 2x IPA", "type": "ipa", "locale": "domestic", "abv": 9.3},
    {"name": "Lake Erie Session", "type": "ale", "locale": "domestic", "abv": 4.2},
    {"name": "Samwell British Pale Ale", "type": "ale", "locale": "import", "abv": 6.5},
    {"name": "Samwell Oatmeal Stout", "type": "stout", "locale": "import", "abv": 5.5},
    {"name": "Samwell Winter Ale", "type": "ale", "locale": "import", "abv": 6.8},
    {"name": "Sparkwood Beer Five", "type": "lager", "locale": "domestic", "abv": 6.2},
    {"name": "Sparkwood Campfire Lager", "type": "lager", "locale": "domestic", "abv": 5.8},
    {"name": "Tartan Scottish Ale", "type": "ale", "locale": "import", "abv": 7.5}
]

console.clear();

console.log('beerData:');
console.table(beerData);

var uniqueTypes = R.pipe(R.pluck('type'), R.uniq, R.sort((a, b) => a > b));
var typeList = uniqueTypes(beerData);

console.log('typeList: ', typeList);

var displayBeerByType = R.pipe(
    R.propEq('type'),
    R.filter(R.__, beerData),
    console.table.bind(console)
);


R.forEach(displayBeerByType, typeList);