JavaScript_870x220

In this tutorial, we build a Chrome extension to download gists from GitHub Gist and explore the different parts that make up a Chrome extension, effectively harnessing the power of jQuery and JavaScript to build an extension.

Chrome extensions are browser programs made to customize functionality and modify behavior in the Google Chrome browser. They are written in HTML, JavaScript and CSS. With Chrome extensions, you can do more than just customize web pages—you can also add custom behaviors and functionality to suit your needs by harnessing the power of JavaScript. In short, any JavaScript program that can be created in the browser can be written as a Chrome Extension.

GitHub Gist is a simple way to share code snippets and pastes with others. It is a platform where you can share single files, parts of files or full applications with other people. Gists are driven by git version control, so they also have complete revision histories.

In this tutorial, we will create a Chrome extension to download code snippets from GitHub gists.

Prerequisites

To follow along, a basic understanding of JavaScript / ES6 and jQuery is required. Knowledge of HTML and CSS is recommended but not mandatory. You should also have at least Node version 6+ installed on your system. For a guide on how to install Node, check out the official docs.

Deep Dive

First we need to create a project directory to work from. Create a folder called gistdownloader and open it in your favorite text editor. Create an index.html file in the root directory and create a js directory with a main.js file.

Let’s start by creating the manifest.json file. The manifest file simply describes the extension’s content. Every Chrome extension is required to have a manifest``.json file. In the root of your project, create a manifest.json file and add the following code to it.

    // manifest.json
    {
      "manifest_version": 2,
      "name": "GitHub Gist downloader",
      "description": "An extension used for downloading github gists",
      "version": "1.0",
    
      "browser_action": {
        "default_icon": "icon.png"
      },
    
      "permissions": [
        "activeTab",
        "download"
      ],
    
      "content_scripts": [
        {
          "matches": ["https://gist.github.com/*"],
          "js": ["js/main.js"],
          "run_at": "document_end"
        }
      ]
    }

Chrome manifests are JSON Objects with various properties. Some are required, while some are optional, and which to use depends on the function of your extension. Our manifest file expects to have a manifest_version of 2.

Every Chrome extension needs an identifier. This is usually the extension’s icon. The browser_action object tells the extension which icon to use. It also has other values that can be found here.

The permissions property is an array of permissions our extension needs. Since we need access to the current tab and the ability to download when the gist is opened, we set the value to activeTab and download.

Finally, the content_scripts array contains an object with three properties.

  • matches: This is Required. It specifies which pages this content script will be injected into (which is https://gists.github.com in our case).
  • js: This is Optional. It specifies the list of JavaScript files to be injected into matching pages. These are injected in the order they appear in this array.
  • run_at: This basically tells the extension when to inject the script. In our case, we want it to be injected after the page has successfully loaded.

More info on this can be found here.

At this point, our folder structure should look like this:

    gistdownloader/
        js/
            main.js
        icon.png
        index.html
        manifest.json

The main.js file will be the main entry file for the project and will update the relevant DOM elements in the html file when needed.

Copy the following code into the index.html file:

    <!-- ./index.html -->
    <!doctype html>
    <html lang="en">
      <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
     <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    
       <title>Gist Downloader</title>
      </head>
      <body>
         <div class="file">
            <div class="file-header">
              <div class="file-actions">
              </div>
            </div>
        </div>
        <script src="./js/main.js"></script>
      </body>
    </html>

In the index.html file, we do three things:

  1. We load in bootstrap in the head of the page.
  2. We create div with classes. This is where we will load in the button to download the gist.
  3. Finally we load in our main.js file.

We haven’t created our main.js file yet, so let’s go ahead and do that. Copy the following code into the main.js file:

    // js/main.js
    
    $(window).on('load', function() { 
      const fileActions = document.body.querySelectorAll(
        '.file .file-header .file-actions '
      );
    
      $.each(fileActions, function(i, val) {
        const containerEl = document.createElement('span');
        $(containerEl).append(
          "<button type='submit' onclick='prepareDownload()' class='customName btn btn-sm copy-pretty tooltipped tooltipped-n BtnGroup-item' aria-label='Download the file'>Download file</button>"
        );
        $(this).prepend(containerEl);
      });
    
      addEventListenerForRawLinks()  
    });
    
    function download(filename, text) {
      const element = document.createElement('a');
      element.setAttribute(
        'href',
        'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
      );
      element.setAttribute('download', filename);
      element.style.display = 'none';
      document.body.appendChild(element);
      element.click();
      document.body.removeChild(element);
    }
    
    // when button is clicked
    function prepareDownload() {
      const gistText = document.getElementsByClassName('highlight tab-size js-file-line-container')[0].innerHTML;
      download("gist", gistText )
    }
    
    function addEventListenerForRawLinks() {
        var links = document.getElementsByClassName("customName");
        if (links !== null) {
            links[0].addEventListener('click', prepareDownload);
        }
    }

Let’s go over the file. First we use the window.load function to make sure the scripts start running when the page is fully loaded.

Next, we select all the elements matching the classes '.file .file-header .file-actions '. That is where we intend to put our download button.

After we loop through all the matching div’s, we then create a span tag and append a button to it. Finally, we prepend the button to the outer div.

Notice the onclick=``"``prepareDownload``" function inside the button, as that is the method that will trigger the download when clicked.

We finally end that function by adding an event listener to grab raw links from the page. (Raw links are the absolute links to the gist files.) We do this so as to comply with GitHub’s content script policy.

Outside that block, we define three functions:

  • download : The actual function for downloading the file. It accepts two parameters: a file name and the contents to download.

  • prepareDownload : This is the function that is called when the button is clicked. It grabs the text of the gists by getElementsByClassName method. It then calls the download function and passes in the required parameters.

  • addEventListenerForRawLinks : This is the function called when the download button is clicked. Its purpose is to call the prepareDownload function.

And that’s all! We have our extension. However, one thing is left. We have been using jQuery without having it in our file, which will cause some errors if we try to load the file. So we need to include jQuery in our main.js file.

To do that, first grab the jQuery code from this page and save it into a file called jquery-3.1.1.js in our js directory.

Next we need to concatenate the two JavaScript files together and build our final project files. For that we will use gulp, a lightweight JavaScript task runner.

Under your project directory in your terminal type in the following:

npm install --save dev gulp gulp-concat

That command will create a package.json file. The file should look like this:

    // ./package.json
    {
      "name": "gistdownloader",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "gulp": "^4.0.0",
        "gulp-concat": "^2.6.1"
      }
    }

Finally we need to create a gulpfile.js to run the functions. Create a gulpfile.js in the root of your project and add the following to it:

    // ./gulpfile.js
    
    var concat = require('gulp-concat');
    var gulp = require('gulp');
    
    gulp.task('addJs', function() {
      return gulp.src(['./js/jquery-3.3.1.js', './js/main.js'])
        .pipe(concat('main.js'))
        .pipe(gulp.dest('./build/js'));
    });
    
    gulp.task('copyFiles', function () {
       return gulp.src(['*.html', '*.png', 'manifest.json'])
            .pipe(gulp.dest('./build/'));
    });

In this file we create two tasks—one to add our Js files together (with the help of the gulp-concat plugin), and one to copy our files to a **build** directory.

In your terminal under the root folder, run the following commands:

****
    gulp addJs 
    gulp copyFiles

Once that is successful, you will see a build directory created with our files in it.

Testing Our Extension

Now it’s time to test what we have done so far. Open your Chrome browser and go to Settings > Extensions. Next, turn on developer mode by toggling the button. You should now see a button on the left side that says load unpacked. Click it to upload your build folder.

s_0331D6F0B6B9A9F5A93946DE6A751DF3EC4F585DE527C2043F218D83A76C24F5_1551952491477_1

After uploading the build folder, you should now see your extension loaded.

s_0331D6F0B6B9A9F5A93946DE6A751DF3EC4F585DE527C2043F218D83A76C24F5_1551952552825_1

To test our extension, go to gists.github.com and select a gist. You should see a download button next to the raw button. Click it and it will download the gist!

s_0331D6F0B6B9A9F5A93946DE6A751DF3EC4F585DE527C2043F218D83A76C24F5_1551952716676_1

Note: All the files for this extension can be found here.

Conclusion

In this tutorial, you have learned how to create Chrome extensions that can manipulate page data. There’s a whole lot you can do with Chrome extensions! Be sure to check out the official documentation. Happy coding.


Great Apps start with a Great UI

Want to learn more about creating great web apps? It all starts out with Kendo UI, a complete UI component library that allows you to quickly build high-quality, responsive apps. It includes everything you need, from grids and charts to dropdowns and gauges.

KendoJSft


Christian Nwamba
About the Author

Christian Nwamba

Christian is a Lagos, Nigeria based software developer and developer advocate. He’s keeps pushing boundaries with/for the Next Billion Users and Next Million Developers through Microsoft

Related Posts

Comments

Comments are disabled in preview mode.