Pegboard Window Manager: A Javascript window manager written with jQuery

November 30th, 2009 by Cory Leave a reply »

Introducing Pegboard: A Javascript Window Manager that is written in Javascript (with a light dusting of jQuery). You can download Pegboard here. Click here to see a quick demo of Pegboard.

What is Pegboard?

Pegboard is a window manager written in Javascript and GPL licensed (I’m entertaining arguments for other licensing). It simplifies the process of creating, managing, and interacting with windows (dialogs) within your web application. It has been tested in FF 3 and Chrome. It should also work with other Webkit browsers and works with IE8. Other versions of IE were not tested.

It uses jQuery and jQuery UI to accomplish a chunk of the window functionality such as dragging and resizing. The Dialog jQuery UI widget is not used by default but can be used through the jWindow plugin, if one so desires.

Pegboard is not a web desktop like eyeOS or any other web based desktop replacement. It could be used by such an application but that is not why Pegboard has been created.

Why was it created?

As I write and use more webapps I’ve grown tired and resentful of the standard “single model” method for displaying windows. A dialog is popped up and disables the rest of the screen until you leave the popup. It has become increasingly clear to me that as complexity in web applications grows, the need for a window manager will grow with them. Sometimes you just need to show several dialogs at once to make the user experience less painful. Or what if the user needs to refer to something on the web page while entering data into your dialog? Pegboard aims to solve all these problems and problems that you don’t even know exist.

It exists to create a standard environment for application developers to use to design the window portion of their application. An app like Meebo or gTalk or even Gmail would be well-served by Pegboard. Any application that requires multiple windows to be displayed at once could use Pegboard.

Why jQuery?

When starting on a new project, I’ll be the first one to NOT recommend using a Javascript library–or any library for that matter. I can’t stand them. (That’s a subject for another post, though.) Pegboard has been written with a light dusting of jQuery. In fact, the Pegboard core probably only uses jQuery in less than 10 lines.

So why did I use it? jQuery UI’s Resizable and Draggable. I didn’t want to have to recreate the wheel on these and I reasoned that anyone with a need for a window manager will either probably be using a JS library such as jQuery, or able to edit Pegboard and replace the jQuery integration with their own code.

I didn’t check, but Prototype might even work out of the box. Though, the resizable and draggable aspects would probably need some finangling. How many prototype users are out there?

Is Pegboard stable?

At this time I’ve only used Pegboard in creating the demos included at the bottom of this page. It should work in a production environment but I’m not making any guarantees. This should be considered a preview release. I’m trying to determine what the fate of this project should be. Is there enough demand out there for a project such as this?

How does Pegboard work?

Pegboard is basically a DIV manager. You use DIVs to create Windows (PBWindow), which are then managed by PBWM (Pegboard Window Manager).

To turn a DIV into a Pegboard Window (PBWindow) you don’t have to add any additional HTML. Pegboard assumes your windows will have a “window” CSS class but this can easily be changed.

Pegboard doesn’t handle any window titles, buttons, or any other aspect of the window except the frame. This is intentional to allow maximum customization with your project.

At the Pegboard has only been tested with absolutely positioned divs but it should also work with other types of positioning.

Getting Started

I’ve tried to design Pegboard to be agnostic of current windowing methods. I haven’t tested this, but I’m hoping to make it integrate with current techniques such as ThickBox or other custom methods for displaying dialogs. Take a look at the basic demo for a working example. This guide assumes that you have a webpage setup similar to that.

Setting Up A Window

There is nothing special on the HTML side. Feel free to wrap a window in a custom window DIV or pass an already created DIV.

<div id="myFirstWindow" class="window">This is a window</div>

Using the jQuery Plugin:

$(document).ready(function() {
   /***
    * The jQuery plugin is very basic and can only be used to create PBWindows
    * It accepts three arguments:
    *		workspace: This is the container for your windows.  It can be document.body.  Think of it as the desktop.
    *		windowopts: These are the settings to pass to PBWM
    *		pbwmopts: These are the default options to use with each PBWindow.  You can enable things like resizing and dragging
    */
	$('.window').pegboard( document.getElementById('workspace') );

   // NOTE: The .pegboard function should only be called once.
   // If you need to create additional windows after calling .pegboard,
   // use the PBWM.createWindow function below.
});

Using regular ol’ Javascript:

// The first argument is the DIV that you wish to turn into a window
// The second argument is a list of properties to control the window such as resizing and dragging
// See function PBWindow in pegboard.js for a list...
PBWM.createWindow( document.getElementById('myFirstWindow') , {} );

// Create window also accepts a list of elements, so you could do something like:
PBWM.createWindow( $('.window') );

// For multiple windows, you would need multiple createWindow() calls

// If you would like to create a window using some method other than PBWindow, for example a jWindow (jQuery UI Dialog)
PBWM.insertWindow ( new jWindow( document.getElementById('myFirstWindow') ) );

// Initialize the window manager either before or after you create windows
// After is faster
// Pass the workspace for your windows.  This is the same as the workspace as defined above with the jQuery plugin
PBWM.init( document.getElementById('workspace') );

});

That’s it. You now have a window in PBWM.

Window Manipulation

Now, let’s manipulate that window. Pegboard doesn’t inject any nodes into your DIV element at all. This means that if you want a title or Minimize/Maximize/Close buttons, you will have to create them yourself. In the future this may change, but for now I see this as a strength since you can come up with your own functionality here.

Before doing any type of manipulation, you will have to get the PBWindow object (or jWindow or any other type of *Window extension that has been created.) At the time of writing there are only PBWindow and jWindow.

// All windows are linked to DIV objects, as such, to perform an action you
// must pass the DIV element of the window to the callAction function.
//
// NOTE: jQuery elements not allowed

// Now, you can pass that Window object to PBWM to perform actions

// Will maximize window to fill the entire Workspace (desktop) container.
// This may not be the entire window depending on how you initialized PBWM.
PBWM.callAction( document.getElementById('myFirstWindow'), 'maximize' );

// This will return a window to its original dimensions and position before being maximized or minimized
PBWM.callAction( document.getElementById('myFirstWindow'), 'restore' );

// Minimize doesn't do anything except add a "minimized" CSS class to your DIV
// This is to allow custom UI for minimization.
// You can add .minimized { display:none; } to your stylesheet to make it disappear
PBWM.callAction( document.getElementById('myFirstWindow'), 'minimize' );

// This action is called automatically when the window gets focus
// But you can also call it to set a window as active
// This means that it will be PBWM.activeWindow, receive an "active" class,
// and be brought to the top
PBWM.callAction( document.getElementById('myFirstWindow'), 'activate' );

// This closes the window permanently and removes it from PBWM
// If you have a form dialog that you want to reuse, you should probably
// use minimize and restore instead of close
PBWM.callAction( document.getElementById('myFirstWindow'), 'close' );

// The popout action has had limited testing and probably won't work for most scenarios
// It will pop a DIV out of a web page into its own new browser window
// In the future this will be very cool, but right now it is only a POC
PBWM.callAction( document.getElementById('myFirstWindow'), 'popout' );

// There are also a couple other actions that I won't cover here and probably more
// will be added in the future.

So now you can create windows and manipulate them. You will notice that since there are no built in minimize/restore buttons, once you minimize a window it is gone. That is where the example Taskbar extension comes into play.

The Taskbar extension is not designed to be complete. It only exists to show you how to make your own extension that will work just right with your app. The Taskbar can probably be easily modified to work with most apps, though.

The Taskbar extension takes advantage of the many hooks that have been sprinkled throughout the PBWM core. More hooks will almost certainly be added in the future. You can see a list of all the hooks by searching for ‘hook_’ in pegboard.js.

To use the included Taskbar extension, you only need to include the pegboard-taskbar.js file and then direct it to a Taskbar List element (UL/OL).

// Put this before you initialize PBWM with either PBWM.init or $().pegboard();
// That's it.  The rest of the interaction is handled automatically through hooks.
PBTaskbar.taskbarElement = $('#taskbar');

Extending Pegboard with Hooks

Hooks are easy to use and powerful. Think of them as events. You can use hooks to create all sorts of UI widgets to display your windows. Take a look. Here is an example of a modification to the included Taskbar to only display windows when you minimize them. You could easily create a custom management widget to bring up a list of open windows when you double tap Tab, for example.

If you are curious about hooks, have a look at the pegboard-taskbar.js file. Basically, all hooks are called before the action takes place, and the Window object is passed along with the call.

Events

But what if you want to assign real events to certain windows? There has been some functionality included to let you do this… sortof. At this time it is more of an afterthought and as such, might not be completely usable at this time.

It also doesn’t work like a real HTML or jQuery event would. To use events you just assign them to the window object instead of binding them.

There are two types of adding events, you can either extend the PBWindow object itself to include your arbitrary events or assign them on a per-window basis. This allows you to assign a custom minimize event to all your windows, but a close function that would only execute on a single Window.

/*
	Extend the PBWindow Object
	These functions will exist for all PBWindows created after you include this code
*/
PBWindow.prototype.minimize = function(W) {
	// do something when the window minimizes
};
PBWindow.prototype.close = function(W) {
	// do something when the window is closed
};

/*
	And this will only function for the window we created above
	It will not execute for other windows that have been created
*/

// pass the DIV for the window to get the Window object
var PBW = PBWM.findWindow( document.getElementById('myFirstWindow') );
if ( false !== PBW ) // false is returned if the element doesn't have a window
{
   PBW.minimize = function(W) {
      // do something when the window minimizes
   };
   PBWindow.prototype.close = function(W) {
      // do something when the window is closed
   };
}

/*
	And now, to use the functions
*/

// if you extended the PBWindow, this will call your minimize function for each window
// otherwise, if you only assigned it to a single window, it will only call if myFirstWindow is minimized
PBWM.callAction ( PBWM.findWindow( document.getElementById('myFirstWindow') ) , 'minimize'  );

// NOTE: Internal action calls currently don't use the callAction function and therefore won't
// 	call your custom functions.  I'm still working out the best way to handle internal action
//		calls.  For now, the best way to create these events is to use the system of hooks that
// 	are outlined above.

PBWM.cloneWindow

Lastly, there is a cool function that might be useful, and that function is PBWM.cloneWindow. It allows you to take an already created window and clone it into another separate window. cloneWindow accepts three arguments: function(PBW, properties, bCloneTree). PBW is the Window object to clone, options are unique window properties that you want to pass and bCloneTree defaults to true and will clone the entire HTML tree along with the window.

All Online Demos

At this time there are only a handful of demos online and included with Pegboard. They are working examples of the basic functionality.

Known Bugs

  • Cloning a window or popping it out and back in causes resizing and (sometimes) dragging to stop functioning. This seems to be a problem with jQuery. jWindow (jQuery Dialogs) are unaffected.
  • Popping out a window will cause functions to stop working. A fix is in the works for this. Popout functionality is largely uncreated as this point in time so you may see a lot of stuff not work here.
  • Resizing only works when you include the jQuery ui stylesheet. Take a look at the source for the demos.

3 comments

  1. Alex says:

    great! very nice!
    under what kind of license type is this application?

    Reply

    Cory reply on January 28th, 2010 02.58.01:

    Even though it doesn’t say it in the code, it is GPL3 licensed. I’m also entertaining arguments for other licenses.

    Reply

  2. tamale says:

    I found a bug where redraw would keep adding additional instances of the ‘window’ class. I fixed it by adding these three lines to line 517 in pegboard.js:

    //these three lines remove duplicate classes of ‘window’
    add = add.split(‘ ‘).unique().slice(0,-1).join(‘ ‘);
    if( add.split(‘ ‘).slice(-1) == ‘window’ ) add = add.split(‘ ‘).slice(0,-1).join(‘ ‘);
    if( add == ‘window’ ) add = ”;

    Reply

Leave a Reply