Exporting pens made easy

Update 1 (06/2017)

Support added for list view

Update 2 (07/2018)

Fix for Firefox and added dialog boxes to help the user

Added iframe utilities to the post, showing the process

Added help GIF

var links = [], titles, data, accept, link;

// Get all the pen IDs on the page
if (window.location.href.indexOf('?grid_type=list') === -1) {
    titles = document.getElementsByClassName('cover-link');
    for (var i = 0; i < titles.length; i++) {
        links.push(titles[i].href);
    }
} else {
    titles = document.getElementsByClassName('title');
    for (var i = 0; i < titles.length; i++) {
        links.push(titles[i].firstElementChild.href);
    }
}

// Alert the user if getting pen IDs failed
if (links.length === 0) {
    alert('Download pens: No pens found on this page :(')
} else {
    // Warn the user about lag, if user accepts launch download
    accept = confirm('Starting download...\nIf not all pens start downloading, please check if you have allowed codepen.io to display pop-ups\n\nTabs will fly open and close, THIS MAY LAG YOUR COMPUTER. Do you want to continue?')
    if (accept) {
        // Generate download links and click them
        for (var i = 0; i < links.length; i++) {
            links[i] = links[i].replace(/http:\/\/codepen.io\/|https:\/\/codepen.io\//, '');
            data = links[i].split('/pen/');
            link = document.createElement('a');
            link.href = '//codepen.io/' + data[0] + '/share/zip/' + data[1];
            link.target = '_blank';
            // Need to add the link to the body for Firefox
            document.body.appendChild(link);
            link.setAttribute("type", "hidden");
            link.click();
        }
    }
}

Updated complete script

The problem

So you’ve been on Codepen for a while and you’ve created quite a lot of pens. Being very proud of said pens, you’ve surely been thinking

Gosh, I would really like to save all of my work to my local drive, but going through my pens one by one is reaaaaaally tiring!

Fear not, valorous programmer! I have made a way to make exporting pens great again!

TL;DR

Drag this link to your bookmark bar. Go to your Codepen profile, and click the link. All the pens on the page will start downloading. If they don’t, allow Codepen to display pop-ups

Codepen export plugin

Nice.

The solution

The export link

First, let’s take a look at how Codepen manages pen exports. I clicked the Export button and got the link adress of Export .zip

https://codepen.io/ninivert/share/zip/PmqOMp/

Hmmm, very interesting! So I entered the link in my adress bar and sure enough, my pen started downloading!

Getting pen adresses

So we need two components to generate our export links

Now how do we get these components? Let’s look at a classic pen URL

https://codepen.io/ninivert/pen/PmqOMp

It’s very easy to get these components from the pen URL

var url = 'https://codepen.io/ninivert/pen/PmqOMp'
url = url.replace(/http:\/\/codepen.io\/|https:\/\/codepen.io\//, '')
url     // ninivert/pen/PmqOMp
var data = url.split('/pen/')
data[0] // ninivert
data[1] // PmqOMp

Now, let’s recompose the export URL

var export = 'https://codepen.io/' + data[0] + '/share/zip/' + data[1]
export // https://codepen.io/ninivert/share/zip/PmqOMp

Upon entering the URL in my adress bar, the pen starts downloading!

Getting ALL pens

Now, how do we get ALL of a user’s pens? Since the pens have a random hash, it is impossible to loop through all of them. Let’s then use the pen display on one’s Codepen profile page. Upon inspecting the page HTML source code, I see a very easy solution

<a href="http://codepen.io/ninivert/pen/PmqOMp" class="cover-link"></a>

Awww yeah, this means I can use the getElementsByClassName method to get all pens URLs

var links = document.getElementsByClassName('cover-link')
links // [a.cover-link, a.cover-link, ... a.cover-link]
var urls = []
for (var i = 0; i < links.length; i++) {
  urls.push(links[i].href)
}
urls // ['http://codepen.io/ninivert/pen/PmqOMp', ...]

Sidenote: this is only possible in Grid View, as the link with the class only exists there. I’ve considered trying out List View, but it would be more complicated and would cause problems with simultaneous downloads on potato PC’s

Edit: the link in the embedded pen now works on both Grid and List view :)

Aaand the downloads

Let’s generate links and assign each of them to a URL

var a = document.createElement('a')
a.href = '//codepen.io/' + data[0] + '/share/zip/' + data[1]
a.target = '_blank'
a.click()

Sidenote: I intentionally left out https://, otherwise the link would go to a Codepen 404

Script injection

When we assemble everything, we get this script

var links = document.getElementsByClassName('cover-link')
for (var i = 0; i < links.length; i++) {
  var url = links[i].href
  url = str.replace(/http:\/\/codepen.io\/|https:\/\/codepen.io\//, '')
  var data = url.split('/pen/')
  var a = document.createElement('a')
  a.href = '//codepen.io/' + data[0] + '/share/zip/' + data[1]
  a.target = '_blank'
  a.click()
}

Very nice! Now we just need to convert the script to an encoded URI component. To the encoded script, we add javascript:(function(){/* script */})() for it to be actually recognized and executed as a script. Below are the tilities used to minify the code, encode, and generate the link.

var script = /* script above */
var uri = encodeURIComponent(script)
var href = 'javascript:(function()%7B' + uri + '%7D)()%3B'

Tiny JavaScript Minifier

JS to link

Final code

We finally set the last href variable as the link href

<a href="javascript:(function()%7Bvar%20links%20%3D%20document.getElementsByClassName('cover-link')%3B%0Afor%20(var%20i%20%3D%200%3B%20i%20%3C%20links.length%3B%20i%2B%2B)%20%7B%0A%20%20var%20url%20%3D%20links%5Bi%5D.href%3B%0A%20%20url%20%3D%20url.replace(%2Fhttp%3A%5C%2F%5C%2Fcodepen.io%5C%2F%7Chttps%3A%5C%2F%5C%2Fcodepen.io%5C%2F%2F%2C%20'')%3B%0A%20%20var%20data%20%3D%20url.split('%2Fpen%2F')%3B%0A%20%20var%20a%20%3D%20document.createElement('a')%3B%0A%20%20a.href%20%3D%20'%2F%2Fcodepen.io%2F'%20%2B%20data%5B0%5D%20%2B%20'%2Fshare%2Fzip%2F'%20%2B%20data%5B1%5D%3B%0A%20%20a.target%20%3D%20'_blank'%3B%0A%20%20a.click()%3B%0A%7D%7D)()%3B">Export pens</a>

Improvements

The link might not work at first try, because your browser (Chrome 57 for me) is automatically blocking new tabs from being opened. I’d need authorisations only a browser plugin could get to open more than 2 tabs simultaneously. If you’ve got any thoughts on this problem, please let me know in the comments!

Also, the script targets unwanted links on the homepage, but eh.

Thanks a lot for reading!