{"id":3995,"date":"2010-04-13T11:47:27","date_gmt":"2010-04-13T18:47:27","guid":{"rendered":"http:\/\/3.260"},"modified":"2012-04-12T19:36:52","modified_gmt":"2012-04-12T19:36:52","slug":"develop-with-jetpack-sdk-0-2","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/labs\/2010\/04\/develop-with-jetpack-sdk-0-2\/","title":{"rendered":"Beginning development with Jetpack SDK 0.2"},"content":{"rendered":"<p><em>This article is a translation of a <a href=\"http:\/\/www.xuldev.org\/blog\/?p=513\">recent article in Japanese<\/a> by fellow Jetpack Ambassador <a href=\"http:\/\/www.xuldev.org\/blog\/\">Gomita<\/a>. \u2014 <a href=\"http:\/\/mitcho.com\">mitcho<\/a><br \/>\n<\/em><\/p>\n<p>Mozilla Labs recently released <a href=\"http:\/\/mozillalabs.com\/jetpack\/2010\/03\/26\/announcing-jetpack-sdk-0-2\/\">version 0.2<\/a> of the Jetpack SDK, which fixes some issues of the 0.1 release such as a glitch regarding development with Windows. SDK 0.2 doesn&#8217;t include the planned APIs for rapid development of new browser functionality, but you can still play with SDK 0.2 to get a flavor for development with the Jetpack SDK.<\/p>\n<p>In this article we begin by setting up an SDK 0.2 development environment and explain the steps required to develop a simple, practical add-on using SDK 0.2. The instructions here are for Windows, but the basic steps are the same in every platform.<!--more--><\/p>\n<h3>Installing Python<\/h3>\n<p>The first step to using the Jetpack SDK is to install Python. How to install Python depends on your OS, but in Windows you can choose the &#8220;Python 2.6.5 Windows installer&#8221; from the <a href=\"http:\/\/www.python.org\/download\/\">Python<\/a> site and follow the installation wizard. Here, I&#8217;ll use C:Python26 as the installation path.<\/p>\n<p>After the install, you can activate the &#8220;python&#8221; command in your command line by adding &#8220;C:Python26&#8221; to the Windows &#8220;Path&#8221; preference. (If there is already another value, delimit with a semicolon: &#8220;;&#8221;.) Run the command &#8220;cmd&#8221; from the Start menu to start the command prompt and run &#8220;python -V&#8221; to confirm the Python version, &#8220;Python 2.6.2&#8221;:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">C:&gt;python -V\r\nPython 2.6.2\r\n<\/pre>\n<p>Note, the <a href=\"https:\/\/jetpack.mozillalabs.com\/sdk\/0.2\/docs\/#guide\/getting-started\">Jetpack SDK Docs<\/a> state that Python 2.5+ is required, but there seem to be some incompatibilities with Python 3.0.1 at this time. In addition, in my experience the SDK worked fine without the &#8220;Python for Windows extensions.&#8221;<\/p>\n<h3>Setting up the Jetpack SDK<\/h3>\n<p>Next, we set up the Jetpack SDK. Download the Jetpack SDK 0.2 package from the <a href=\"https:\/\/jetpack.mozillalabs.com\/\">Jetpack site<\/a>, unzip it, and place it somewhere convenient. Here, I used &#8220;C:jetpack-sdk-0.2&#8221;.<\/p>\n<p>To use the Jetpack SDK, it must be &#8220;activated.&#8221; From the command prompt, go to the Jetpack SDK folder and run &#8220;binactivate&#8221;:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">C:jetpack-sdk-0.2&gt;binactivate\r\nWelcome to the Jetpack SDK. Run 'cfx docs' for assistance.\r\n(C:jetpack-sdk-0.2) C:jetpack-sdk-0.2&gt;\r\n<\/pre>\n<p>Next, run &#8220;cfx docs&#8221; to open the SDK documentation in the browser. The SDK documentation starts a local server on port 8888.<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">(C:jetpack-sdk-0.2) C:jetpack-sdk-0.2&gt;cfx docs\r\nOne moment.\r\nOpening web browser to http:\/\/127.0.0.1:8888.\r\n<\/pre>\n<h3>The package directory structure<\/h3>\n<p>Addons built with the Jetpack SDK are called &#8220;packages.&#8221; Let&#8217;s try building a simple &#8220;hello world&#8221;-style package, but first let&#8217;s see what the final directory structure of this package will look like:<\/p>\n<table style=\"height: 279px;margin: 0pt 0pt 10px 80px\" width=\"450\">\n<tbody>\n<tr>\n<th><span style=\"float: left\">directory\/file<\/span><\/th>\n<th><span style=\"float: left\">Note<\/span><\/th>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px\" src=\"http:\/\/www.xuldev.org\/common\/folder.png\" alt=\"\u30d5\u30a9\u30eb\u30c0\" width=\"16\" height=\"16\" \/>jetpack-sdk-0.2<\/td>\n<td>the Jetpack SDK folder<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:21px\" src=\"http:\/\/www.xuldev.org\/common\/folder.png\" alt=\"\u30d5\u30a9\u30eb\u30c0\" width=\"16\" height=\"16\" \/>packages<\/td>\n<td>the main packages folder<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:37px\" src=\"http:\/\/www.xuldev.org\/common\/folder.png\" alt=\"\u30d5\u30a9\u30eb\u30c0\" width=\"16\" height=\"16\" \/>hello-world<\/td>\n<td>package root<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:53px\" src=\"http:\/\/www.xuldev.org\/common\/file.png\" alt=\"\u30d5\u30a1\u30a4\u30eb\" width=\"16\" height=\"16\" \/>package.json<\/td>\n<td>package manifest file<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:53px\" src=\"http:\/\/www.xuldev.org\/common\/file.png\" alt=\"\u30d5\u30a1\u30a4\u30eb\" width=\"16\" height=\"16\" \/>README.md<\/td>\n<td>package documentation<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:53px\" src=\"http:\/\/www.xuldev.org\/common\/folder.png\" alt=\"\u30d5\u30a9\u30eb\u30c0\" width=\"16\" height=\"16\" \/>lib<\/td>\n<td>the package code directory<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:69px\" src=\"http:\/\/www.xuldev.org\/common\/file.png\" alt=\"\u30d5\u30a1\u30a4\u30eb\" width=\"16\" height=\"16\" \/>main.js<\/td>\n<td>main program code<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"float: left;margin: 4px 5px;margin-left:69px\" src=\"http:\/\/www.xuldev.org\/common\/file.png\" alt=\"\u30d5\u30a1\u30a4\u30eb\" width=\"16\" height=\"16\" \/>simple-dialog.js<\/td>\n<td>a custom code library<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The package&#8217;s root directory is placed in the &#8220;packages&#8221; directory in the Jetpack SDK folder, and includes the &#8220;package.json&#8221; manifest file and the &#8220;README.md&#8221; documentation file. The &#8220;lib&#8221; folder includes the package&#8217;s main program code and any custom libraries used by our addon.<\/p>\n<h3>Creating the package<\/h3>\n<p>We begin by creating the &#8220;hello-world&#8221; directory in C:jetpack-sdk-0.2packages . Next the manifest file &#8220;package.json&#8221; is created. The manifest file includes metadata about our package in JSON format. If you&#8217;ve ever created a XUL-style addon before, you can think of this as similar to the &#8220;install.rdf&#8221; file. Here, I used the following as the manifest:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">{\r\n    \"id\": \"helloworld@xuldev.org\",\r\n    \"version\": \"0.1\",\r\n    \"description\": \"This is my first package.\",\r\n    \"author\": \"Gomita &lt;gomita@xuldev.org&gt;\"\r\n}\r\n<\/pre>\n<p>The &#8220;id&#8221; property is used as a unique ID for all addons including Jetpack packages and is often formatted as an email address. This corresponds to XUL-based addons&#8217; &lt;em:id&gt; tag.<\/p>\n<p>Next, reload the SDK documentation in the browser and confirm that &#8220;hello-world&#8221; shows up under &#8220;Package Reference.&#8221;<\/p>\n<h3>Writing the main code<\/h3>\n<p>The next step is to add some working code to the hello-world package. Create a &#8220;lib&#8221; folder under the package root and create a &#8220;main.js&#8221; under &#8220;lib&#8221; with the following code:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">exports.main = function(options, callbacks) {\r\n    console.log(\"Hello, World!\");\r\n};\r\n<\/pre>\n<p>The main program code is always loaded as a module called &#8220;main&#8221;. This &#8220;main&#8221; property is made accessible from outside code using the <a href=\"http:\/\/commonjs.org\/\">CommonJS<\/a>-style code &#8220;exports.main = &#8230;&#8221;. &#8220;console.log&#8221; is a global function made available by Jetpack and the SDK prints the string to the command prompt.<\/p>\n<p>It&#8217;s worth noting that, in the current Jetpack SDK, calling &#8220;<code>console.log(\"\u3053\u3093\u306b\u3061\u306f\");<\/code>&#8221; doesn&#8217;t yield the expected Japanese output. In the future such output will be handled through the planned <a href=\"https:\/\/wiki.mozilla.org\/Labs\/Jetpack\/Reboot\/JEP\/113\">localization API<\/a>.<\/p>\n<h3>Testing our package<\/h3>\n<p>With some simple code in our &#8220;main&#8221; function, it&#8217;s time to try this code out. To test this code, we run &#8220;cfx run -a firefox&#8221; in the command prompt. By running &#8220;cfx run&#8221; with the &#8220;-a firefox&#8221; option, we load our package into a brand new Firefox profile and launch Firefox.<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">(C:jetpack-sdk-0.2) C:jetpack-sdk-0.2&gt;cd packageshello-world\r\n\r\n(C:jetpack-sdk-0.2) C:jetpack-sdk-0.2packageshello-world&gt;cfx run -a firefox\r\ninfo: Hello, World!\r\nOK\r\nTotal time: 1.531000 seconds\r\nProgram terminated unsuccessfully.\r\n<\/pre>\n<p>After Firefox loads, confirm that the command prompt reads &#8220;info: Hello, World!&#8221; When you quit Firefox, the testing will end.<\/p>\n<h3>Using a standard library<\/h3>\n<p>Now we&#8217;ll edit our code to invoke the timer library which is one of the Jetpack SDK&#8217;s standard libraries. The timer library is a module which abstracts various timer-related functionality, similar to the DOM&#8217;s window.setTimeout, window.clearTimeout. Details on this library are available in the <a href=\"https:\/\/jetpack.mozillalabs.com\/sdk\/0.2\/docs\/#module\/jetpack-core\/timer\">SDK documentation<\/a>. Moreover, although not in the documentation, timer.setInterval and timer.clearInterval also work in this version.<\/p>\n<p>To use this library in our main program code, we first must invoke this library with the CommonJS require function. We modify the main.js file as follows:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">var timer = require(\"timer\");\r\nexports.main = function(options, callbacks) {\r\n    timer.setInterval(function() {\r\n        console.log(new Date().toLocaleTimeString());\r\n    }, 1000);\r\n};\r\n<\/pre>\n<p>After this change, run &#8220;cfx run -a firefox&#8221; in the command prompt to test it. Check to make sure that the current time is being printed to the command prompt once a second:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">(C:jetpack-sdk-0.2) C:jetpack-sdk-0.2packageshello-world&gt;cfx run -a firefox\r\ninfo: 10:37:21\r\ninfo: 10:37:22\r\ninfo: 10:37:23\r\ninfo: 10:37:24\r\ninfo: 10:37:25\r\n<\/pre>\n<h3>Creating a custom library<\/h3>\n<p>Next we&#8217;ll create a custom library to add some functionality not currently included in the Jetpack standard library. Implementing advanced functionality in add-ons, like filesystem access, involves using <a href=\"https:\/\/developer.mozilla.org\/en\/XPCOM\">XPCOM<\/a> components. Jetpack encourages seprarating the use of XPCOM components into separate modules which are then used by the main program code. The Jetpack SDK doesn&#8217;t currently disallow direct XPCOM access within Jetpack add-on code, but such a restriction is forthcoming. Modularizing XPCOM code into separate libraries now allow you to easily migrate to equivalent standard libraries in the future.<\/p>\n<p>Let&#8217;s create a simple-dialog library to display a modal dialog much like window.alert does. The Jetpack code&#8217;s runtime environment doesn&#8217;t include access to the regular window or document objects, so just calling window.alert doesn&#8217;t work. To create an alert from this context, we use the <a href=\"https:\/\/developer.mozilla.org\/en\/nsIPromptService\">nsIPromptService<\/a> XPCOM component. In our package&#8217;s lib folder, create a &#8220;simple-dialog.js&#8221; file. Just like our main program code, we implement this library as a CommonJS module using &#8220;exports.<em>methodname<\/em> = function(&#8230;){&#8230;}&#8221;.<\/p>\n<p>The simple-dialog library will have these two methods:<\/p>\n<table style=\"margin: 0 0 10px 80px\">\n<tbody>\n<tr>\n<th><span style=\"float: left\">Method<\/span><\/th>\n<th><span style=\"float: left\">Note<\/span><\/th>\n<\/tr>\n<tr>\n<td><code>alert(<em>text<\/em>)<\/code><\/td>\n<td>Displays an alert dialog with the string in <em>text<\/em> and an OK button. Equivalent to the DOM&#8217;s window.alert.<\/td>\n<\/tr>\n<tr>\n<td><code>confirmYesNo(<em>text<\/em>)<\/code><\/td>\n<td>Displays a confirmation dialog with the string in <em>text<\/em> and Yes and No buttons. The method returns true if the user presses &#8220;yes&#8221; and false if &#8220;no.&#8221;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Here is the code for simple-dialog.js:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">var promptSvc = Cc[\"@mozilla.org\/embedcomp\/prompt-service;1\"].\r\n                getService(Ci.nsIPromptService);\r\n\r\nexports.alert = function(text) {\r\n    promptSvc.alert(null, \"[Jetpack]\", text);\r\n};\r\n\r\nexports.confirmYesNo = function(text) {\r\n    var pos = promptSvc.confirmEx(\r\n        null, \"[Jetpack]\", text, promptSvc.STD_YES_NO_BUTTONS,\r\n        null, null, null, null, {}\r\n    );\r\n    return pos == 0;\r\n};\r\n<\/pre>\n<p>Lines 1-2 are for calling nsIPromptService. Note that Cc, Ci are aliases for Components.classes and Components.interfaces, respectively, and are made available by Jetpack as global variables. Lines 4-6 implement the alert method for showing alert dialogs using nsIPromptService&#8217;s alert method. Lines 8-14 implement simple-dialog&#8217;s confirmYesNo method using nsIPromptService&#8217;s confirmEx method to display the dialog with yes and no buttons. nsIPromptservice&#8217;s confirmEx method returns 0 if the user presses &#8220;yes&#8221; and 1 if &#8220;no&#8221;, so we modify this value and return it.<\/p>\n<h3>Using our custom library<\/h3>\n<p>Let&#8217;s call this new custom library from our main program code and verify that it works. Here&#8217;s our updated main.js file:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">var simpleDialog = require(\"simple-dialog\");\r\n\r\nexports.main = function(options, callbacks) {\r\n    var adult = simpleDialog.confirmYesNo(\"Are you over 18 years old?\");\r\n    if (adult) {\r\n        simpleDialog.alert(\"Welcome!\");\r\n    }\r\n    else {\r\n        simpleDialog.alert(\"Good bye!\");\r\n    }\r\n};\r\n<\/pre>\n<p>Run &#8220;cfx run -a firefox&#8221; and confirm that a confirmation dialog, as seen below, is displayed. Pressing &#8220;yes&#8221; and &#8220;no&#8221; should give you the appropriate alert dialogs as well.<\/p>\n<p><img decoding=\"async\" src=\"\/\/\/var\/folders\/Xr\/XrC2E9n7FcWD7pxxv8puwE+++TI\/-Tmp-\/com.apple.mail.drag-T0x10051fce0.tmp.Ppt6Wu\/confirmyesno-en.png\" alt=\"\" \/><\/p>\n<h3><img decoding=\"async\" src=\"\/\/\/Users\/mitcho\/Desktop\/confirmyesno-en.png\" alt=\"\" \/><\/h3>\n<h3><a href=\"http:\/\/mozillalabs.com\/jetpack\/files\/2010\/04\/confirmyesno-en.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-269\" src=\"http:\/\/mozillalabs.com\/jetpack\/files\/2010\/04\/confirmyesno-en-300x111.png\" alt=\"\" width=\"300\" height=\"111\" \/><\/a><\/h3>\n<h3>Implementing a network status observer<\/h3>\n<p>Now let&#8217;s use this hello-world package as a foundation for a more practical add-on. Using the <a href=\"https:\/\/jetpack.mozillalabs.com\/sdk\/0.2\/docs\/#module\/jetpack-core\/observer-service\">observer-service<\/a> module included with the Jetpack SDK, we can monitor Firefox&#8217;s online\/offline network status changes.<\/p>\n<p>Firefox internally broadcasts various application events to observers via the <a href=\"https:\/\/developer.mozilla.org\/ja\/NsIObserverService\">nsIObserverService<\/a> XPCOM component. When Firefox goes offline, a &#8220;network:offline-status-changed&#8221; notification is broadcast. To subscribe this notification and act on it, we use the observer-service library&#8217;s add method. add&#8217;s first argument is the name of the notification we want to subscribe to and the second argument is a callback function. The callback function is given two arguments, of which the second is a string equal to either &#8220;online&#8221; or &#8220;offline.&#8221; In our add-on, we&#8217;ll check this value and display an appropriate alert using simple-dialog.<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">var simpleDialog = require(\"simple-dialog\");\r\nvar observer = require(\"observer-service\");\r\n\r\nexports.main = function(options, callbacks) {\r\n    observer.add(\"network:offline-status-changed\", function(sbj, data) {\r\n        if (data == \"online\") {\r\n            simpleDialog.alert(\"Firefox is now online.\");\r\n        }\r\n        else if (data == \"offline\") {\r\n            simpleDialog.alert(\"Firefox is now offline.\");\r\n        }\r\n    });\r\n};\r\n<\/pre>\n<p>Launch Firefox by running &#8220;cfx run -a firefox&#8221; and then choose &#8220;File&#8221; &gt; &#8220;Work Offline&#8221; and you should get a notification:<\/p>\n<p><a href=\"http:\/\/mozillalabs.com\/jetpack\/files\/2010\/04\/offline-en.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-270\" src=\"http:\/\/mozillalabs.com\/jetpack\/files\/2010\/04\/offline-en-300x225.png\" alt=\"\" width=\"300\" height=\"225\" \/><\/a><\/p>\n<h3>Adding documentation<\/h3>\n<p>If you add documentation to a package, you can view it by clicking that package in the SDK Documentation. To add documentation, create a &#8220;README.md&#8221; file in the package root directory. &#8220;README.md&#8221; is written in Markdown format which looks like this:<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">This is my *first* package.\r\n\r\n* foo\r\n* bar\r\n* baz\r\n<\/pre>\n<p>Now if you load the SDK documentation using &#8220;cfx docs&#8221; and click on the &#8220;hello-world&#8221; link, you&#8217;ll see this documentation together with the package metadata.<\/p>\n<h3>Exporting an install package<\/h3>\n<p>Jetpack add-ons which are created in this way can then be exported into Firefox-standard XPI files. To export an XPI, go to the package&#8217;s root directory in the command prompt and run &#8220;cfx xpi&#8221;.<\/p>\n<pre style=\"margin: 12px 0 15px 80px\">(C:jetpack-sdk-0.2) C:jetpack-sdk-0.2packageshello-world&gt;cfx xpi\r\nExporting extension to hello-world.xpi.\r\n<\/pre>\n<p>This creates an XPI file called hello-world.xpi . Opening this file in any Firefox profile will let you install it using the regular add-on install mechanism.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article is a translation of a recent article in Japanese by fellow Jetpack Ambassador Gomita. \u2014 mitcho Mozilla Labs recently released version 0.2 of the Jetpack SDK, which fixes some issues of the 0.1 release such as a glitch &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/labs\/2010\/04\/develop-with-jetpack-sdk-0-2\/\">Continue reading<\/a><\/p>\n","protected":false},"author":456,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[588],"tags":[22915,19630,19655,4127],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/posts\/3995"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/users\/456"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/comments?post=3995"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/posts\/3995\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/media?parent=3995"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/categories?post=3995"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/labs\/wp-json\/wp\/v2\/tags?post=3995"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}