Ext JS: Nonblocking Notifications (Steam-style)

Permanent Link: Ext JS: Nonblocking Notifications (Steam-style) 17. November 2010 Comment No Comment

If you want to notify the user about something in Ext.JS, you do only have the MessageBox (at least I don't know any other possibility). The only disadvantage of MessageBox is that it's in front of any other element and usually has to be clicked away manually. Since we wanted to notify our users of things without them having to click away the notification and without it blocking the rest of the application, I wrote a Notification Extension which allows you to easily create Notifications on the bottom right of the browser window. The notifications look like this:

A single notification in application context

To be able to create those notifications, you need to add some CSS first:

.ux-chip-notification {
position: absolute;
right: 10px;
bottom: 10px;
background: #CEDFF5;
z-index: 1001;
border: 1px solid #99BBE8;
}

Second you need to add 2 JavaScript files for the 2 components Ext.ux.Chip.Notification and Ext.ux.Chip.NotificationMgr. The first component is the Notification itself, the NotificationMgr manages all your notifications, so that your notifications are displayed above each other in case you create more than one notification (see image later on):

Notification.js (Ext.ux.Chip.Notification)
NotificatioMgr.js (Ext.ux.Chip.NotificationMgr)

Once you have added those 3, you can simply create a notification with the following code:

new Ext.ux.Chip.Notification({
title: 'Something changed',
text: 'This notification is to inform you that something has changed. I will autoclose in 10 seconds.',
autoClose: 10000
});

The parameter autoClose is optional and it defines the time in milliseconds after which the notification is closed automatically. If you leave out the autoClose, the notification has to be closed manually by clicking on it. Right now the notification looks like this:

A single notification with autoClose

The second example has no autoClose, meaning the notification has to be closed manually. Additionally it has an icon:

new Ext.ux.Chip.Notification({
title: 'Something different changed',
icon: 'images/award_star_gold_2.png',
text: 'This notification is to inform you that something different has changed. ' +
'I will not autoclose, but I have an icon! If you click me I will close.'
});

The icon is always shown on the right side of the notification title:

A single notification without autoClose, with icon

Last, if for some reason you want to change the behaviour, when the user clicks the notification (remember: by default the notification is closed when clicking it), you can do it by simply overriding the handler:

new Ext.ux.Chip.Notification({
title: 'Unclosable notification',
icon: 'images/award_star_gold_2.png',
text: 'If you click me, I will not close, instead my text will change',
handler: function() {
this.setText('See? My text has changed!');
}
});

If you have created all 3 notifications at the same time, the notifications now look like this:

3 notifications

If you close one of these notifications (or when they are auto-closed) the notifications above it will be automatically re-placed, so that there are no holes between the notifications. This is managed by the NotificationMgr.

Ext.JS: Global shortcuts (e.g. close current tab)

Permanent Link: Ext.JS: Global shortcuts (e.g. close current tab) 17. März 2010 Comment No Comment

Ext.JS has an easy way of configuring keyboard shortcuts using Ext.KeyMap. If you wanna use global shortcuts you simply have to bind it to document. I wanna show you in an example how you can bind Alt+X to closing the current tab of a TabPanel layout accessible by the JavaScript variable tabPanelLayout.

new Ext.KeyMap(
document,
[
{
// Alt + X: Close current tab
key: 'x',
alt: true,
// Prevent any browser actions triggered by the shortcut that may occur
stopEvent: true,
fn: function() {
var activePanel = tabPanelLayout.getActiveTab();
Layout.closeTab(activePanel);
}
}
]
);

That's about it! If you only have this one shortcut, you can leave out the JS array.

OOP in JavaScript - Part 4: Scoping

Permanent Link: OOP in JavaScript - Part 4: Scoping 21. Dezember 2009 Comment No Comment

I agree, it took me a while to write part 4 of my OOP in JavaScript series, but here it is now. As for the whole series, basic JavaScript knowledge is needed.

Scoping together with OOP is always an issue, when using asynchronous calls combined with callbacks. Let's create a very easy example where one method of a class registers an onclick EventListener and the second method of the class is the callback for the EventListener. The second method simply calls an alert with a message text defined in the class. Note that the Event Listener registration is not cross-browser safe:

function Handler()
{
}

Handler.prototype.messageText = 'You have clicked somewhere in the document';

Handler.prototype.registerAll = function()
{
window.addEventListener('click', this.handleOnclick, true);
}

Handler.prototype.handleOnclick = function(event)
{
alert(this.messageText);
}

var handler = new Handler();
handler.registerAll();

When clicking anywhere in the document you'd be expecting to see a message box "You have clicked somewhere in the document", but you only get a mesage box saying "undefined". Why is that?

The second argument of addEventListener() is the callback, it expects a function. By putting this.handleOnclick as callback, you are passing a copy of that function. It would be same if you had written it like this:

window.addEventListener('click', function() { alert(this.messageText); }, true);

As you can clearly see now the function stands for itself, this is not in the context(=scope) of the class Handler. In order to achieve exactly that, JavaScript gives us 2 possibilities: apply() and call(). Basically they are both the same, the only difference is in the way you pass further arguments. For a closer distinction, take a look at this page.

By passing an instance as first argument we are telling the function that is called in which scope to be executed, meaning: What instance is this? Let's alter the registerAll method of the example:

Handler.prototype.registerAll = function()
{
window.addEventListener('click', this.handleOnclick.apply(this), true);
}

When running your page now, you will notice that the alert puts out the right message, but is called the moment your page is rendered and the onclick event doesn't work anymore. This is because apply() and call() directly call the respecting function.

What we need to do, is to dynamically create a function that is called with the right scope when needed.

function Handler()
{
}

Handler.prototype.messageText = 'You have clicked somewhere in the document';

Handler.prototype.registerAll = function()
{
window.addEventListener('click', this.createOnclickHandler(), true);
}

Handler.prototype.createOnclickHandler = function()
{
var myScope = this;
return function(event) {
var handle = function(event) {
alert(this.messageText);
}
handle.call(myScope, event);
}
}

var handler = new Handler();
handler.registerAll();

Finally we get to see a "You have clicked somewhere in the document" message box only when clicking somwhere in the document. Note that the second parameter of call() is the Event Object, which is passed as first argument to the called function.

If using this is not total must for you, you can also throw away one of the functions and simply use myScope instead of this:

Handler.prototype.createOnclickHandler = function()
{
var myScope = this;
return function(event) {
alert(myScope.messageText);
}
}

OOP in JavaScript - Part 3: Extending classes

Permanent Link: OOP in JavaScript - Part 3: Extending classes 22. September 2009 Comment No Comment

After I've already covered normal and static classes, Part 3 is about extending classes. As before, basic knowledge in OOP and JavaScript is needed.

First, let's create a class Human with setters and getters for the age:

function Human()
{

}

Human.prototype._age = 0;

Human.prototype.setAge = function(age)
{
if (typeof age != 'number') {
throw('Invalid Age provided');
}
this._age = age;
};

Human.prototype.getAge = function()
{
return this._age;
};

Since there are no keywords like class, private, public or protected in Javascript it's no surprise there's no keyword extends aswell. In order to extend a class you have to use prototyping:

function Woman(age)
{
    this.setAge(age);
}

Woman.prototype = new Human();

Now we've created a new class Woman that inherits all methods and properties from Human by assigning Woman's prototype to new Human(). To show it's easily possible, I've also changed the constructor as it now takes age as parameter. Creating a new object Woman and reading the age looks like this:

var woman = new Woman(22);
alert(woman.getAge());

You can overload methods simply by redifining them. In this case we'll overload the getAge method where we'll add an additional check and then call the parent getAge method. Since you actually don't call the parent method but call the parent method in static way, it is important to add call(this) in order to call the method in the actual context.

Woman.prototype.getAge = function()
{
if (this._age > 100) {
throw('This person is probably already dead!');
}
return Human.prototype.getAge.call(this);
};

Multiple inheritance is possible, but not very easy. For a method using Swiss Inheritance, check this page. Extending static classes might be possible, but I don't know a way. Problem is you need to clone the parent class (which is not that easy in JavaScript), if you write it like this the child becomes the parent and the other way round, meaning if you add a method in the Child it is also available in the parent:

function Cookie()
{

}

Cookie.get = function()
{
return 'something';
};

var CookieChild = Cookie;
alert(CookieChild.get());

Part 4 (probably the last one of the series for now) will cover scoping.

OOP in JavaScript - Part 2: Static classes and Singleton

Permanent Link: OOP in JavaScript - Part 2: Static classes and Singleton 15. September 2009 Comment No Comment

After I've covered normal classes in Part 1 of the series, the second part will focus on static classes and singletons. As before basic OOP and JavaScript knowledge is needed.

Generally writing and using static classes is monstly the same as normal classes. The difference is that you don't need to instantiate the object with new and you can't use prototyping nor this, since everything is in static context. One useful implementation for static classes is the Registry Design Pattern. First off we create the class with a property to store the registry values in afterwards:

function Registry()
{
}

Registry._data = {};

Right now, I can't really do a lot with this class, that's why I'll create a static method that allows me to set key-value-combinations:

Registry.set = function(key, value) {
Registry._data[key] = value;
}

Now I'm able to register stuff in the registry. Since I don't need an instance of the class I can call my method directly:

Registry.set('userIsAuthorized', false);

I'll leave out the getter you'd still need for a useful registry.

If you want to prevent anyone from writing new Registry() you only have to throw an exception in the constructor:

function Registry()
{
throw('You cannot instantiate Registry');
}

Maybe for some reason you wanna use Registry as a singleton. In that case you have to combine normal class methods and properties with static ones. You write the Registry as a normal class and implement one static property instance and one static method getInstance. The Registry class would then look like this:

function Registry()
{
}

Registry.instance = null;

Registry.getInstance = function() {
if (Registry.instance === null) {
Registry.instance = new Registry();
}
return Registry.instance;
}

Registry.prototype._data = {};

Registry.prototype.set = function(key, value) {
this._data[key] = value;
}

Since JavaScript doesn't have access modifiers like private, public or protected I had to remove the exception from the constructor. You could do it with a randomly generated token passed to the constructor, but this method is not bulletproof like any other I can think of.

In part 3 (that'll come later this week) I will write about extending classes.

OOP in JavaScript - Part 1: Writing classes

Permanent Link: OOP in JavaScript - Part 1: Writing classes 12. September 2009 Comment No Comment

OOP in JavaScript works a bit different than known from other languages. That's why I decided to do a series of "OOP in JavaScript" blogposts, where I want to show how OOP in JavaScript works. The series will discuss topics like creating classes, prototyping, extending classes, static classes, scoping, and so on. To understand these postings a basic knowledge of JavaScript and OOP is needed. This first part is about creating classes/prototyping.

First thing you need to know: There is now keyword class in JavaScript. In order to create a class in JavaScript, all you have to do is to create a function and then create a new class instance by using the known keyword new:

function Product() {}

var product = new Product();

The function we've created is the constructor of our new class. To create methods and properties there are two ways: You can either declare them in the constructor or you can do prototyping.

Declaring it in the constructor:

function Product() {
this.id = 71288;

this.getId = function() {
return this.id;
}
}

Prototyping:

function Product() {}

Product.prototype.id = 71288;

Product.prototype.getId = function() {
return this.id;
}

Personally I prefer prototyping as it is more clear. If you want to call the function getId() it always works the same way with both methods:

var product = new Product();
alert(product.getId());

This class is still quite static, since the id is hardcoded in it. To make it dynamic I just need to modify the constructor to make it accept a config object. Notice that I replaced the id property with a config property that stores the whole config given to the object.

function Product(config) {
this.parseConfig(config);
}

Product.prototype.config = {};

Product.prototype.parseConfig = function(config) {
if (typeof config != 'object') {
throw('Parameter "config" must be of type object');
}
// ID should always be set and a number
if (typeof config.id != 'number') {
throw('Config Parameter config.id must be a number');
}
this.config = config;
}

Product.prototype.getId = function() {
return this.config.id;
}

As you can see I also already implemented checks that the config must always be an object and that an id must always be set and a number. Creating a new instance of the class now looks like this:

var product = new Product({
id: 71288
});

This way we can now dynamically create Product Objects based on Product IDs we read from e.g. a database.

In the second part, that will come within the next few days I will write about static classes.

Cookies in JavaScript

Permanent Link: Cookies in JavaScript 29. Juli 2009 Comment No Comment

Using in Cookies is rather complicated. All the cookies are in the string document.cookie. Here's a little static class I wrote a while ago to handle saving, reading and removing Cookies in JavaScript

/**
* Static Class for Cookie functions
*/
function Cookie()
{
}

/**
* Save new Cookie
*
* @param string name
* @param string value
* @param number days
*/
Cookie.save = function(name, value, days)
{
if (typeof days != 'undefined') {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
var expires = "; expires=" + date.toGMTString();
} else {
var expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
}

/**
* Read Cookie value
*
* @param string name
*/
Cookie.get = function(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') {
c = c.substring(1,c.length);
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
}

/**
* Delete Cookie
*
* @param string name
*/
Cookie.remove = function(name) {
Cookie.save(name,"",-1);
}

invalid XML tag syntax

Permanent Link: invalid XML tag syntax 6. Mai 2009 Comment No Comment

If you should come across this error message in your JavaScript console and the error is thrown in a JavaScript file that is included via <script src="js/somefile.js" type="text/javascript">: Check that there are no script Tags in your .js file

Useful Firefox addons for web developers

Permanent Link: Useful Firefox addons for web developers 9. Februar 2009 Comment No Comment

Here's a short list of useful Firefox addons for web developers:

Firebug

Probably the most useful addon around. You cannot only change the whole page on the fly (HTML and CSS) but you also have the very useful Javascript console which can even be used by your application for debugging. Furthermore you can see all AJAX activities. Download at https://addons.mozilla.org/de/firefox/addon/1843

FirePHP

FirePHP allows you to send debug messages to Firebug through a PHP Script. Download at https://addons.mozilla.org/de/firefox/addon/6149

Greasemonkey

Greasemonkey allows you to create JavaScript scripts for specific or all websites (Use of wildcard * possible!). This can be very useful to either test how new scripts would integrate into your website or to change the look and behaviour of any other webseite. Download at https://addons.mozilla.org/de/firefox/addon/748

Stylish

Same as Greasemonkey only for CSS. This way you can easily test new styles on a website before integrating them. Download at https://addons.mozilla.org/de/firefox/addon/2108

Web Developer

The classic one. Allows you to easily change behaviour of your browser (caching, JavaScript, etc.) and has a whole lot of other useful tools like showing document size, the styles, submitting the page to the W3C Validator, Browserframe resize (to test your application for specific resolutions), viewing response headers, outlining specific elements and so on. Download at https://addons.mozilla.org/de/firefox/addon/60

Search Status

Although it also shows the page rank (not very reliable), I only use this addon to display all the nofollow links on a page, which is really extremely useful. Download at https://addons.mozilla.org/de/firefox/addon/321

DNS Cache

My own Firefox extension that allows you to disable the DNS caching of Firefox, which comes in quite handy when you have to check your webservers quickly. For a more detailed description see here. Download at https://addons.mozilla.org/de/firefox/addon/5914

Detecting mobile devices: Android devices

Permanent Link: Detecting mobile devices: Android devices 3. Februar 2009 Comment No Comment

I have an addition for my recent blog post "Detecting mobile devices" a while ago. In order to also detect Android phones (like the T-Mobile G1), you simply need to add the string "android" to the mobileClients array (both PHP and JavaScript). I also edited the original blogpost by adding the "android" string.

JavaScript loops profiled

Permanent Link: JavaScript loops profiled 20. Januar 2009 Comment No Comment

Today I was curious and wanted to know which way of looping in JavaScript fastest. So far, I always use for var i in array, since someone once told me, it is the fastest way. For testing I created an Array with 10000 elements:

ids = [];
for (var i = 1; i <= 10000; i++) {
ids.push(i);
}

I used the JavaScript Profiler of Firebug for profiling. The testsystem was: Intel Dual Core T2500 @ 2.00 GHz, 2 GB RAM, Ubuntu 8.04, Firefox 3.0.5 (only installed addon is Firebug). I profiled each loop variation 5 times and took the average time for comparison. The loop variations did nothing but loop and were the following:

Loop 1:

for(var i = 0; i < ids.length; i++) {}

Loop 2:

for (var i in ids) {}

Loop 3:

function process(element, index, array) {}
ids.forEach(process);

When I started the test, I didn't think there would be such huge differences in the performance of those three:

JavaScript loops profiled

Here are the profiling results in detail, in case you're interested (all times in ms):

Loop 1:
1,428
0,842
0,987
0,833
0,831

Loop 2:
6,084
4,471
6,040
6,256
6,509

Loop 3 (number in brackets is the profiled runtime of the function process() - see declaration above)
33,055 (16,439)
33,262 (16,489)
33,792 (17,044)
34,682 (17,312)
35,875 (17,637)

Detecting mobile devices

Permanent Link: Detecting mobile devices 13. Januar 2009 Comment No Comment

Nowadays it is getting more important to having his website also readable for mobile clients. While there is the possibility of using WURFL for detection, WURFL seems to be a bit overhead when you only want to display simple contents (mostly text). I agree, that you should use WURFL, or similar, if you're planning on providing media contents, such as videos. Still, you have to keep at the back of your mind, that it is very unlikely that you will have users using a 3 year old Siemens Mobile Phone browsing your website. The target consumers are definately iPhone users or any other user with a newer mobile phone.

When browsing to your website, people want to type the known adress (e.g. www.phpdevblog.net) instead of a new adress (e.g. www.phpdevblog.net/mobile). So obviously the best way would be to detect users using mobile devices and redirecting them to the mobile adress or using another Front Controller or… (the decision is up to you). An easy way is to check the users Useragent in order to detect mobile devices. I will show you two examples, one covering server-side detection (using php - obviously) and the other covering client-side detection using JavaScript. Both ways should cover most mobile devices, if you have any additions please let me know! Thank you

Edit on Februar 3rd, 2009: I added the needed string for Android phones to the arrays

Server-Side detection:

class Client
{
/**
* Available Mobile Clients
*
* @var array
*/
private $_mobileClients = array(
"midp",
"240x320",
"blackberry",
"netfront",
"nokia",
"panasonic",
"portalmmm",
"sharp",
"sie-",
"sonyericsson",
"symbian",
"windows ce",
"benq",
"mda",
"mot-",
"opera mini",
"philips",
"pocket pc",
"sagem",
"samsung",
"sda",
"sgh-",
"vodafone",
"xda",
"iphone",
"android"
);

/**
* Check if client is a mobile client
*
* @param string $userAgent
* @return boolean
*/
public function isMobileClient($userAgent)
{
$userAgent = strtolower($userAgent);
foreach($this->_mobileClients as $mobileClient) {
if (strstr($userAgent, $mobileClient)) {
return true;
}
}
return false;
}

}

$client = new Client();
$client->isMobileClient($_SERVER['HTTP_USER_AGENT']);

Client-Side detection:

function Client() {
}

Client.prototype.mobileClients = [
"midp",
"240x320",
"blackberry",
"netfront",
"nokia",
"panasonic",
"portalmmm",
"sharp",
"sie-",
"sonyericsson",
"symbian",
"windows ce",
"benq",
"mda",
"mot-",
"opera mini",
"philips",
"pocket pc",
"sagem",
"samsung",
"sda",
"sgh-",
"vodafone",
"xda",
"iphone",
"android"
];

Client.prototype.isMobileClient = function(userAgent)
{
userAgent=userAgent.toLowerCase();
for (var i in this.mobileClients) {
if (userAgent.indexOf(this.mobileClients[i]) != -1) {
return true;
}
}
return false;
}

var client = new Client();
client.isMobileClient(navigator.userAgent);