Google Chrome, Javascript, NodeJS, Opera

Video – Memory Management Masterclass with Addy Osmani

Really good video on managing memory using Google Chrome Dev Tools.
From YouTube:Published on Sep 2, 2014

Addy is a senior engineer on the Chrome web engineering team, focusing on tools to help improve developer productivity and satisfaction. He works on Polymer – a Web Component library, is the the lead engineer on Yeoman and Web Starter Kit and regularly writes about web application architecture and the front-end. Outside of Google, Addy enjoys both hacking on open-source projects like TodoMVC and Grunt-UnCSS. He has authored books on JavaScript design patterns and frameworks.

Efficient JavaScript webapps need to be fluid and fast. Any app with significant user interaction needs to consider how to effectively keep memory usage down because if too much is consumed, a page might be killed, forcing the user to reload it and cry in a corner. Automatic garbage collection isn’t a substitute for effective memory management, especially in large, long-running web apps. In this talk we’ll walk through how to master the Chrome DevTools for effective memory management. Learn how to tackle performance issues like memory leaks, frequent garbage collection pauses, and overall memory bloat that can really drag you down.


Advertisements
Standard
Browser Extension, Google Chrome, Javascript, Look-It-Up, Opera

Google Chrome and Opera Extension – Look-it-up, a Dictionary Context Menu Extension

Adds context menu item that looks up selected text on dictionary sites. You have the choice of dictionary.com, merriam-webster.com (dictionary and thesaurus), thefreedictionary.com, thesaurus.com, urbandictionary.com, wikipedia.org, wiktionary.org, and yourdictionary.com to look selected terms up with. There is an options page to enable or disable the different menu items included. This is actually a rewrite and improvement of an older version that looked up terms on dictionary.com exclusively. While using the old version, I eventually found myself in need of more options for this add-on so here it is.
 

manifest.json


{
  "name": "Look-it-up",
  "description": "Adds context menu item that looks up selected text on dictionary sites",
  "version": "0.2",
  "permissions": ["contextMenus"],
  "background": {
    "scripts": ["app.js"]
  },
  "options_page": "options.html",
  "icons" : {
        "16" : "book16.png",
        "32" : "book32.png",
        "48" : "book48.png",
        "128" : "book128.png"
    },
  "manifest_version": 2
}

app.js


/** app.js */
var defaultentries = [
	{
	"menu":"dictionary.com",
	"active":1
	},{
	"menu":"merriam-webster.com-dict",
	"active":1
	},{
	"menu":"merriam-webster.com-thes",
	"active":1
	},{
	"menu":"thefreedictionary.com",
	"active":1
	},{
	"menu":"thesaurus.com",
	"active":1
	},{
	"menu":"urbandictionary.com",
	"active":1
	},{
	"menu":"wikipedia.org",
	"active":1
	},{
	"menu":"wiktionary.org",
	"active":1
	},{
	"menu":"yourdictionary.com",
	"active":1
	},
];
var entries = {};
if (!localStorage.getItem("entries")) {
	localStorage.setItem("entries", JSON.stringify(defaultentries));
}
entries = JSON.parse(localStorage.getItem("entries"));
function isActive(menu){
	console.log("isActive");
	var elen = entries.length;
	console.assert(!elen == 0);
	for(i=0;i<elen;i++){
		if(entries[i]["menu"] == menu){
			console.log(entries[i]["menu"]);
			if(entries[i]["active"] == 1){
				return true;
			}else{
				return false;
			}
		}
	}
}
function setActive(menu){
	console.log("setActive");
	var elen = entries.length;
	console.log(menu);
	console.assert(!elen == 0);
	for(i=0;i<elen;i++){
		if(entries[i]["menu"] == menu){
			entries[i]["active"] = 1;
		}
	}
	localStorage.setItem("entries", JSON.stringify(entries));
}
function setInactive(menu){
	console.log("setInactive");
	var elen = entries.length;
	console.log(menu);
	console.assert(!elen == 0);
	for(i=0;i<elen;i++){
		if(entries[i]["menu"] == menu){
			entries[i]["active"] = 0;
		}
	}
	localStorage.setItem("entries", JSON.stringify(entries));
}
/* Create the context menu */
var tm = chrome.contextMenus.create({"title": "Look-it-up", "contexts":["selection"]});
if(isActive("dictionary.com")){
	chrome.contextMenus.create({"title": "dictionary.com", "contexts":["selection"], "parentId": tm, "onclick": lookItUp1});
}
function lookItUp1(i, t){
	var createProperties = {url: "http://www.dictionary.com/cgi-bin/dict.pl?term=" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("merriam-webster.com-dict")){
	chrome.contextMenus.create({"title": "merriam-webster.com-dict", "contexts":["selection"], "parentId": tm, "onclick": lookItUp2});
}
function lookItUp2(i, t){
	var createProperties = {url: "http://www.merriam-webster.com/dictionary/" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("merriam-webster.com-thes")){
	chrome.contextMenus.create({"title": "merriam-webster.com-thes", "contexts":["selection"], "parentId": tm, "onclick": lookItUp3});
}
function lookItUp3(i, t){
	var createProperties = {url: "http://www.merriam-webster.com/thesaurus/" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("thefreedictionary.com")){
	chrome.contextMenus.create({"title": "thefreedictionary.com", "contexts":["selection"], "parentId": tm, "onclick": lookItUp4});
}
function lookItUp4(i, t){
	var createProperties = {url: "http://www.thefreedictionary.com/" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("thesaurus.com")){
	chrome.contextMenus.create({"title": "thesaurus.com", "contexts":["selection"], "parentId": tm, "onclick": lookItUp5});
}
function lookItUp5(i, t){
	var createProperties = {url: "http://thesaurus.com/search?q=" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("urbandictionary.com")){
	chrome.contextMenus.create({"title": "urbandictionary.com", "contexts":["selection"], "parentId": tm, "onclick": lookItUp6});
}
function lookItUp6(i, t){
	var createProperties = {url: "http://www.urbandictionary.com/define.php?term=" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("wikipedia.org")){
	chrome.contextMenus.create({"title": "wikipedia.org", "contexts":["selection"], "parentId": tm, "onclick": lookItUp7});
}
function lookItUp7(i, t){
	var createProperties = {url: "http://en.wikipedia.org/wiki/" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("wiktionary.org")){
	chrome.contextMenus.create({"title": "wiktionary.org", "contexts":["selection"], "parentId": tm, "onclick": lookItUp8});
}
function lookItUp8(i, t){
	var createProperties = {url: "http://en.wiktionary.org/wiki/" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}
if(isActive("yourdictionary.com")){
	chrome.contextMenus.create({"title": "yourdictionary.com", "contexts":["selection"], "parentId": tm, "onclick": lookItUp9});
}
function lookItUp9(i, t){
	var createProperties = {url: "http://www.yourdictionary.com/" + encodeURIComponent(i.selectionText)};
	chrome.tabs.create(createProperties);
}

options.html


<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Look-it-up Options</title>
	<script type="text/javascript" src="options.js"></script>
</head>
<body>
<ul id="menu">
</ul>
<div class="summary">Changes will take place the next time you start your browser. 
Or if you don't want to close the browser you could uncheck and recheck the enabled box next to this extension on the Extensions tab page.</div>

</body>
</html>

options.js


window.onload = function () {
	ckb = chrome.extension.getBackgroundPage().entries;
	var list = document.getElementById("menu");
	for(i = 0; i < ckb.length; i++){
		console.log(ckb[i]["active"]);
		if(ckb[i]["active"] == 1){
			var li = document.createElement("li");
			var temp = "<input type=\"checkbox\" class=\"butt\" checked=\"true\" value=\"" + ckb[i]["menu"] + "\"><span>" + ckb[i]["menu"] + "</span></input>";
			
			li.innerHTML = temp;
			list.appendChild(li);
		}else{
			var li = document.createElement("li");
			var tempp = "<input type=\"checkbox\" class=\"butt\" value=\"" + ckb[i]["menu"] + "\"><span>" + ckb[i]["menu"] + "</span></input>";
			li.innerHTML = tempp;
			list.appendChild(li);
			
		}
	}
	as = document.getElementsByClassName("butt");
	for(i=0;i<as.length;i++){
		as[i].addEventListener("click",function(evt){
			if(evt.target.checked == true){
				
				chrome.extension.getBackgroundPage().setActive(evt.target.value);
			}else{
				chrome.extension.getBackgroundPage().setInactive(evt.target.value);
			}
		},false)
	}
}


Install Look-it-up Context Menu Extension in Chrome (Google Chrome Store)

Install Look-it-up Context Menu Extension in Opera (My Repository)

Standard
Browser Extension, Google Chrome, Javascript, Opera, Share-It

Google Chrome Extension Share-it New Feature Update – An Options Page

The Google Chrome extension Share-it now has an Options page that enables you to choose which menu items are visible. Users simply can check which menu items to use and which to hide on the options page instead of navigating a menu of irrelevant menu items that they do not use. Preferences in an add-on are stored within the localstorage object in the browser in order to persist across start ups and closings. Developing an options page for your add-on isn’t a very difficult task, however, there are a couple of coding issues  to be aware of.

Share-it context menu running in Google Chrome

Share-it Context Menu

Share-it context menu options page running in Google Chrome

Share-it Options Page

Opera Next Is In The Loop

Something else to note is that I now also test Share-it on Opera Next where it runs superbly. I plan on eventually submitting this to the Opera store for consumption . For now, however, there is a link to download the .nex file at the bottom of this post if you want to use in now. Opera Next is based on the open source Chromium browser which Google Chrome is also based on. The context menu API is basically the same throughout as it allows for a shared code-base for context menu add-on development.

Share-it Context Menu running on Opera Next

Share-it Context Menu running on Opera Next

Caveats Developing an Options page or Popup page

There are a couple of tricky issues you should keep in mind if you are developing an options page for your add-on. First relates to scripting in pages inside of your add-on. There can be no inline script in your pages. Just keep your JavaScript in a separate file and make sure you are careful with your scripting not to create any inline script. Usually this means listening for click events and handling them using the event itself (similar to the way you would do it with Actionscript and Flex if you’ve ever done any Flash coding). The second issue relates to message and object passing. The JavaScript executing in your page isn’t in global scope for security reasons. There’s a special function to call, chrome.extension.getBackgroundPage(), that you have to call in order to gain access to the background page (app.js in this add-on). If you run into problems developing your page you should inspect the running page itself. The generated background page inspector will not show errors for secondary parts of the add-on (remember, not in global scope). Check out the source code below if you’re curious.

Source Code:

manifest.json

{
  "name": "Share-it",
  "description": "Adds context menu items to share the current page on a number of websites",
  "version": "0.4",
  "permissions": ["contextMenus"],
  "background": {
    "scripts": ["app.js"]
  },
  "options_page": "options.html",
  "icons" : {
		"16" : "favicon.ico",
		"32" : "peopleicon32.png",
		"48" : "peopleicon48.png",
		"128" : "peopleicon128.png"
	},
  "manifest_version": 2
}

app.js

/** app.js */
var defaultentries = [
	{
	"menu":"facebook.com",
	"active":1
	},{
	"menu":"twitter.com",
	"active":1
	},{
	"menu":"plus.google.com",
	"active":1
	},{
	"menu":"linkedin.com",
	"active":1
	},{
	"menu":"digg.com",
	"active":1
	},{
	"menu":"stumbleupon.com",
	"active":1
	},{
	"menu":"tumblr.com",
	"active":1
	},{
	"menu":"friendfeed.com",
	"active":1
	},{
	"menu":"fark.com",
	"active":1
	},{
	"menu":"blinklist.com",
	"active":1
	},{
	"menu":"plurk.com",
	"active":1
	},
];
var entries = {};
if (!localStorage.getItem("entries")) {
	localStorage.setItem("entries", JSON.stringify(defaultentries));
}
entries = JSON.parse(localStorage.getItem("entries"));
function isActive(menu){
	console.log("isActive");
	var elen = entries.length;
	console.assert(!elen == 0);
	for(i=0;i<elen;i++){
		if(entries[i]["menu"] == menu){
			console.log(entries[i]["menu"]);
			if(entries[i]["active"] == 1){
				return true;
			}else{
				return false;
			}
		}
	}
}
function setActive(menu){
	console.log("setActive");
	var elen = entries.length;
	console.log(menu);
	console.assert(!elen == 0);
	for(i=0;i<elen;i++){
		if(entries[i]["menu"] == menu){
			entries[i]["active"] = 1;
		}
	}
	localStorage.setItem("entries", JSON.stringify(entries));
}
function setInactive(menu){
	console.log("setInactive");
	var elen = entries.length;
	console.log(menu);
	console.assert(!elen == 0);
	for(i=0;i<elen;i++){
		if(entries[i]["menu"] == menu){
			entries[i]["active"] = 0;
		}
	}
	localStorage.setItem("entries", JSON.stringify(entries));
}
/* Create the context menu */
var hp = chrome.contextMenus.create({"title": "Share-it"});

if(isActive("facebook.com")){
	chrome.contextMenus.create({"title": "facebook.com", "contexts":["page"], "parentId": hp, "onclick": facebookIt});
}
function facebookIt(i, t){
	var createProperties = {url: "http://www.facebook.com/sharer.php?u="+ encodeURI(t.url) + "&src=" + encodeURIComponent("Share-it")};
	chrome.tabs.create(createProperties);
}

if(isActive("twitter.com")){
	chrome.contextMenus.create({"title": "twitter.com", "contexts":["page"], "parentId": hp, "onclick": twitterIt});
}
function twitterIt(i, t){
	var createProperties = {url: "https://twitter.com/share?url="+ encodeURI(t.url) + "&text=" + encodeURIComponent(t.title)};
	chrome.tabs.create(createProperties);
}

if(isActive("plus.google.com")){
	chrome.contextMenus.create({"title": "plus.google.com", "contexts":["page"], "parentId": hp, "onclick": googleplusIt});
}
function googleplusIt(i, t){
	var createProperties = {url: "https://plusone.google.com/_/+1/confirm?hl=en&url="+ encodeURI(t.url)};
	chrome.tabs.create(createProperties);
}

if(isActive("linkedin.com")){
	chrome.contextMenus.create({"title": "linkedin.com", "contexts":["page"], "parentId": hp, "onclick": linkedinIt});
}
function linkedinIt(i, t){
	var createProperties = {url: "http://www.linkedin.com/cws/share?url="+ encodeURI(t.url)};
	chrome.tabs.create(createProperties);
}

if(isActive("digg.com")){
	chrome.contextMenus.create({"title": "digg.com", "contexts":["page"], "parentId": hp, "onclick": diggIt});
}
function diggIt(i, t){
	var createProperties = {url: "http://digg.com/submit?url="+ encodeURI(t.url) + "&title=" + encodeURIComponent(t.title)};
	chrome.tabs.create(createProperties);
}

if(isActive("stumbleupon.com")){
	chrome.contextMenus.create({"title": "stumbleupon.com", "contexts":["page"], "parentId": hp, "onclick": stumbleuponIt});
}
function stumbleuponIt(i, t){
	var createProperties = {url: "http://www.stumbleupon.com/submit?url="+ encodeURI(t.url)};
	chrome.tabs.create(createProperties);
}

if(isActive("tumblr.com")){
	chrome.contextMenus.create({"title": "tumblr.com", "contexts":["page"], "parentId": hp, "onclick": tumblrIt});
}
function tumblrIt(i, t){
	var createProperties = {url: "http://www.tumblr.com/share?v=3&u="+ encodeURI(t.url) + "&t=" + encodeURIComponent(t.title)};
	chrome.tabs.create(createProperties);
}

if(isActive("friendfeed.com")){
	chrome.contextMenus.create({"title": "friendfeed.com", "contexts":["page"], "parentId": hp, "onclick": friendfeedIt});
}
function friendfeedIt(i, t){
	var createProperties = {url: "http://www.friendfeed.com/share?url="+ encodeURI(t.url) + "&title=" + encodeURIComponent(t.title)};
	chrome.tabs.create(createProperties);
}

if(isActive("fark.com")){
	chrome.contextMenus.create({"title": "fark.com", "contexts":["page"], "parentId": hp, "onclick": farkIt});
}
function farkIt(i, t){
	var createProperties = {url: "http://cgi.fark.com/cgi/fark/farkit.pl?u="+ encodeURI(t.url) + "&h=" + encodeURIComponent(t.title)};
	chrome.tabs.create(createProperties);
}

if(isActive("blinklist.com")){
	chrome.contextMenus.create({"title": "blinklist.com", "contexts":["page"], "parentId": hp, "onclick": blinklistIt});
}
function blinklistIt(i, t){
	var createProperties = {url: "http://www.blinklist.com/index.php?Action=Blink/Addblink.php&Url="+ encodeURI(t.url) + "&Title=" + encodeURIComponent(t.title)};
	chrome.tabs.create(createProperties);
}

if(isActive("plurk.com")){
	chrome.contextMenus.create({"title": "plurk.com", "contexts":["page"], "parentId": hp, "onclick": plurkIt});
}
function plurkIt(i, t){
	var createProperties = {url: "http://plurk.com/?qualifier=shares&status=" + encodeURIComponent(t.title) + "%20%2D%20" + encodeURI(t.url)};
	chrome.tabs.create(createProperties);
}

options.html


<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Share-it Options</title>
	<script type="text/javascript" src="options.js"></script>
</head>
<body>
<ul id="menu"></ul>
<div class="summary">Changes will take place the next time you start your browser.
Or if you don't want to close the browser you could uncheck and recheck the enabled box next to this extension on the Extensions tab page.</div>
</body>
</html>

options.js


window.onload = function () {
	ckb = chrome.extension.getBackgroundPage().entries;
	var list = document.getElementById("menu");
	for(i = 0; i < ckb.length; i++){
		console.log(ckb[i]["active"]);
		if(ckb[i]["active"] == 1){
			var li = document.createElement("li");
			var temp = "<input class="\&quot;butt\&quot;" type="\&quot;checkbox\&quot;" checked="checked" value="\&quot;&quot;" />" + ckb[i]["menu"] + "";

			li.innerHTML = temp;
			list.appendChild(li);
		}else{
			var li = document.createElement("li");
			var tempp = "<input class="\&quot;butt\&quot;" type="\&quot;checkbox\&quot;" value="\&quot;&quot;" />" + ckb[i]["menu"] + "";
			li.innerHTML = tempp;
			list.appendChild(li);

		}
	}
	as = document.getElementsByClassName("butt");
	for(i=0;i<as.length;i++){
		as[i].addEventListener("click",function(evt){
			if(evt.target.checked == true){

				chrome.extension.getBackgroundPage().setActive(evt.target.value);
			}else{
				chrome.extension.getBackgroundPage().setInactive(evt.target.value);
			}
		},false)
	}
}

Version 0.5 update – fixed the graphics display in newest Chrome browser

If you downloaded an extension from somewhere other than the Google Store (or the Opera Store), Chrome will not let you install it on the fly (Opera will install it, however, you’ll have to go to the extensions page to enable it). You have to go to the browser menu and choose Tools->Extensions to open up the Extensions tab. Then drag the extension from wherever it is on the file-system to the open Extensions page. This should start the install process. If you ever feel funny about a packed extension that was downloaded you can unzip them to inspect the code (if you don’t have 7zip you’ll have to change the file extension to .zip – do it on a copy).

Related:

Chrome Browser:

Opera Next Extension Development:

References From Google Developer:

Standard
Google Chrome, Opera

The New Face of Opera, Google’s Chromium Project

The next version of Opera will be wearing a new face of sorts, it will have Google Chromium at its core. Opera has adopted Google’s Chromium project, the open source version of their proprietary browser Google Chrome,  to be the nucleus of all future versions of the popular browser. The new version, aptly named Opera Next (or Opera 15), is already available for perusal (http://www.opera.com/developer/next). Although available, this version is still under development, so don’t expect to wowed if you are a Google Chrome user as it still rides like plain old Chromium. With updates promised on a bi-weekly basis, it is still uncertain whether legacy users will feel at home using the new version of browser.

Opera Next Speed Dial News

Under perpetual development at this stage, Opera’s legacy features are regularly being integrated. So far, Opera’s unique Internet compression system (renamed from Turbo to Off-Road mode), has been integrated thus providing a faster Internet experience when your connection slows down. Opera Link, Opera’s bookmarking system, isn’t integrated as of the time of this writing which forces users of the new version to use the web interface while still under development. Opera Speed Dial, however, has a new face that has three new states that the old version didn’t have before; one of which allows you to ‘stash’ links you come across instead of bookmarking them (for now at least). The legacy Opera Mail feature has been broken off into a new separate application ( http://www.opera.com/computer/mail ), which contains all of the legacy mail operations and features.

Opera Link on Opera Next

Opera Speed Dial Stash Graphical Mode

Opera Next Speed Dial Stash Link Mode

Extensions

What does this mean for legacy add-on developers? The new core means studying how Chrome extensions are built. Opera Next will run two kinds of extensions: a limited subset of Google’s .crx extensions and Opera’s Navigator Extensions (.nex). Opera’s extension developer documentation has already been updated (http://dev.opera.com/extension-docs/index.html) for the next version. Once you take a gander at the documentation you’ll realize that most of the API is derived from Chrome’s. There are, however, large differences that should be noted. For instance, my post on Google Chrome context menu development will also apply to Opera Next context menu development – but anything I wrote concerning Omnibox extension development will not work for Opera Next as Opera Next does not have an Omnibox API yet (and may never have one). Opera Next uses only a subset of Chrome’s API which will most likely be expanded upon by the Opera team in the form of the opr prefix.

Little by little the new version of Opera is taking on a new personality that may or may not  become popular with legacy users. Only time and good development will tell. In my opinion – so far so good.

RELATED:

Standard
Javascript, Opera

Opera Unite Programming from a Newbie Perspective

Opera Unite is a personal web server platform provided by the Opera browser. To me, Opera Unite is a JavaScript tinkerers platform that is perfect for experimenting with different ideas I may come up with. The Opera web site gives the newbie a list of articles that introduces the different parts of the platform and libraries used to develop an Opera Unite program (http://dev.opera.com/articles/unite/). This article is meant as an introduction to the platform from a newcomers point of view. You should download and install the Opera browser from http://www.opera.com/. There are versions for virtually every popular operating system in use today.

What is Opera Unite?

Opera Unite is a Web Server service that lives inside of the Opera browser. Opera provides your browser with a web address when you enable Unite services in the browser. All calls to that web address are forwarded through the Opera web site to your Unite service through a special service proxy.  People call the address Opera provides you with to run an Opera Unite program installed in your browser. When you are not online, or when your Opera Unite service is not running users that call your web address are forwarded to your Opera Unite profile page.

How Do I Enable Opera Unite?

To enable the Unite service, click the Panels button in the lower left corner of the Opera browser, a toolbar should open up on the left side of the screen. Click the third button down from the top which is the Unite button. The Unite service panel should be open. Click the start button on the top of the panel and follow the directions. The Unite service comes with six apps by default (you should see them listed).

Programming for the Opera Unite Service

In order to program anything for Opera Unite you must have a basic working knowledge of JavaScript. If you are new to it there are plenty of tutorials out there on the Internet. JavaScript is not difficult to learn even though it may seem the opposite from looking at somebody else’s code sometimes. Similar to C++, JavaScript allows a programmer to abstract the abstracted abstraction. In other words, it may seem difficult simply because the script is some other programmers interpretation of something. The basic syntax of JavaScript is always the same, no matter how someone else structures or abstracts something.

(At this point I am assuming you know a bit of JavaScript and you want to get started)

The Structure and Anatomy of an Opera Unite Application

The organization of an Opera Unite application is a simple folder structure containing the files and scripts that your application will use zipped together (with the .zip file extension changed to .ua). There is an xml configuration file that tells Unite the name, path, author, among other things. Also, there is a html index file that acts strictly as a script loader (if you have ever scripted Spartan html this concept shouldn’t be new to you). Other than that there are the scripts that get executed which usually consist of a main.js and library files that you plan to utilize.


  • someuniteapp.ua
    • -scriptsfolder
      • –main.js
    • -index.html
    • -config.xml

    If this is your first time experimenting with Unite development on a Windows system there is something that you must do before you get started. Windows by default hides all known file extensions in Windows Explorer. You must change this in order to be able to change the extension of the zip files you create (so Unite will recognize it as an application). Do this by going to Folder Options in your Windows Explorer Tools menu and choose the View tab. Turn off the check box labeled Hide extensions for known file types. Click the OK button and you’re done. Keep in mind that with this option turned off you rename file types as well as their names (you’ll have to be careful not to change or erase the extension while renaming anything else on your system)(or you could change the setting back when you are finished).

    Config File

    Before anything you should create the configuration file. This is a short xml file that describes your program to Opera:

    
    <?xml version="1.0" encoding="utf-8"?>
    <widget webserver="yes" file="yes" network="private public" >
    <widgetname>YourOperaUniteApplicationName</widgetname>
    <description>A description of your Opera Unite application.</description>
    <author>
    <name>Your Name Goes Here</name>
    <organisation>If you‘re part of a company it’s name goes here</organisation>
    </author>
    <feature name="<a href="http://xmlns.opera.com/webserver&quot;">http://xmlns.opera.com/webserver"</a>>
    <param name="type" value="service"/>
    <param name="servicepath" value="thepaththatyouwanttouse"/>
    </feature>
    <feature name="<a href="http://xmlns.opera.com/fileio&quot;">http://xmlns.opera.com/fileio"</a>></feature>
    </widget>
    
    

    Opera based their Unite feature on their already established widget system which also uses xml configuration files. The file is pretty basic as you can see. The important fields are the application name and description, the author name, and the servicepath field. The service path field is the actual path address for the application that comes after your top level Opera Unite url. Features that are part of Opera’s xml namespace get a feature tag with an address. In the example above I have included the FileIO xml tag for the FileIO feature (you need it to use templates or write to a data file, so I included it).

    Dummy Index File

    The next file is the dummy index.html file. All that file does is call the scripts that are included and ran when you get a visitor. It is not meant to be a home page or anything like that. You create the home page from your script file (you can use a html template file for your html). It could be a blank page template or even just the script tags that include your scripts. Any library (server-side) you plan to use is included here. There is also a loader script library that you can use as well. To me, its just seems easier to use the standard html script tags to include my server-side scripting files:

    
    <!doctype html>
    <html>
    <head>
    <title></title>
    <script src="scripts/template/template.js"></script>
    <script src="scripts/main.js"></script>
    </head>
    <body>
    </body>
    </html>
    
    
    Scripts

    The next thing that you should do is create your scripts folder. In it you will include you main.js along with the libraries you will use in subfolders. Opera has a very good set of libraries to get you started on the server side( http://dev.opera.com/libraries/unite/ ). Keep in mind that you can write your own libraries if you please. The platform is completely extensible in that regard.

    Your main.js file is meant to be the heart of your Opera Unite application. All page requests are answered and processed in this file. Basically, what you do is set up listeners that listen for a ‘page’ request. That listener function will call a specified function (that you write) that will process the request and return a page to the client.  You can write the most Spartan system possible (such as the blog application explained in the developers primer article that concatenates the html response together in a string variable) or you can use the markuper template library – or even roll your own. It really doesn’t matter how you put the response together just as long as what is returned to the client is valid.
    A really simplified example would be (no template):

    // main.js
    var webserver;
    window.onload = function () {
    webserver = opera.io.webserver
    if (webserver)
    {
    // page requests are listened for here (_index is the default listener for the app)</code><code>
    webserver.addEventListener('_index', myHomePage, false);
    }
    }
    function myHomePage(e)
    {
    // set up the response object here
    var response = e.connection.response;
    // page variable
    var pg = “<html><head><title>Spartan Response</title></head><body>Hello simple example</body></html>”;
    // now send it to the client
    response.write(pg);
    // done now close the connection
    response.close();
    }

    Granted, this example really doesn’t do anything special; but it does give the very basic structure of the whole request process. Basically, listeners are set up that call handler functions that handle requests. Response objects are created through the WebServerRequestEvent object that is passed to the handler function (e in the case above) and written out to the client using the write() function of that response object.

    Other Various Files

    Other files that are included would be the icon files that you point to in your configuration file (they’re not really needed but that is where you would include them). Other graphics that your application would use could go in an images folder. Cascading Style Sheets used by your application could go in another folder.

    Note: Opera Unite applications are dependent upon your connection UPLOAD speed. Big images and files could slow down your application drastically. If your application is image laden your best bet would be to upload them to a service such as flickr and call them from within the client html. The same goes for scripting libraries used inside the client html. You could call libraries from google and save the bandwidth to make your application more responsive. However, any libraries you use on the server side should be included in the .ua file.

    Testing

    Opera has to approve any program that they include on their site. However, in order to test something you write you’ll have to install the program off of your hard drive. To do that you zip the files together, rename the file to have a .ua extension instead of a .zip extension (Windows users may have to change the Show File extensions for all Known File Types Windows Explorer setting in order to do this), and drag it to the browser window. This should start the installation process. Keep in mind that you have to have the Unite service already running to install anything from your hard drive.

    Troubleshooting

    If you are having problems installing your application there are two things to check first. Firstly, check and make sure that the Opera Unite service is currently running when you drag your application to the browser window. Secondly, check and make sure that the index.html and the config file is at the root of the .ua (zip) file. Some compression programs will include the folder as the top level – which is what you don’t want (Opera will not recognize it as an application). Simply select all of the files enclosed (instead of right clicking on the folder) in the app folder and add them that way. Other than that – check your script for mistakes.

    In Conclusion

    While this has been a rudimentary look at how an Opera Unite application is constructed I tried to cover the most important parts plainly and blatantly. You can see how requests and responses are handled and how the basic structure of an application comes together. Opera Unite applications can be as simple or as complex as you want them to be.

    From here you could utilize functionality in your application by learning how to use the different Unite libraries in your scripts (the two most prevalent would be Markuper and Yusef Opera’s template and server libraries).

    Reading

Happy Tinkering!

Standard