Posts in "Chrome Extensions" tag

How to Use the Omnibox API in Google Chrome

Posted on 26 July 2010 and tagged with 0 comments

The Omnibox API in Chrome is currently an experimental API, which means you can’t upload the extension to the gallery if your using it. Though, it works very well for local testing, and that is what we are going to do now.

When I first heard there was going to be an Omnibox API I figured it would let us hook in on every change in the address bar and then add my own results to it. However, this isn’t how the API was implemented, and after reading Aaron’s comment about why, I understand and believe it’s the right way to go.

[…] The omnibox has a very small results space. I'm hesitant to get into a
situation where a bunch of extensions are fighting to make their results the most relevant. […]

Aaron Boodman

In fact the only difference from how I thought it would work is that each extension sets a keyword (like search engines) in the manifest (omnibox_keyword) and this is the word the extension will respond to in the Omnibox. This means that, like Aaron said, we won’t have to fight with other extensions about space – we have our own reserved space in the results list. Smart!

How to Use

Like mentioned earlier, you need to first add a setting to the manifest.json file where you specify the keyword the Omnibox will respond to. This will look identical to how the custom search engines behave in the Omnibox

omnibox-1

Currently the Omnibox API consists of four events:

  • chrome.experimental.omnibox.onInputStarted
  • chrome.experimental.omnibox.onInputChanged
  • chrome.experimental.omnibox.onInputEntered
  • chrome.experimental.omnibox.onInputCancelled

The two events you will use the most are onInputChanged and onInputEntered. InputChanged occurs every time you type something in the Omnibar (after typing the keyword). This is also the event where you will be inserting the suggestions you want to show up in the results list. This is done in the following manner (placed in the background.html file):

chrome.experimental.omnibox.onInputChanged.addListener(function (text, suggest) {
    if (text.search('coffee') > -1) {
        var suggestions = [];

        suggestions.push({ content: 'Coffee - Wikipedia', description: 'Coffee - Wikipedia' });
        suggestions.push({ content: 'Starbucks Coffee', description: 'Starbucks Coffee' });

        suggest(suggestions);
    }
});

The suggest function we are calling at the end wants an array of suggestions. Each suggestion object has tree properties; content, description, and descriptionStyles (optional). The content property is the value the address bar will get when we choose this suggestion, and also the value we receive in the onInputEntered event. The description is the text we see in the results list. Lastly the descriptionStyles; it lets us style the description in different ways, e.g. green, grayed and bold. More on how this works later.

When running this code and typing our keyword in the Omnibox (e.g. “sp”), pressing space, and typing “coffee” you will get the following results:

omnibox-3

Pretty awesome, right! Now this example is quite useless but we could do something much smarter such as submitting this command to a web server, which then starts the coffeemaker and a notification popup is shown when it’s ready.

Whenever you press enter the value will be sent to the event listeners of the onInputEntered event. This event receives the entered text through it’s first parameter “text”. See example:

chrome.experimental.omnibox.onInputEntered.addListener(function (text) {
    alert('You entered: ' + text);
});

If you liked the idea about the coffeemaker, then here is where you will send the command to the web server. Or whatever you like it to do!

Beautify it

The suggestions objects we created earlier only used the content and description properties, and leaving out the descriptionStyles property. This property is quite cool (and nicely implemented). It determines how the description will look in the results list. We can choose between four different styles:

  • styleDim – slightly grayed out, used for helper text
  • styleMatch – used to display matched text, in bold
  • styleUrl – used for URLs and filenames
  • styleNone – resets the style to normal

They are then used by inserting in an array and specifying at which offset it should take effect. This means we can stack up as many different styles we want. We only have to set the offset at which their styles begin. Lets say we have a suggestion where we will display an URL and its page title. It would look like this in code:

var url = 'http://google.com';
var title = 'Google Search';

var suggestion = {
    content: url,
    description: url + ' - ' + title,

    descriptionStyles: [
        chrome.experimental.omnibox.styleUrl(0),
        chrome.experimental.omnibox.styleDim(url.length)
    ]
};

And this is how it looks in our Omnibar:

omnibox-4

Go create some code!

That’s all I have about the Omnibox API. It’s your turn now to create some nice extensions making use of this API, I sure will. However as noted before, you can’t upload extensions using experimental APIs to the extensions gallery. Hopefully it won’t take that long before the Chrome devs finish it.

If anything’s unclear, feel free to leave a comment :-)

Resources

Read More »

To-Do Chrome Extension Part 3 – Make it Work

Posted on 18 April 2010 and tagged with 0 comments

This is part three of the series on making a to-do extension for Google Chrome.

In this post we are going to finish the browser action popup to a state where it is fully functional. That means being able to add, remove, edit and complete tasks. We will also apply some styling so our eyes won’t burn to death each time we look at it.

Bind event handlers

We have a lot of elements laying in our extension, that does nothing. We need to hook these up to an event handler. In JavaScript there are two ways you can add an event handler to an element. First, and most common, is by using the addEventListener method (target.addEventListener('click', handler, false)) or using jQuery ($(target).click(handler)). These are great because they make the HTML code very clean from JavaScript and element attributes. The other way is through element attributes, e.g. <a onclick="handler()">. In a website this easily gets messy in the markup and makes it harder to maintain. On the other hand, in extensions we write a lot more JavaScript than in a normal webpage and would rather choose to have clean and maintainable script files. This is why I will attach all event handlers through element attributes in our extension.

We only need four handlers in our popup – the checkbox (onchange), task textbox (onkeyup), remove button (onclick) and the add task textbox (onkeyup), which makes our code look like this:

<div id="results">  
    <div class="item" jsselect="$this" jsvalues="id:id">
        <input type="checkbox" onchange="view.changeCompleted(this.parentNode.id, !!this.value)" class="completed" checked="checked" jsvalues="checked:completed" />
        <span class="title" onkeyup="view.changeTitle(this.parentNode.id, this.innerText)" contenteditable="true" jscontent="title"></span>    
        <button class="remove" onclick="view.removeTask(this.parentNode.id)" title="Remove Task">Remove</button>
    </div>
</div>
<input type="text" id="add" onkeyup="view.newTask(event)" placeholder="Write task and press enter..." />

You likely notice that I have included some parameters to each event handler. These help to identify which task I am changing. E.g. this.parentNode.id gets the parent element’s id attribute (where we store the task’s id).

Implementing the handlers

We will be keep all our handlers in the view object that we created in the previous post. The first handler is the one to add a task, called newTask. It will fire each time you type in the textbox but we only want save a new task when the user presses the enter key, which is why we need an if statement to check for that. The rest is simply adding the new task with our TaskRepository and resetting the textbox. We will also change the init function a bit since last post.

textbox: null,

init: function() {
    this.textbox = document.getElementById('add');
    this.renderView();
},

newTask: function(e) {
    if (e != null && e.which == 13 && this.textbox.value != "") { // enter key
        db.create({
            title: this.textbox.value,
            completed: false,
            created: new Date()
        });
        this.textbox.value = '';
        view.renderView();
    }
},

There are three handlers left to write, but they are very self explanatory and won’t need a closer description as they will merely be using methods of TaskRepository. Following is the code for changeTitle, changeCompleted and removeTask:

changeTitle: function(id, title) {
    db.getById(id).title = title;
    db.save();
},

changeCompleted: function(id, completed) {
    db.getById(id).completed = completed;
    db.save();
},

removeTask: function(id) {
    db.remove({ id: id });
    view.renderView();
    return;
},

Styling the popup

If you run the extension in its current state you will see that all functionality works as expected, but like I said in the previous post, it looks horrible and is hard to use. I have prepared some CSS styles for it, but as this post isn’t about CSS I won’t be getting into details about it. If you want to have a look at the CSS code, see the ZIP file at the bottom.

Before and after styling the popup:

Before styling  image

Round up

In only three posts we managed to create a fully functional to-do manager for Google Chrome. This shows how easy it is to develop for Chrome with HTML 5 and JavaScript.

Our extension still doesn’t fulfill all our requirements, though. We still need to implement an options page and a task counter in the icon. That’s the subject for the next post.

I have “compiled” the extension to an installable .crx file in case you want to run it and see how it works. Personally I’m already using this extension for my to-do lists.

Source code is, like always, available in a link below.

Downloads

Install extension!
Download To-Do Extension Source (Part 3)

Read More »

To-Do Chrome Extension Part 2 – Browser Action Popup

Posted on 25 March 2010 and tagged with 0 comments

This is part two of the series on making a to-do extension for Google Chrome.

In this post I will cover how to implement a good database layer for our extension, as well as how to use the jsTemplate library to display our data nicely. Fairly simple right? Let’s get to work.

We will begin with writing the necessary HTML markup for displaying the tasks. To make it easier to display data from JavaScript in HTML we will be using a template system called jsTemplate, created by Google. It will allow us to separate the presentation from logic, which is always a good idea.

Let’s add the jsTemplate library to our popup.html file. There are three files needed to run the library, but to save some file requests I joined them to one file. You can find this file in the ZIP-archive at the bottom of this post. Extract the file in the /js/libs/ folder and add the following line to the head section:

<script src="js/libs/jstemplate.js" type="text/javascript"></script>

We need to define the properties of a task – a task will have a descriptive title, a boolean for whether the task is completed, and lastly a date of creation. Translating this to HTML markup means we need a checkbox, text element and a remove button for each task item. The date of creation will not be displayed in my version, but it is always good idea to include it incase you need it later. The template for a task item will look like the snippet below.

<div id="results">
    <div class="item" jsselect="$this" jsvalues="id:id">
        <input type="checkbox" class="completed" checked="checked" jsvalues="checked:completed" />
        <span class="title" contenteditable="true" jscontent="title"></span>    
        <button class="remove" title="Remove Task">Remove</button>
    </div>
</div>

As you have probably already seen—there are a number of weird, non-standard attributes in the code. These are used by jsTemplate to select properties from the data we pass in. The jsselect attribute means we will iterate over the variable specified in it—in this case the $this variable. The jsvalues attribute can change properties of the element, and jscontent sets the text of the element. Now there is one more attribute that is not very commonly used in HTML, which is the contenteditable attribute. It makes it possible to edit the content even though it is not an input or textarea element, which is perfect for this specific use-case (we can style it just like we want to).

We will also need a text box for entering a new task, which will look like this:

<input type="text" id="add" placeholder="Write task and press enter..." />

Just a normal input element, but with the new HTML 5 attribute called placeholder. It shows the text in gray and when clicking the text box it disappears. Very cool feature, that previously required lots of scripts and styling.

Data storage in extensions

If you load the extension in Chrome it won’t really do much at all – even though we have made a template and included the necessary scripts. What we need to do is bind the data storage to the template system so that it has some data to play with. To store data in Chrome we can use two different systems, Local Storage or Web Database. The most common and easiest one is Local Storage and is also the one we will use here. It is simply an object that we can set properties on and they are automatically saved for us, even after browser restarts.

Remember that localStorage can only store strings, which means you have to store your objects in a JSON-string with JSON.stringify(object)

Before we get ahead of ourselves and hook up the template system (fun!), we will create a class for handling data storage. This will make it a lot easier to get, add and remove tasks from the storage.

TaskRepository = function() {
    this.init();
    return this;
};

TaskRepository.prototype = {
    tasks: [],
    index: 0,

    init: function() {
        this.load();
    },

    create: function(task) {
        if (task == null) throw new TypeError('Missing task parameter');

        task.id = this.index++;

        this.tasks.push(task);
        this.save();
    },

    // 'get' and 'remove' functions removed for brevity,
    // see ZIP-archive for full code sample.

    load: function() {
        this.tasks = (localStorage.tasks ? JSON.parse(localStorage.tasks) : []);
        this.index = localStorage.index ? localStorage.index : 0;
    },

    save: function() {
        localStorage.tasks = this.tasks ? JSON.stringify(this.tasks) : null;
        localStorage.index = this.index;
    }
};

That is quite a bit of code but quite simple at a closer look. Basically what it does is load data from localStorage in init() and puts it in a local variable, tasks, and each method uses that variable to query data. After editing data you will have to save the data back to localStorage with the save method. An index number variable is also increased every time you create a task – it is used as ID which makes it easier to get and remove a specific task.

Hook up the template system and display some data

We are now ready to display some data in our browser action popup. To do that we create a View class which will include all presentation logics like load, edit and remove. We initialize this class in the onload event (and of course, in the popup.html file).

View = function() { return this; }
View.prototype = {
    init: function() {
        // Manually create a task
        db.create({
            title: 'My first task is to make a task',
            completed: false,
            created: new Date()
        });
        
        this.renderView();
    },

    renderView: function() {
        // Tells jsTemplate to load our tasks and display them with our template
        jstProcess(new JsEvalContext(db.tasks), document.getElementById('results'));
    }
};

var db = new TaskRepository();
var view = new View();

window.onload = function() { view.init(); };

Round up

task If you reload the extension in Chrome you should see one task appearing in the list, and you should also notice that the layout is worse than the web was ‘99 (see image). There is no way to add a task either, nor remove or edit yet. In the next post we will fix all these points.

Download the Source

Download To-Do Extension Source (Part 2)

Read More »

Experimental APIs in Chrome Extensions - #1

Posted on 20 March 2010 and tagged with 0 comments

This post covers the new Chrome Extension API methods which are in development. Some of which are already in the dev release of Google Chrome (5.0.356.2). Most of these methods will most likely change before they are released in the stable channel, but it will give you an idea of what you can expect from the extension API in the future.

Enabling experimental APIs

To use the experimental methods you need to first turn them on in both Chrome and on per extension basis. Begin with adding --enable-experimental-extension-apis to the command arguments of Chrome. Next find the extension you want to try the experimental APIs on and add experimental to the permission section of the manifest file.

Clipboard (chrome.experimental.clipboard)

Copying text is already possible in Chrome by selecting text in a textarea and executing the execCommand("Copy") method. Though, this is quite a hacky way so it is great to know that this will now be offered through an API method. Methods for cutting and pasting data is also available.

Following is an example of usage:

// Select
document.getElementById('textarea1').select();

// Copy/Cut [executeCopy(tabid, callback)]
chrome.experimental.clipboard.executeCopy(1, function() {
    console.log('copied!');

    // Focus an element
    document.getElementById('textarea2').focus();

    // Paste [executePaste(tabid, callback)]
    chrome.experimental.clipboard.executePaste(1, function() {
        console.log('pasted!');
    });
});

What I am missing is a method where I can pass a string as an argument, which will be copied directly, without needing to select the text beforehand.

Infobar (chrome.experimental.infobars)

chrome-experimental-infobar2You are most likely familiar with infobars from when you login to a web page and Chrome asks if it should remember your login. Infobars can be used when you want to alert users of a completed action, or when you want to give user a choice to take an action (e.g. backup the extensions data).

Following is an example usage:

// Show an infobar [show(details, callback)]
chrome.experimental.infobars.show({ path: 'infobar.html' }, function(window) {
    console.log('Infobar showed, in window ', window);
});

It would be great if we got some default CSS styles for buttons and links for a consistent look, as currently we have to style them ourselves. I am pretty sure this is in the pipeline, though.

Idle (chrome.experimental.idle)

This API is quite interesting. It allows us to query which state (idle, active) Chrome currently is in. This can be useful when you want to run a certain process when Chrome gets in idle state, e.g. pause the music of a music player after being idle for 30 minutes.

Following is an example usage:

// Find out which state Chrome is in. [queryState(threshold, callback)]
chrome.experimental.idle.queryState(15, function(state) {
    console.log('Chrome is in ', state, ' state.');
});

// Event which fires when state changes
chrome.experimental.idle.onStateChanged.addListener(function(state) {
    console.log('Changed to ', state, ' state.');
});

Currently the documentation for this API is not ready (or the API itself for that matter) which means I don’t know which unit the threshold is in, but we can assume it is in seconds.

Stay up to date

As these experimental APIs changes a lot before they are released it is a good idea to check back at the documentation at Google Code regularly. You can also have a look at the latest trunk, more specifically the extension_api.json file, for the absolute latest APIs.

These were only some of the experimental APIs the extension API offers, for more a full documentation see the links above. I will also continue writing these type of posts for the experimental APIs in Chrome.

Read More »

To-Do Chrome Extension Part 1 – Project Setup

Posted on 7 March 2010 and tagged with 0 comments

This is part one of the series on making a to-do extension for Google Chrome.

In this series we will create a simple to-do extension where you can add tasks through a browser action popup. When you have completed a task, you should be able to remove it from the list. The browser action icon should display how many tasks you have left to complete. It will also feature a basic theme picker in the options page.

This series on Chrome Extensions is aimed towards beginners in extension developing but with basic experience in JavaScript and HTML.

Let’s start with creating a good folder structure for our extension. We know we will store images, HTML, CSS and JavaScript files. We might even use external JavaScript libraries like jQuery. Images, CSS and JavaScript files will go into their own separate folders. Our HTML files will stay in the root folder. This makes the following folder structure:

  • CSS
  • Images
  • JS 
    • Libs

Manifest File

After we have created these folders we will create a manifest.json file that holds metadata about our extension in JSON format. There are two required properties that you need to specify in the manifest file—name and version—but we will go ahead and add a few more that we need for our to-do extension.

Create a manifest.json file and fill in the following information.

{
	"name": "To Do",  
	"description": "A simple To Do extension.",
	"version": "1.0.0",
	"icons": { 
		"32": "images/icon32.png",
		"48": "images/icon48.png",
		"128": "images/icon128.png" 
	},
	"browser_action": {
		"default_icon": "images/icon19.png",
		"popup": "popup.html"
	},
	"options_page": "options.html"
}

That is quite a mouthful, but I will break it down row by row. The first three properties are quite self-explanatory so I will go directly to icons section. There are two required icon sizes that you should ship with your extension; these are 48x48 and 128x128 pixels. The 128 pixel icon will be displayed in the install dialog and the 48 pixel icon shows up on the extensions page. I also added a reference to a 32 pixel icon as that is the size the extensions gallery is using.

Next property is browser_action which defines the clickable icon next to the Omnibar in Google Chrome. It needs a default icon with size of 19 pixels. If you link to an image larger than 19 pixels it will be resized to fit. Popup property is the HTML file that will be shown when you click the browser action icon. We will simply call this file popup.html.

Lastly we define a page that will act as a options page, named options.html. Users will be able to access it from the options button at the extensions page.

The manifest file is finished for now, but we need to create a few files that we have referenced in the file.

Creating Necessary Files

Creating a clear and good looking icon is quite challenging so we can use one of the icon-search services available on the web. I’m usually using one called Iconfinder.net which feature a large library of icons and ability to filter on name and size. For our simple extension I found a good icon of which I then created four different sizes (19x19, 32x32, 48x48 and 128x128) and saved them in the images folder.

Now we need a couple of HTML files, namely popup.html and options.html, and their respective JavaScript and CSS files (with the same name). The HTML files should be saved in the root next to manifest.json, JavaScript files in the js folder, and CSS files in the css folder.

We will add some basic HTML 5 markup to the HTML files. Open both popup.html and options.html and paste the following code snippet in each file. Also change {name} to the filename in each file (e.g. “Popup” in popup.html).

<!DOCTYPE html>
<html>
<head>
    <title>{name} - To-Do</title>
    <link rel="stylesheet" href="css/{name}.css" type="text/css" />
    <script src="js/{name}.js" type="text/javascript"></script>
</head>
<body>
    <h1>{name}</h1>
</body>
</html>

Creating a file to keep up with version changes in your extension is good practice. I usually create a very basic JSON file called release-notes.json, where I store changes in the following format:

{
    "version": "1.0.0",
    "log":[
        "Initial release."
    ]
}

Copy the JSON data and paste it in a file called release-notes.json in the root folder. I will later in this series describe how you can display it in your extension’s options page.

Running the Extension

extension-loaded Finally we are ready to load the extension into Google Chrome to see if it works. You do this by navigating to chrome://extensions/. Turn developer mode on if it is off. Next click the Load unpacked extension… button. You will be presented with a folder browser. Find the folder you created for the extension, and press OK. The extension should now load and display in the list. A browser action icon should also be visible in the toolbar.

Of course there is no functionality at all in this version of the extension. We will work on that in the next part of this series.

Download the Source

Download To-Do Extension Source (Part 1)

Read More »

Loading Posts...