segunda-feira, 17 de novembro de 2014

Creating A “Save For Later” Chrome Extension With Modern Web Tools

Creating A “Save For Later” Chrome Extension With Modern Web Tools


  • By Daniel Sternlicht

  • November 17th, 2014

  • CSSJavaScriptTools

  • 0 Comments

Creating an prolongation for a Chrome browser is a good approach to take a tiny and useful suspicion and discharge it to millions of people by a Chrome Web Store. This essay walks we by a growth routine of a Chrome prolongation with complicated web collection and libraries.


It all starts with an idea. Mine was shaped while reading an engaging (and long) essay about new front-end technologies. we was concentrating on reading a essay when unexpected my mother called me to flog out a bad baby seagul that got stranded on a balcony. When we finally got behind to a article, it was too late — we had to go to work.


To make a prolonged story short, we suspicion it would be good to emanate a Chrome prolongation that enables we to symbol your reading swell in articles so that we can continue reading them after — anywhere.


“Markticle” is a name we chose for this extension. I’ll share here a technologies that we used to rise it. After reading this article, you’ll have a ready-to-use “Save for Later”-like Chrome extension.


image01-preview-opt1

Prior Knowledge


We’re going to use a few front-end technologies. While we can learn some of them on a fly, believe of others is compulsory (marked in bold):


  • jQuery

  • AngularJS

  • Node.js

  • Grunt

  • Bower

  • Yeoman

Scaffolding


Let’s start with some infrastructure work.


Assuming you’re informed with npm2 (Node.js’ package manager), we’re going to use a Yeoman generator to emanate a simple prolongation for Chrome.


Note: If we still don’t have Yeoman commissioned on your machine, start by following a “Getting Started3” tutorial.


Open a new authority line or depot window, and write a following command:


npm implement -g generator-chrome-extension

This will implement Yeoman’s generator for Chrome extensions on your machine.


Create a new folder in your record system:


mkdir my-extension

And afterwards run a following authority to beget all of a files that you’ll need to start building your extension:


yo chrome-extension

After regulating this command, a generator will ask we that facilities to embody in a extension.


In a case, Markticle should do a few things:


  1. Add an idol subsequent to a residence bar.

  2. Run on any page that a user opens.

  3. Run some formula in a credentials to bond a stream page to a prolongation in sequence to save data.

For a initial feature, we’ll name “browser” as a UI action. To capacitate a prolongation to run on any web page, we’ll check a “Content scripts” box. Finally, to capacitate credentials processes to run, we’ll use a background.js file.


Note: Another approach to emanate a Chrome prolongation is to use a online generator Extensionizr4. Extensionizr is a good apparatus that helps we emanate simple Chrome extensions. It has mixed pattern options, all of that can be enabled with checkboxes. In a end, you’ll get a ZIP record that includes all of a files you’ll need to start operative on a extension. The downside is that you’ll need to configure Grunt and Bower manually.


Folder Tree


Let’s demeanour during a generated files and folders we’ve got now.


  • app

  • test

  • bower.json

  • package.json

  • Gruntfile.js

Gruntfile.js is where we’ll configure Grunt tasks for serving, building, contrast and wrapping a extension.


The package.json and bower.json files are Node.js and Bower JSON files that conclude a extension’s dependencies on third-party plugins and libraries.


The test folder will embody all of a section and end-to-end tests for a extension. Finally, a app folder is a many engaging since it is where a core of a prolongation will reside.


After reordering some of a folders and files, here’s what a app folder will demeanour like:


image06-preview-opt
  • icons
    • icon-16.png

    • icon-19.png

    • icon-38.png

    • icon-128.png


  • images

  • views

  • scripts
    • inject.js

    • background.js


  • styles

  • main.css

  • _locales
    • en

    • messages.json


  • index.html

  • manifest.json

The many critical record here is manifest.json. It is indeed a heart of a extension, and it specifies several things, including a following:


  • the plcae of any record used by a extension,

  • which idol to benefaction as a “action” button,

  • the permissions that your prolongation needs,

  • the name of a extension.

Here’s an instance of what a manifest.json record should demeanour like:



"name": "Markticle",
"version": "1.0.0",
"manifest_version": 2,
"icons":
"16": "icons/icon-16.png",
"38": "icons/icon-38.png",
"128": "icons/icon-128.png"
,

"default_locale": "en",
"background":
"scripts": [
"scripts/helpers/storage.helper.js",
"scripts/background.js"
]
,

"browser_action":
"default_icon": "icons/icon-19.png",
"default_popup": "index.html"



First Flight


We now have a simple prolongation that does nothing. Still, only to make certain all is in place and operative properly, let’s exam a prolongation in runtime.


Open Chrome and write this in a residence bar:


chrome://extensions

This page displays information about all of a extensions now commissioned in your browser.


In a top-right corner, you’ll see an choice to capacitate “Developer mode.” Click it.


Now, click a “Load unpacked extension” button, crop to a plcae of a prolongation we created, name a app folder, and click “Select.”


image04-preview-opt5

You should now see a extension’s idol subsequent to a residence bar.


image05-preview-opt

Installing Dependencies


Before regulating a app, we need to implement some Node.js plugin dependencies. We’ll do so by regulating a following command:


npm install

The final thing we need to do before diving into a formula is set adult a dependencies of a third-party libraries we’re going to use. We do this in a bower.json file:



"name": "Markticle",
"version": "1.0.0",
"dependencies":
"angular": "1.2.6",
"jquery": "2.0.3",
"normalize.scss": "3.0.0"
,

"devDependencies":


I chose 3 libraries for this project: AngularJS, jQuery and Normalize.css. To implement these, run this command:


bower install

Development


Now that we are prepared to start development, let’s separate a work into dual parts.


The initial partial will be a popup window that opens when a user clicks a extension’s icon. Markticle’s popup will benefaction a list of bookmarks (i.e. websites) that a user has saved.


image07-preview-opt

The second partial connects a user’s actions to a prolongation itself. Each time a user takes a sold movement on a page, a prolongation should save a URL and pretension of a stream add-on (so that we know what to arrangement in a popup).


The initial partial is flattering straightforward. We’ll use classical AngularJS formula to rise it.


Let’s start by adding a following record structure to a app/scripts folder.


  • scripts
    • controllers
      • main.controller.js


    • directives
      • main.directive.js


    • helpers

    • storage.helper.js

    • services
      • storage.service.js


    • app.js

    • inject.js

    • background.js


In a app.js file, we’ll supplement a following code, that will conclude a app’s categorical module:


angular.module('markticle', []);

Now, let’s supplement some simple formula to a index.html file:


!DOCTYPE HTML
html
head
couple href="styles/main.css" rel="stylesheet"
/head
physique ng-app="markticle"
div id="main_wrapper"Sample/div

book src="bower_components/jquery/jquery.min.js"
book src="bower_components/angular/angular.min.js"


book src="scripts/app.js"
book src="scripts/controllers/main.controller.js"
book src="scripts/directives/main.directive.js"
/body
/html

What we’ve finished here is really simple:


  • define a tellurian AngularJS procedure named markticle,

  • add a singular div component with representation text,

  • include a list of book files that we’re going to use.

Now, let’s extend a div component that we created.


div id="main_wrapper" ng-controller="MainController"
header
h1My Marks/h1
/header
section id="my_marks"/section
/div

Again, zero special here — we’ve only set adult an AngularJS controller named MainController and combined some header and section tags for a arriving content.


In a app/scripts/controllers/main.controller.js file, let’s emanate a new AngularJS controller:


angular.module('markticle').controller('MainController', function($scope) 
$scope.marks = [];
);

This controller now doesn’t do most solely conclude an array, named marks, that is trustworthy to a controller’s scope. This array will embody a user’s saved items.


Just for fun, let’s supplement dual equipment to this array:


$scope.marks = [

title: 'Smashing magazine',
url: 'http://www.smashingmagazine.com/'
,

title: 'Markticle',
url: 'https://markticle.com'

];

Now, in a index.html file, let’s loop by them with a ng-repeat directive:


section id="my_marks"
ul
li ng-repeat="mark in marks"
a target="_blank" ng-href="mark.url"mark.title
/li
/ul
/section

Click a extension’s idol to open a popup and see a result!


After adding some simple CSS to a main.css file, here’s what we’ve come adult with:


image00-preview-opt

Now for a second part.


In a second part, we’ll bond user interactions to a extension.


Let’s start by adding a new skill to a manifest.js file:




"background": …,
"content_scripts": [

"matches": ["http://*/*", "https://*/*"],
"js": ["bower_components/jquery/jquery.min.js", "scripts/inject.js"]

],



Here, we’ve combined a skill named content_scripts, that has a possess dual properties:


  • matches

    This is an array that defines in that websites to inject a book — in a case, all websites.

  • js

    This is an array of scripts that will be injected into any web page by a extension.

Let’s open a inject.js book and supplement some simple formula to it:


$(document).ready(function() 
var createMarkticleButton = function()
var styles = 'position: fixed; z-index: 9999; bottom: 20px; left: 20px;';
$('body').append('');
;
$(document).on('click', '#markticle_button', function()
var pretension = document.title;
var url = window.location.href;
console.log(title + ': ' + url);
);
createMarkticleButton();
);

This book does dual things once a page is ready. First, it adds a simple symbol regulating a createMarkticleButton() method. Then, it adds an eventuality listener that writes a URL and pretension of a stream page to Chrome’s console any time a user clicks a button.


To exam this, go to chrome://extensions, find your extension, and click a “Reload” button. Then, open any website, click a Markticle button, and demeanour during a console in Chrome Developer Tools.


image03-preview-opt6

Storing Data


To store information in a prolongation (without carrying to use a server-side solution), we have several options. My favorite is HTML5 localStorage7.


Let’s go behind to a scripts folder and emanate a localStorage service. First, revise app/scripts/helpers/storage.helper.js:


var markticleStorageService = function() 
var lsName = 'marks';
var information = localStorage.getItem(lsName) ? JSON.parse(localStorage.getItem(lsName)) : [];

lapse

get: function()
lapse data;
,
add: function(item)
this.remove(item.url);
data.push(item);
this.save();
,
remove: function(url)
var idx = null;
for(var i = 0; i data.length; i++)
if(data[i].url === url)
idx = i;
break;


if(idx !== null)
data.splice(idx, 1);
this.save();

,
save: function()
localStorage.setItem(lsName, JSON.stringify(data));

;
;

With this, we’re initial holding a data array with a stream information that we’re pulling from localStorage. Then, we’re divulgence a few methods to manipulate a data, such as get(), add() and remove().


After formulating this class, let’s also supplement it as an AngularJS use in app/scripts/services/storage.service.js:


angular.module('markticle').service('StorageService', markticleStorageService);

Note: Don’t forget to impute to both scripts in index.html.


The reason we’ve separate it into dual scripts is since we’re going to reuse a markticleStorageService category in background.js, where we won’t entrance AngularJS.


Returning to a MainController, let’s make certain we’re injecting a storage use in a app:


angular.module('markticle').controller('MainController', function($scope, StorageService) 
$scope.marks = […];
);

Finally, let’s bond a StorageService information to a app and deliver a routine that will be used in a UI.


angular.module('markticle').controller('MainController', function($scope, StorageService) 
$scope.marks = StorageService.get();
$scope.removeMark = function(url)
StorageService.remove(url);
$scope.marks = StorageService.get();
if(!$scope.$$phase)
$scope.$apply();

;
);

Back to a index.html file. Let’s supplement an choice to mislay equipment by joining a perspective to a controller’s remove() method:


li ng-repeat="mark in marks"
a ng-href="mark.url"mark.title/a
camber class="remove" ng-click="removeMark(mark.url)"remove/span
/li

So, any time a user clicks a “Remove” button, it will call a remove() routine from a controller, with a page’s URL as a parameter. Then, a controller will go to StorageService and mislay a intent from a information array and save a new information array to a localStrorage property.


Background Process


Our prolongation now knows how to get and mislay information from a localStorage service. It’s time to capacitate a user to supplement and save items.


Open app/scripts/background.js, and supplement a following code:


chrome.extension.onMessage.addListener(function(request, sender, sendResponse) 
if(request)
var storageService = new markticleStorageService();
if(request.action === 'add')
storageService.add(request.data);


);


Here, we’re adding a listener for a onMessage event. In a callback function, we’re formulating a new instance for markticleStorageService and removing a request object. This intent is what we’re going to send with a chrome.extension.sendMessage eventuality that is triggered from a inject.js script. It contains dual properties:


  • action

    This is a form of movement that we wish a credentials routine to perform.

  • data

    This is a intent of a information that we wish to add.

In a case, a form of movement is add, and a intent is a indication of a singular item. For example:



title: 'Markticle',
url: 'https://markticle.com'


Let’s go behind to a inject.js book and bond it to a background.js script:


$(document).on('click', '#markticle_button', function() 
var pretension = document.title;
var url = window.location.href;
chrome.extension.sendMessage(
movement : 'add',
data:
title: title,
url: url

);
alert('Marked!');
);

Now, go to any website and click a “Mark me!” button. Open a popup again and see a new intent you’ve only added. Pretty cool, right?


image02-preview-opt

Build


We’ve combined a cold “Save for Later” Chrome prolongation of sorts. Before releasing it to a Chrome store, let’s speak about a build routine for a Chrome extension.


A build routine for this kind of app could have a few goals (or “tasks,” to use Grunt’s fixing convention):


  • test (if you’re essay section tests for a extension),

  • minify,

  • concatenate,

  • increment a chronicle series in a perceptible file,

  • compress into a ZIP file.

If you’re regulating Yeoman’s generator, we can perform all of these tasks automatically by regulating this command:


grunt build

This will emanate a new dist folder, where we will find a minified and concatenated files, and another folder named package, where you’ll find a ZIP record named with a stream chronicle of your extension, prepared to be deployed.


Deploy


All that’s left to do is muster a extension.


Go to your “Developer Dashboard8” in a Chrome Web Store, and click a “Add new item” button.


image08-preview-opt9

Browse to a ZIP record we combined and upload it. Fill in all of a compulsory information, and afterwards click a “Publish changes” button.


Note: If we wish to refurbish a extension, instead of formulating a new item, click a “Edit” symbol subsequent to a extension. Then, click a “Upload updated package” symbol and repeat a remaining steps.


Conclusion


As we can see, building a Chrome prolongation has never been easier!


If we use Node.js and Grunt for their time-saving features, AngularJS as a growth horizon and a Chrome Web Store for distribution, all we need is a good idea.


I wish you’ve enjoyed reading this article. If it was too prolonged to review in one sitting, cruise regulating Markticle10.


(il, al)


Footnotes


  1. 1 http://www.smashingmagazine.com/wp-content/uploads/2014/11/image01-large-opt.jpg

  2. 2 http://nodejs.org/

  3. 3 http://yeoman.io/learning/index.html

  4. 4 http://extensionizr.com/

  5. 5 http://www.smashingmagazine.com/wp-content/uploads/2014/11/image04-large-opt-500×180.jpg

  6. 6 http://www.smashingmagazine.com/wp-content/uploads/2014/11/image03-large-opt.jpg

  7. 7 http://www.w3schools.com/html/html5_webstorage.asp

  8. 8 https://chrome.google.com/webstore/developer/dashboard

  9. 9 http://www.smashingmagazine.com/wp-content/uploads/2014/11/image08-large-opt.jpg

  10. 10 https://markticle.com/

↑ Back to topShare on Twitter



Creating A “Save For Later” Chrome Extension With Modern Web Tools

Nenhum comentário:

Postar um comentário