Letzte Blogger

Scott Lee

Staff
3 Nachrichten
21. August 2014

Martin Yan

Staff
5 Nachrichten
20. August 2014

Olaf Kock

Staff
77 Nachrichten
19. August 2014

Miguel Ángel Pastor Olivar

Staff
15 Nachrichten
19. August 2014

James Falkner

Staff
94 Nachrichten
18. August 2014

Josiah "Duke" Harrist

Staff
1 Nachrichten
13. August 2014

James Min

Staff
30 Nachrichten
12. August 2014

Adam Victor Brandizzi

Staff
3 Nachrichten
12. August 2014

Yogesh Sharma

1 Nachrichten
12. August 2014

Peter Mesotten

6 Nachrichten
10. August 2014

The Nitty-Gritty: Theme Improvements and Bootstrap in Liferay 6.2

Company Blogs 13. Januar 2014 Von Nate Cavanaugh Staff

With the release of Liferay 6.2, there have been a few questions regarding Bootstrap as well as general theme changes, and I'd like to take a minute to go into more details about what we've added, but also some of the rationale for some of the decisions.

Jorge's discussed a lot of the benefits, and the feedback we've gotten from the community has definitely been great.

If I had to sum-up, here are the most common questions I've gotten from developers about Bootstrap in 6.2:

  • Why did you choose version 2.3.2 instead of 3?
  • How do I use my Bootstrap theme?
  • Do you support Bootstrap's JavaScript plugins?
  • Why do all of the Bootstrap rules have .aui in front of them?

You may in fact have wondered those same things. Or maybe you didn't, but now that I've mentioned it, it's eating a hole in your brain. In order to alleviate your burning curiosity, I'll answer these questions first.

Why did you choose version 2.3.2 instead of 3?

We have been following Bootstrap 3's development since it was first announced in December of 2012, so we knew it was coming and had been discussing it while we were working on the Alloy and Liferay portions of converting over to it. Basically, the reason why we didn't use Bootstrap 3 comes down to 2 reasons:

  1. It was released on August 19th, 2013, roughly a month and a half before we were planning on releasing. Trying to cram it in at the last minute would have led to nothing but major bugs, weeping, gnashing of teeth, etc.
  2. It completely dropped support for IE7 and below. While in Liferay 6.2 we provided limited support for IE7 and below, it's just not feasible yet for this version to completely drop support for everyone across the board.

Hopefully that makes sense, and technically, you could still use Bootstrap 3 in your own theme and portlets (I'll go more into how this may be possible below).

How do I use my Bootstrap theme?

A common case is that someone has taken a generated theme (from a site such as Bootswatch) and want to use it inside of Liferay. If you're a theme developer, here's the easiest way you could accomplish that. I'm assuming you're using the plugins SDK and are familiar with placing your files in the _diffs/ directory):

  1. Inside of your theme's _diffs/css/ directory, create a file called aui.css.
  2. Open the aui.css file and do a find/replace with the following values: find: ../img/ replace: ../images/aui/ (and of course, deploy your theme).

This will use the default bootstrap icons, so there may be one or two that we have added that may not show up for you. If you'd like to use our version of FontAwesome (so you can use resolution independent icons as well), go ahead and paste this code inside of your _diffs/css/custom.css:


$FontAwesomePath: "aui/alloy-font-awesome/font" !default;

@import "aui/alloy-font-awesome/scss/variables";
@import "aui/alloy-font-awesome/scss/mixins-alloy";
@import "aui/alloy-font-awesome/scss/path-alloy";
@import "aui/alloy-font-awesome/scss/core";

body {
    @import "aui/alloy-font-awesome/scss/bootstrap";
    @import "aui/alloy-font-awesome/scss/extras";
    @import "aui/alloy-font-awesome/scss/icons-alloy";
    @import "aui/alloy-font-awesome/scss/icons-alloy-extra";
}

Do you support Bootstrap's JavaScript plugins?

We are only using the Bootstrap CSS and HTML conventions, but not any of their JavaScript. One reason is because Bootstrap uses jQuery and we use AlloyUI, and loading multiple JS libraries is just an overall bad practice. However, we did create equivalents for most of the Bootstrap JS components. For instance, we have a Modal, Tooltips, Pagination, Popovers, Tabs, and more. If there are ones you would like, please let us know, and we'll definitely prioritize getting them in :)

Why do all of the Bootstrap rules have .aui in front of them?

This is one of those changes that doesn't seem like much, but is actually really powerful.

With any framework, there's always a balancing act that involves juggling between simplicity and flexibility. Mnay of the issues we would see frequently in previous versions of Liferay were issues where our assumptions in making things easier could make it harder for theme developers to customize the portal.

Bootstrap is a very opinionated framework, which is what many people love, and many people can be frustrated by.

Previously, we always prefixed our CSS classes with .aui-, which is by far the safest. But this seemed wrong to do with Bootstrap's CSS classes. For one thing, it made it so that you couldn't easily just copy/paste the examples from the Bootstrap documentation and use it.

What we decided to do instead was to place a selector of .aui before all of Bootstrap's rules, so that the rules look like .aui .btn, etc.

What this does is allows you to not only easily remove Bootstrap from affecting the page, but even allows you to only apply Bootstrap selectively to different portions of the page (and this applies to Bootstrap's normalize.css rules as well).

For instance, let's imagine you want to only apply Bootstrap to the portlet's, but not touch anything else on the page. You would simply remove the aui CSS class from the $root_css_class variable, and edit your portlet.vm file and add it there.

You can take this CSS class and apply it anywhere (maybe you want everything only in one specific layout column, or only on one specific page, etc).

This is actually really exciting for theme developers and system integrators, but also allows casual users the ability to use Bootstrap without having to do any crazy workarounds.

What else is new in 6.2?

Bootstrap is completely controllable from the theme level

In earlier versions of Liferay, we included the Alloy CSS on the portal level. This led to issues such as having a reset.css being loaded from the theme, and one loaded from Alloy (and since it was on the portal level, it wasn't very easy to remove it without causing havoc on the rest of the CSS).

However, in 6.2, we've made it so that the portal looks for the CSS from the theme (we assume your theme has an aui.css file). What's great about this is that we also include the SCSS source, so you can control only specific variables or override specific files if you wanted to. For instance, if you wanted to customize specific files (say _thumbnails.scss) you could overwrite just that file. Or if you needed full control and wanted your version of Bootstrap to only have some bare minimum of button and form css, you can do that as well (though not necessarily recommended, as there are items in the portal that might be relying on other pieces of the framework).

An easier way to do media queries

We have also added a mixin called "respond-to" that allows you to easily target certain types of devices without having to remember the media queries. For example:

/* will only style phone displays smaller than 768px */
@include respond-to(phone) {
    body { background: red; }
}

/* will only style phone and tablet displays smaller than 980px */
@include respond-to(phone, tablet) {
    body { background: blue; }
}

/* will only style tablet and desktop displays greater than 768px */
@include respond-to(tablet, desktop) {
    body { background: green; }
}

What's cool about this is that you can use it at any level in your SCSS. For instance, you can either group a set of rules like so:

@include respond-to(phone) {
    body { background: red; }
    input { width: 100%; }
}

or, you can use it in the middle of an already deeply nested SCSS structure, such as:

body {
    input {
        background: white;

        @include respond-to(phone) {
            background: blue;
        }
    }
}

A new Dockbar display mode

One of the things you may have noticed about the Classic theme in 6.2 is that the Dockbar doesn't appear to be a bar, exactly. If you're not sure what I mean, here is what it looks like in 6.2:

image

It may not be obvious, but in this mode, the dockbar is actually split, as you can see here:

image

One of the things we wanted to do was to find a way to make the dockbar appear a little less intrusive to the overall design. We also wanted this to be something that all themes could use if they wanted to.

So what we did was add a new mode for the Dockbar. Basically, if you add a CSS class onto your body element called dockbar-split, it will trigger the new display mode.

In order to add this CSS class, you don't even need to overwrite your portal_normal template. Simply create a file _diffs/templates/init_custom.vm and in there add this code:

#set ($css_class = "${css_class} dockbar-split")

We also made it so that the deafult style was much more neutral. While the Classic theme looks like the above screenshots, this is what the Dockbar looks like in a default theme with no customizations:

image

And here's what the Split Dockbar looks like with no customizations:

image

As you can see, it's much less targeted to any one specific design.

We are bundling in FontAwesome by default

If you haven't heard of FontAwesome, you should definitely check it out. It offers a wide range of icons and a lot of flexibility.

But the main benefits can be summed up as:

  • Completely scaleable icons, even for high resolution devices
  • Easily change the size and the color of the icons via CSS

Final words

Overall, we've addressed a lot of the issues we've seen theme developers run into, as well as add features that solve many of their common business goals.

As always, feel free to ask any questions you may have here, or in the forums :)

Liferay.com, mobile sites and responsive layouts

Company Blogs 22. Mai 2011 Von Nate Cavanaugh Staff

Bryan recently blogged about the new site design and how Liferay.com is displaying a mobile formatted website for different types of devices (go ahead and test it out, hop into any browser and resize the page as small as it can go). Today I want to discuss a bit how we achieved this from a technical level. This is one of the most common questions I not only got from people who attended our East Coast Symposium, but also inside of Liferay from other devs.

Liferay.com can resize from desktop to mobile dynamically

Because we like to keep our ear to the ground on all things front end, we’ve actually been following the discussion/debate about responsive web design since Ethan Marcotte published his article on the subject last year. If you haven’t read the article, the tl;dr version is that using CSS media queries, it’s possible to pass a different set of CSS styles to different devices and have a design that’s customized for a specific browser/device size.

As far as mobile strategies go, it’s probably the easiest to implement, and there are a lot of quick wins with it. However, Jon Neal pointed out to me when we were first discussing this idea that there’s very little difference between using CSS media queries and just using Javascript to simulate the same thing.

I’ve since read other people bring up a similar point, and it’s a fair one. In fact, there are multiple issues with CSS media queries that make them cumbersome. For instance, let’s say we want to share some styling across two specific devices. It’s possible using the logical operators but even then, you end up with duplicate CSS (and it becomes much harder to read). And the fact that IE doesn’t support media queries was another major impediment to us using it, so we were going to need to use JavaScript anyways (either to add the CSS classnames or to parse mediaqueries).

So Jon wrote up a script that would handle this logic for us. Here’s how the script works: When the page is loaded or resized, the script checks the width of the window and it looks into an object of 4 major size specifications; 960, 720, 480, and 320.

What do those numbers mean? 960px is based on the uber-popular 960 grids which is a layout width that looks great on most desktop computers, and the iPad in landscape mode. 720px is a layout width that looks great when the iPad is in portrait mode. 480px is the width of the window of most smart phones in landscape mode. 320px is the width of the window of most smart phones in portrait mode.

The other great thing Jon came up with was to add greater-than and less-than css classes. I’ll show how this works in a second, but the idea is basically, you can not only target a specific device width, but also if it’s greater than or less than any of those above widths.

If your target device is 600x800, you can still target that device with the css classes.

With what Jon prototyped, I’ve created an Alloy module that codifies this so that it’s super easy to use (while at the same time, staying out of the way for users who don’t wish to use it).

For a quick and simple demo, open up the viewport demo (if you view it in Chrome or Safari, Firefox or Opera, you’ll see some cool CSS3 generated content/animation, but the demo works in any browser).

To get the module to run, all you need to do is call: AUI().use('aui-viewport') in a script tag anywhere on your page.

Now you can target your site design to specific device sizes. Let’s say we want to give our navigation items to sit side by side when we view on the ipad or larger, but that we want to have them stack on smart phones:

#navigation li {
    display: inline;
    float: left;
}
.aui-view-lt720 #navigation li {
    display: block;
    float: none;
}

or let’s say we don’t want to make sure our images don’t exceed a certain width on the iPad in portrait mode

.aui-view-720 img {
    max-width: 300px
}

Or perhaps we want to target just portrait mode of smartphones and tablets:

.aui-view-720 body, .aui-view-320 body {
    background: #ccc;
}

You can even combine this with our other browser selectors to target very specific browsers:

.touch.aui-view-lt720 {} /* Touch based smartphones */
.webkit.aui-view-lt960 {} /* Webkit based tablets and smartphones */
.win.aui-view-720 {} /* Smaller browser views on just Windows */

Now, how is the logic applied? If the screen is equal to or between any of our defined widths, it will get this CSS class: aui-view-{WIDTH}. And, if the screen is greater than any of our widths, it will also get: aui-view-gt{WIDTH}. Lastly, if the screen is less than any of our widths, it will also get: aui-view-lt{WIDTH}.

So a window size of 1900x1200 would get:

aui-view-gt320
aui-view-gt480
aui-view-gt720
aui-view-gt960
aui-view-960

whereas a window size of 800x600 would get:

aui-view-gt320
aui-view-gt480
aui-view-gt720
aui-view-720
aui-view-lt960

Notice that the classes represent the width rounded down to the nearest division. This is to guarantee that your styled layout won’t exceed the current width. Because of that the css class doesn’t exactly represent the current width, but rather enforces you to think about the layout at specified sizes and devices.

Caveats

Life is not all cheese and bananas with this system, as there are some caveats to be aware of:

  • You are delivering the same content to all devices.

    Depending on the project, this could either seal the deal or break it. There’s an added cost to shipping down different images for the same page, and some mobile websites may wish to send only a very limited set of content to mobile devices (or perhaps very targeted content). The answer to this then is decide which of the 2 strategies you wish to use for your specific project: same content for all devices, but dimension specific designs, or redirect to a mobile/touch specific community that serves a targeted set of content and design.

  • Designs can require more planning for each of the design layouts

    You’ll notice that the design now has to accommodate basically 4 different views: desktop, tablet(portrait), and smartphone (portrait and landscape). This of course means thinking about a different set of design compromises and layouts for how the same content should fit (or which content should be hidden) for different types of views. Of course, this caveat extends to both content strategies, but having the same content automatically in every view often requires a bit more forethought during the design process.

How can you get this module?

If you’re an Enterprise Edition customer, this module will be available in the 6.0 SP2 service pack (and the great part is, if you don’t want to use it, it will have 0 affect on your application). If you’re running Community Edition, this will be in Liferay 6.1 or if you’re running trunk of Liferay you can see it today. We’ll also be releasing another version of AlloyUI soon that will have it included.

Conclusions

Overall, the response has been great, and the design/development process was surprisingly smooth. I would recommend it as a general principle for your design, as it helps our designs fit the fluid nature of the web.

More information

Responsive Web Design by Ethan Marcotte
MediaQueri.es
The practicalities of CSS Media Queries, lessons learned.

Using jQuery (or any Javascript library) in Liferay 6.0

Company Blogs 20. Juli 2010 Von Nate Cavanaugh Staff

 One of the biggest feature requests from Liferay 5.2 was the ability to upgrade the included version of jQuery. Many users would like to use third-party plugins, and most of those require the latest jQuery library (1.4.x as of this writing).

So for 6.0, we solved this a couple of different ways. First, we no longer include jQuery by default. We have rebuilt our UI to run off of AlloyUI which is built on top of YUI3.
By moving off of jQuery, it's also allowed us to step out of the way of developers who wish to  use any version of jQuery that they need without worrying about conflicts with the core portal javascript.
The other way we solved this for the future was by creating our own namespace. Since we're still using a Javascript library (YUI3), we would still have the same risk of conflicts.
So instead of calling YUI() in the portal, we created AUI(). By creating the "AUI" namespace, we are able to guarantee that our environment won't conflict with someone who wants to upgrade their version of YUI3 in the future.
 
But even though we believe strongly in AlloyUI and YUI3, there are existing applications with codebases on jQuery and porting them over is not always possible.
Or perhaps there is some other Javascript library (such as YUI2, Dojo, qooxdoo, ExtJS, etc) that you need to include for the same reason.
 
So today, I want to show a couple of ways to include the third-party javascript library into Liferay that you want. I'll be using jQuery, and I'll be using the URL to their "production" version: http://code.jquery.com/jquery-1.4.2.min.js
 
There are a couple of ways you can include jQuery onto the page.
Jonas has covered a great way in his blog post on building jQuery portlets in Liferay 6.
 
First, using the same basic principle, is including it in your portlet.
In your liferay-portlet.xml add this entry:
<header-portlet-javascript>http://code.jquery.com/jquery-1.4.2.min.js</header-portlet-javascript>
That will add jQuery 1.4.2 onto the page wherever that portlet happens to be rendered.
 
Second, the other way is to add it into your theme. Inside of your theme's templates/portal_normal.vm you would add this line in the head of your theme:
<script src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
This will make jQuery available for everywhere in Liferay, including plugins that you deploy.
 
Third, you can even use AlloyUI to load up other JS files. This is useful if you can't or don't want to edit either the liferay-portlet.xml or the HTML.
 
In any Javascript that gets added to the page, you can do:
AUI().use('get', function(A){
    A.Get.script('http://code.jquery.com/jquery-1.4.2.min.js', {

       onSuccess: function(){
           // jQuery() can be used in here...

      }
 });
});
 
Also, I want to point out that for those people who built apps on Liferay with the version of jQuery that was included (1.2.6), and if you would like to continue to use that version and the plugins that were included by default, we have kept that directory in the source for 6.0 as a deprecated fallback.
 
This allows people who don't want to upgrade the JS portion of their app, they can easily include the previous version.
The way that would look different is that it would just point to the different path, like so:
<header-portlet-javascript>/html/js/jquery/jquery.js</header-portlet-javascript>
The path to the previous version is:
/html/js/jquery/jquery.js and the /html/js/jquery/ directory contains all of the plugins from 5.2 that work with jQuery 1.2.6.
 
I hope that is helpful, and much thanks to Jonas for his blog post about the sample jQuery plugin. And of course, please let me know if there are any questions :)

AlloyUI - Working with Widgets, Part 1

Company Blogs 14. April 2010 Von Nate Cavanaugh Staff

 What is an Alloy widget?

A widget is something that does some sort of task. Like what, you might ask? For instance, in Alloy, there's a TreeView widget, where you can configure it to display an hierarchy of data, and this hierarchy can be ajaxed in, or shown right away, and with a lot of different options.
That would be a pretty complex widget.
A simple widget would be the Tooltip widget, which just shows users some content when they hover over an item on the page.
 
In Alloy, there's a large collection of widgets, as well as a robust framework for building your own.
 
In this blog post, I'm going to cover some of the widgets that we have in Alloy, then in the next one, I'll post how to create your own. I'm splitting it up because I'm finding both posts have a lot of info to cover, and I don't want people to zone out or miss info they might have found useful because it was buried in a super long post.
 
So let's go ahead and get our sandbox ready:
 
AUI().use('', function(A){
 // Our code will go here
});

I'll reference the name of the widget above the code so you can see the module that we'll be using in order to run it.
 
So let's go ahead and create a TreeView of a user directory that has drag and dropping of elements, and expandable folders.
 
Here's the demo: TreeWidget
Here's the code you would have to write:
 
You would use the aui-tree-view module
 
var users = new A.TreeViewDD({
     children: [{
           label: 'Users',
           expanded: true,
           children: [{
                label: 'Nate Cavanaugh',
                expanded: true,
                children: [
                     {label: 'Documents', leaf: false},
                     {label: 'Downloads', leaf: false},
                     {label: 'Movies', leaf: false},
                     {label: 'todo_list.txt'}
                ]
      }]
 }]
}).render();
 
Let's look at what's going on here. First we're doing a traditional object construction in JavaScript, which is creating a new object. You're saying give me a new TreeView, and let's call it "users".
 
So what's with that .render() at the end?
 
That render() does not have to be called until you're absolutely ready to display the widget. In fact, there are many times where you may wish or need to configure a widget, but only render it under certain circumstances, or after a certain period of time has passed.
 
But if you don't care about waiting, you can just do it all inline (render will still return a reference to the widget).
 
We've just created a tree widget, and have this users variable, so what? Why is this exciting if you're just *using* widgets?
 
Because even if you're just using widgets, you can still do VERY interesting stuff with these widgets. Remember how we talked about working with elements?
 
The widget API is VERY similar to the Node API, meaning that in the same way that you can do get/set for properties on a Node, you can also do that on a Widget. And in the same way that you can use .on() for nodes, you can also do the same thing for Widgets.
 
But before we jump into that, let's look at another widget, something like Tooltip.
 
So let's go ahead and create a Tooltip. We'll use the aui-tooltip module:
var tooltip = new A.Tooltip({
     trigger: '.use-tooltip',
     bodyContent: 'Hello World!'
}).render();
 
What we're doing here is saying give us a new Tooltip, and show it when you hover over any item with the css class of .use-tooltip. It's also saying to show the content of the body to be "Hello World!". Now, in most cases, this wouldn't be super helpful because you wouldn't want to have the same tooltip text for multiple items, but there is a way to easily have it read the title attribute of the element instead. I'm simply manually using the bodyContent to illustrate the next part.
 
So now we have our tooltip object, what can we do to it? Well, all of those things we passed into it, like trigger, and bodyContent can all be read or changed using get/set, and not only that, but you can listen in to when they're changed as well.
 
This might take a second to realize just how cool this is, but take my word for it, it's insanely powerful.
 
Let's take a look. We'll go ahead and change the message of the tooltip to instead say "Hey there Earth!".
 
tooltip.set('bodyContent', 'Hey there Earth!');
 
Now when you hover over it, the tooltip will say 'Hey there Earth!'.
 
Moderately cool, but the real power comes from the change events that are used with this. Whenever you use get or set, the widget fires a custom event for that change.
So in this case, the event that got fired was "bodyContentChange". Every attribute on the widget fires a "change" event when it gets changed, and you can listen to it in two phases.
 
You can basically listen to the attribute get changed before the value is changed, and prevent it if you want, or after it gets changed.
 
This took me a few minutes to sink in, but here are the benefits:
 
Even as a person using the widgets, you have the ability to listen to interesting moments of what's going on and manipulate widgets on the page.
 
Here's an example: 
All widgets have an attribute called "visible", and when this attribute is changed, the widget hides or shows. Usually you just call widget.hide() or widget.show().
 
But let's say on our tooltip, we want to listen before it shows, and if a certain element is missing on the page, we will prevent the tooltip from being shown.
 
We would do:
 
tooltip.on('visibleChange', function(event){
     if(!A.one('#myEl')){
         event.preventDefault();
     }
});
So now, the tooltip will only show up if an element with the id of myEl is on the page.
 
Or here are some more practical examples:
 
Let's say you're creating a Dialog, and you toggle the visibility of the dialog based on if a separate ButtonItem widget is active?
 
You would use the aui-button-item widget, and the aui-dialog widget, and do:
 
var dialog = new A.Dialog({title: 'Hello', bodyContent: 'Lorem Ipsum'}).render();
var buttonItem = new A.ButtonItem({active: true, label: 'Toggle Dialog'});

buttonItem.after('activeChange', function(event){
 dialog.set('visible', event.newVal);
});

Or what about if you have a ColorPicker widget, and want to update the value of a text input when the hex code changes? You can even pass in the events when you create the widget.
 
You would use the aui-color-picker
 
new A.ColorPicker({
     after: {
         hexChange: function(event){                             A.one('#myInputNode').val(event.newVal);

         }       } }).render();
Another thing I mentioned last time was that Widgets can be plugged just like Nodes. Well, remember our handy dandy IO plugin? We can plug any widget with it, and it will automatically know what elements it should grab internally.
 
For instance, let's create another dialog again, and let's plug it: 
var dialog = new A.Dialog({title: 'Hello World'}).plug(A.Plugin.IO, {uri: 'test.html'}).render();
The plugin will smartly grab the area where the content goes, but not the area of the titlebar, and add a loading mask, and insert the content of the ajax request into there.
 
A couple of notes:
 
The .render() method, will, by default, add the widget into the body element. But if you pass it either a selector or a element, it will render the widget into that element on the page.
 
So I could have easily done this:
new A.ColorPicker().render('#myContainer');
 For Widgets, if you attach a listener "on" that event, it fires before the change has been made, so you can think of it as a "before" stage. It's kind of confusing, but it works.
If you listen to the "after" stage, it's after the work has already been done.
In most cases, you'll only care about being notified after, unless you want to prevent something from happening, or want to know before it happens.
90% of the time, I use "after" to listen to widget events.
 
Anyways, that's it for now on using the widgets. Next time, we'll talk about how you can make your own widget, and what a widget is actually made of.
See you then!

AlloyUI - Working with Plugins

Company Blogs 24. März 2010 Von Nate Cavanaugh Staff

 I mentioned last time that I would talk about the IO Plugin that we have that makes one of the most common tasks of updating content.

So first, I'll show you how to use it, then we'll talk more about how to make your own plugin.
 
So let's set up our sandbox:
AUI().use('io-plugin', function(A){
 // Our code will go here
});
 
So the IO plugin is essentially everything that A.io.request is. All the same arguments, same behavior, but what it does for you is kinda cool.
 
There is a common pattern we kept seeing in our ajax calls which was:
 
  1. Insert loading icon into region
  2. Do ajax call
  3. On success, update region with new content
Here is what the code would look might like before:
 
var contentNode = A.one('#contentNode');

if(contentNode) {
  contentNode.html('<div class="loading-animation"></div>');

  var myRequest = A.io.request('test.html', {
    on: {
      success: function(event, id, xhr) {
        contentNode.html(this.get('responseData'));
      }
    }
  });
}

It's a trivial process, but when you see it so often, you start to think that it's one of those patterns that could imagine yourself sitting in a padded room chanting over and over.
 
So we created a handy little plugin that handles this for us, and in a much nicer way. What this plugin does is does an ajax request the same as A.io.request, but also adds a loading mask to the area that we want to load content into. As soon as the content is ready, it parses through it and looks for any javascript that may be inside of it and executes it, and sets the content into our area.
 
var contentNode = A.one('#myNode');

if(contentNode) {
 contentNode.plug(A.Plugin.IO, { uri: 'test.html' });
}
And that's it.
 
Here's what it looks like:
 
It basically will mask any existing content, and add a loading indicator that is centered above it.
 
Here is some of the cool stuff about the plugin (and all plugins in Alloy):
 

1. It has it's own namespace

This means that this plugin lives inside of it's own area on the node so that it won't clobber with other plugins. Here's a good reason. A.Plugin.IO inherits all of the methods and properties that are on A.io.request, so you can call things like .start(), .stop() (to start and stop the request of course), or .set('uri', 'new_test.html'), or .set('dataType', 'json') and everything else we covered in the <a href="http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-ajax?_33_redirect=%2Fweb%2Fnathan.cavanaugh%2Fblog">last post</a>.
If all of that was placed on the main object, then it would conflict with any methods that might exist already on that node, or maybe another plugin.
So instead, it's placed in a namespace, and you can access that like so:
 
contentNode.io
 
So if you want to set the dataType on the plugin to json, you can do:
 
contentNode.io.set('dataType', 'json');
or if you want to stop the connection:
contentNode.io.stop();

2. Plugins can be "unplugged"

This is incredibly useful if you're writing a plugin that should do some clean up work when a user is finished with it (for instance, if you have a plugin that adds in some children elements or adds on some class names to a container).
 
You would just call:
contentNode.unplug(A.Plugin.IO);
 

3. Plugins can be plugged to NodeLists as well as Nodes

So this would work as well:
 
var contentNodes = A.all('.content-nodes');

contentNodes.plug(A.Plugin.IO, { uri: 'test.html' });
 
Then we could grab the first item in the NodeList and access the plugin namespace
contentNodes.item(0).io.set('cache', false);
 

4. Plugins can also be on Widgets

I'll cover widgets more next time, but the same exact process applies, and in fact, the IO plugin is written in such a way that it knows whether it's in a Node or a Widget and will behave accordingly.
 

5. Plugging doesn't have to be a per instance affair.

You can do this:
A.Node.plug(A.Plugin.IO, {autoLoad: false, uri: 'test.html'});
Now you could do:
var contentNode = A.one('#contentNode');
if(contentNode) {
 contentNode.io.start();
}
 
The difference is that since we called A.Node.plug() (which is a static method on the Node class), it plugs all newly created instances with that plugin.
 
I recommend doing it on a per instance basis, however, simply because 1, you'll consume less resources, and two, you don't have to worry about if your existing objects have been plugged.
 
6. You can plug with multiple plugins at once.
So for instance, you can do this:
 
contentNode.plug([
 { fn: A.Plugin.IO, cfg: {uri: 'test.html'} },
 { fn: A.Plugin.MyOtherPlugin }
]);
If that looks confusing, feel free to ignore it, but it simply is a way to pass in mutliple plugins and their configurations (if they need one) all at once.
 

Creating a plugin

 
What's the simplest way to get started creating a plugin? Well here's what's to remember: A plugin, at the very least, is a function, with a property on it called NS which will be it's namespace.
 
So for this example, I'm going to create a plugin that takes an input field, and inserts a "defaultValue". When you focus the field, if the value matches the "defaultValue", it will empty the field, and allow the user to enter their value. When the user moves away from the field, if they haven't entered anything new, it will add in the default text.
 
If you wish to jump to the demo, go ahead and take a look here: Plugin Demo.

 

 
I'm going to start with this markup:
 
<input data-defaultValue="Enter Text" id="myInput" type="text" value="" />
 
HTML5 allows for custom attributes if you prefix the attribute with "data-", so you'll notice I added a new attribute called "data-defaultValue", which our plugin will read.
 
So I'll create the javascript:
 
var defaultValuePlugin = function(config) {
    var node = config.host;

    var defaultValue = node.getAttribute('data-defaultValue');
    var startingValue = node.val();

    if (!startingValue) {
      node.val(defaultValue);
    }
    node.on('focus', function(event) {
      var value = node.val();

      if (value == defaultValue) {
        node.val('');

      }

    });

    node.on('blur', function(event) {
      var value = node.val();

      if (value == '') {
        node.val(defaultValue);

      }
 });
};

defaultValuePlugin.NS = 'defaultValue';

Now all we have to do to get it working is simply plug it onto a node:
 
A.one('#myInput').plug(defaultValuePlugin);
 
I'll go over some points of the code above.
 
One is that, notice that the first line points to config.host. The argument config is the configuration object that is passed into the plugin, but by default the host is always passed into the plugin, so you always have access from the plugin to whatever is being plugged.
It's like a magic link to whatever item you're plugging.
 
The next lines I'm doing the basic work getting an attribute, setting a value if one hasn't been set, and in the bulk of it, attaching focus and blur listeners to do the checking for the value.
 
On the last line, I'm attaching a property called NS to the function that we created. This is the namespace that this plugin will live under, and even if we don't need to access anything specifically, it's there so we can plug something without worrying about it colliding with any other plugins.
 
This is really just scratching the surface of the power that the plugin system offers, but I wanted to show a simple case, rather than bog down in the mire of complexity. If there is any interest in seeing advanced plugins, I can always write an 11th blog post, but the YUI3 page also offers a lot more info if you would like to investigate further as well.
 
Until next time, see you guys later!

 

AlloyUI - Working with Ajax

Company Blogs 17. März 2010 Von Nate Cavanaugh Staff

Ajax is one of those patterns that are a must have with a UI framework, so let's go ahead and jump right into doing some Ajax requests, and then we'll dive into the more complex cases.
 
Let's prep our sandbox, but this time, the module we're going to use is called "aui-io-request".
 
AUI().use('aui-io-request', function(A){
 // Our code will run here
});
 
The simple of the simple, let's assume we're just going to be making a simple ajax request to a plain html file, called "test.html".
 
A.io.request('test.html');
 
That's all there is to it. However, that's not very interesting because it doesn't do anything.
 
Let's say want to send a POST request to the server:
 
A.io.request('test.html', {
   method: 'POST',
   data: {
     key1: 'value'
   }
});
 
How about responding to the server? There are 5 possible callbacks: start, complete, success (or) failure, and end.
 
If I wanted to alert the response from the server, here's what I would do: 
A.io.request('test.html', {
  on: {
   success: function() {
     alert(this.get('responseData'));
   }
  }
});

What is this.get('responseData')? It's basically a normalized property of what is returned from the server. It's useful because A.io.request supports having different types of data returned by the server and automatically handled.
For instance, if your server returns JSON like {"myProperty": 2}, you could do something like:
 
A.io.request('test.html', {
  dataType: 'json',
  on: {
   success: function() {
     alert(this.get('responseData').myProperty); //alerts 2
   }
  }
});
 
You can also work with XML that way. Assuming your server returns something like: <name>AlloyUI</name> you could do:
A.io.request('test.html', {
  dataType: 'xml',
  on: {
    success: function() {
     alert(A.all(this.get('responseData')).all('name').text()); // alerts AlloyUI
    }
  }
});

You can also submit all of the data in a form via ajax as well. Here's the simplest version: 
A.io.request('test.html', {
  form: {

      id: 'myFormId'

  }
});
 
That will serialize all of the data in the form, and send it to "test.html".
 
One other handy feature of this is that you can define an ajax connection once, and reuse it multiple times, and start and stop it later on.
Here's an example: 
var myAjaxRequest = A.io.request('test.html', {
    method: 'POST',
    data: {
      key1: 'value1'

    }

});

Now later on, if I want to make that same ajax call again, all I have to do is call:
 
myAjaxRequest.start();
 
But what if I want to just define the call, but not execute it the first time (for instance, you know you want to run it later, but you don't want to update the server), you can do:
 
var myAjaxRequest = A.io.request('test.html', {
 autoLoad: false,
 ...
});
 
What's cool about this is that if later on, you want to change one of the properties before you send the request, you can do that as well. For instance, let's say you want to disable caching before you start the connection again:
 
myAjaxRequest.set('cache', false);
 
Or if you wanted to change from POST to GET
 
myAjaxRequest.set('method', 'GET');
 
Or change the dataType to JSON:
 
myAjaxRequest.set('dataType', 'json');
 
Or even change the URI at the last moment:
 
myAjaxRequest.set('uri', 'new_test.html');
 
Then when you're ready you would call:
 
myAjaxRequest.start();
 
And if at any time after you have started the request, you want to stop the whole request, you can call:
 
myAjaxRequest.stop();
 
And that's most of it right there. There are some cool plugins that we have that make working with ajax easier, but since the next topic is on plugins, I'll cover those in the next blog post, and they'll be a nice segue between topics.
 
One of those plugins is called A.Plugin.IO, and it's incredibly awesome, because it simplifies the extremely common task of not only loading content into a node or a widget, but adding a loading indicator to that node and automatically parsing the javascript for you.
 
I'll go into more details in the Plugins post, but it's really handy.
 
See you then!

AlloyUI - Working with elements and events

Company Blogs 16. März 2010 Von Nate Cavanaugh Staff

Getting Started

Welcome to our first post in talking about Alloy. I'm going to jump right in, but the only piece of info I want to cover beforehand, and you'll see me do it in every post, is the idea of "sandboxes". Because AlloyUI is built on top of YUI3, it has the concept of the sandbox. What this means is simply a callback where you run your code.

The way it's constructed is that you declare the packages that you want to use, and then, inside of your sandbox, you use them.

The benefit to this is that it allows your code to run as lean as possible, and loading only what it needs, without having to load a lot of stuff on the page first.

How do you create a sandbox?

Simple:

AUI().use(function(A) {   // Your code goes here  });

Let's look at that real quick.

AUI() is a function call, and you attach a .use on it. Inside of that .use(), you can pass 1-n number of arguments, but the last argument *must always be a function*.

You'll notice that the callback gets an argument passed to it called "A". That A is *the* Alloy object. It's where all of Alloy objects and classes are stored.

 

Most of the time you'll be setting up your sandbox using at least one or two packages. Here's how that would look like using the event and node packages:

AUI().use('event', 'node', function(A) {
   // Your code goes here
});

When you see me write code samples where I do something like:

A.one('body');

assume that I am inside of the sandbox.

Working with elements and events

The most common task you're most likely to come across in web development is working with elements on the page, and doing something with them. AlloyUI, because it's built on top of YUI3, has two objects for working with elements on the page, Node and NodeList. These two objects are closely related, and in fact, you can think of them as almost the same. The only difference is that Node is a single item, and NodeList is a collection of Nodes.

There are two methods you can use to get elements on the page, and each will return something different depending on the method.

The methods are called A.one() and A.all().

A.one() will return a Node object if it finds the element on the page (null otherwise), and A.all() will always return a NodeList (if it doesn't match any elements, it will return an empty collection).

 

Here's a few examples of how A.one() would be used:

var el_1 = A.one('#myCustomElement1');

var el_2 = A.one('.custom-element');

var el_3 = A.one('input[type=checkbox]');

Notice how A.one() will accept selector strings that are not just for ID elements? What if there are multiple elements matching the selector? It will just return the first one. This is useful in many situations, and has an impact on performance.

If the selector cannot be matched on the page, then A.one() will return null. This means that in order to operate on the Node element, you have to first do an if check on the variable then do the work.

For instance:

var el_1 = A.one('#myCustomElement1');

if(el_1) {

 el_1.setStyle('height', 50);

}

This can seem a bit verbose to some people, so it could be avoided if you wish. You could write the above like so:

A.all('#myCustomElement1').setStyle('height', 50);

without risk of throwing an error.

So why do I prefer A.one()? Mainly because of performance. A.one() will run about 2-4x faster to grab the element, but it also helps me write out clearer code (I know that if I'm not updating a block of code it's because it didn't find an element, whereas trying to debug long chains of code is a nightmare).

But both methods are there for you.

What kind of selectors are available? By default, anything in CSS2, which covers 98% of the cases and most of the selectors I've needed to write.

However, there is a CSS3 module, and if you need to do something like:

A.all('.custom-element > div:nth-of-type(even)'), just add the "selector-css3" module to your sandbox, and the selectors are available to you.

It's pretty rare that we've actually *needed* these selectors, though, but again, they're there if you need them.

That covers the basics on getting the elements, what about doing something with them?

So, let's cover some common tasks:

Setting styles

I'm going to grab my element:

var nodeObject = A.one('#myElement');

Setting a background color:

nodeObject.setStyle('backgroundColor', '#f00'); //Sets the background color to red

Setting a border 

nodeObject.setStyle('border', '5px solid #0c0'); //Sets a large green border

But what if I want to set multiple styles all at once? Just simply use the setStyles method (notice the "s" on the end of the name?).

nodeObject.setStyles({
   height: 200,
   width: 400
});

You can also get the current style for an element by doing something like:

nodeObject.getStyle('border');

One common task I think we've all done is to try to position a box somewhere on a page? Usually we'll just set the styles on the element, including the positioning.

For instance, let's say we wanted to move something to exactly 100px from the left, and 200px from the top.

Usually we might do something like:

nodeObject.setStyles({
  left: 100,
  position: 'absolute'
  top: 200
});

But then, what happens if it's inside of a positioned container? It will be relative to the container, then your offset will be off.

Instead, here's how you would do it now:

nodeObject.setXY([100, 200])

And it will automatically calculate the parents positioning for you guaranteeing that it's at the spot absolutely on the page that you want it. Much shorter code and much more accurate.

But what is really cool that is related to this is, often times you just want to center an item on the page absolutely. Here's how you would do it:

nodeObject.center();

Working with class names

All of the most convenient ways of working with class names, and then some, are here:

nodeObject.addClass('custom-class');

nodeObject.removeClass('custom-class');

nodeObject.toggleClass('custom-class');

nodeObject.replaceClass('custom-class', 'new-class');

nodeObject.hasClass('custom-class');

nodeObject.radioClass('custom-class');

In that last line, radioClass() will remove the class name from all of the sibling elements, and add it only to the current item, similar to how a radio button would behave.

 

Manipulating elements

Appending a new element to the nodeObject:

nodeObject.append('<span>New Text</span>');

Appending the nodeObject to another element already on the page:

nodeObject.appendTo('body');

Updating the innerHTML of an element:

nodeObject.html('<b>new text</b>');

Removing an element:

nodeObject.remove();

Creating a brand new node from scratch:

var newNodeObject = A.Node.create('<div id="myOtherElement">Test</div>');

Moving up and down the elements

Often you need to jump around to different elements relative to the current one you're on (for instance, to find a parent of a current item or a child/children).

Finding the first parent of nodeObject with the class name of .custom-parent:

nodeObject.ancestor('.custom-parent');

Finding the first child with the class name of .custom-child:

nodeObject.one('.custom-child');

Finding all children with the class name of .custom-child:

nodeObject.all('.custom-child');

It's interesting to note that most of the methods that are on Nodes are also on NodeList. The ones that aren't are usually just the getters where it wouldn't make sense for a collection of items to return the data from any one item.

Meaning this: it makes sense to have a collection, like nodeListObject, which contains 5 div elements, and when you call nodeListObject.setStyle() for that style to be applied to all 5 elements, or if you call nodeListObject.append('<b>test</b>') for it to append a new b element to every item.

But it doesn't make much sense to do: nodeListObject.getStyle('backgroundColor'). What should it return? The first item in the collection? The last item?

And since it's insanely easy to do this instead:

nodeListObject.item(0).getStyle('backgroundColor')

it just makes more sense not to add the methods onto the NodeList to avoid confusion when getting data out of an element.

Getting properties

Now, here comes a really interesting part. Since nodeObject is a wrapped element, you can't just do nodeObject.id to get the id or nodeObject.parentNode. If you tried that, it would return undefined.

Instead, we do nodeObject.get('id') or nodeObject.get('parentNode').

Here's what is REALLY cool about using the getter: nodeObject.get('parentNode') will return another wrapped Node object, and if it's a collection, like nodeObject.get('childNodes'), it will be a wrapped NodeList object.

So all of the DOM properties are available.

EVEN cooler:

get will accept a dot (.) separated list of properties and traverse it for you. So let's say you know you have an item exactly three parents up, and want to set the background color to red:

nodeObject.get('parentNode.parentNode.parentNode').setStyle('backgroundColor', '#f00');

Interaction time

We've touched on how to wrangle the elements on the page. What about adding an event to it, such as doing something when a user interacts with it?

It's actually pretty simple. Every Node and NodeList has a method called on() that let's you, appropriately enough, do something "on" that event.

Let's say I want to alert "Hello" when a user clicks on the nodeObject

nodeObject.on('click', function(event){
   alert('hello');
});

Or let's say we want to add a border when a user first moves their mouse over an item:

nodeObject.on('mouseenter', function(event){
   this.setStyle('border', '5px solid #555');
});

Notice how the "this" object is used without wrapping it? It's automatically wrapped for you to be a Node object, which is incredibly convenient.

But what if, on the off chance, you *must* get the original DOM object. You can do nodeObject.getDOM() and it will return you the underlying DOM element.

This also applies to NodeList objects as well.

So if you do A.all('div').getDOM() it will return you an array of plain DOM elements.

What if about if you need to remove an event?

Let's say you do this:

nodeObject.on('click', myFunc);

you can detach the event by simply doing:

nodeObject.detach('click', myFunc);

or you could even just remove all events by not passing a second argument, like this:

nodeObject.detach('click');

What if you want to do some work on document ready?

You can do:

A.on('domready', function(event){
   // More work here
});

Now, there are times when you want to both load some modules and fire your callback on DOM ready, so here is how you would do that in Alloy:

AUI().ready('event', 'node', function(A){
   // This code will fire on DOM ready 
   // and when this modules are ready
});

Here's an interesting example. Let's say you want to listen on the node for only a specific key combination. For instance, you want to only fire the event when the user presses the escape key, but only when holding down the shift key.

Here's how you would listen to it:

nodeObject.on('key', function(event){
   // escape + shift has been pressed on this node
}, 'down:27+shift');

Now here's another use case some might be curious about. What if you want to prevent the default behavior of an event, for instance, if you want to stop a link's href from being followed?

nodeObject.on('click', function(event){
   event.preventDefault();
});

In Javascript, events bubble, which mean that by default, an even on one element also happens on every element that contain it, so if you click on a link, it will also fire an event on the body element as well.

You can stop your event from bubbling though, here's how:

nodeObject.on('click', function(event){
   event.stopPropagation();
});

You might notice that these are the same methods that exist in the W3C specification, but they're normalized to work the same in all browsers.

But there's also a shortcut if you want to just preventDefault and stopPropagation, which is like so:

nodeObject.on('click', function(event){
   event.halt();
});

Event delegation

Speaking of event bubbling, built into Alloy is event delegation. Event delegation is a technique that let's you can attach one event to a container but have it fire only on the children elements.

Imagine you have a list, and a lot of LI elements inside of it. You could add a new event listener for each element, but as your list grows, the number of listeners will also grow, as well as memory consumption.

And let's say you add elements via ajax, it's a pain to have to reattach events after every update as well.

So let's say go through an example. Let's say that we have this HTML:

<ul id="myList"><li>Test</li></ul>

Here's how we would use delegation:

var myList = A.one('#myList');
myList.delegate('click', function(event){
   alert(event.currentTarget.html());
}, 'li');

Notice a few things. One, we're calling a method called delegate, but it's very similar to "on". In fact, the only difference to the "on" method is that the third parameter is a selector that we will test to make sure the element matches before firing the function.

But also notice that we're referencing event.currentTarget. This is a property that will always point to the element that you are currently listening for, even inside of the "on" method, so I recommend using it.

But now that we've added our event, if you click on the list item, it will alert the contents of that item. Now let's try this:

myList.append('<li>Test 2</li>');

It will add another list item, and when you click on this new item, it will alert "Test 2", without having to reattach the event.

Conclusion

Hopefully this helps show you some of the helpful ways you can work with elements on your page, and help get you up to speed.

AlloyUI

Company Blogs 14. März 2010 Von Nate Cavanaugh Staff

 Hi all, there has been quite a long delay since the last blog post. For those of you who weren't able to check out the webinar, the reasons behind the long silence in my blog postings has been due to the work on the AlloyUI framework.

Over the past 6 months, Eduardo Lundgren and I have been furiously working away building a unified UI library on top of the revolutionary YUI3.

So today, I wanted to answer a few questions about it (in case you have yet to check out the webinar), and to also prep for the coming weeks and blog posts.

The simplest way to describe Alloy is that it's a library of tools, a collection of frameworks, put together and built into one unit. We're taking years of building UIs and the problems we've kept solving and boiling that knowledge down, and releasing it as a separate project.

One of the most common questions I get is: "So what about jQuery and jQuery UI?" You will still be able to use jQuery and jQuery UI (or any javascript or front end library of your choice) in Liferay.

We have however stopped using it for our portlets and plugins, and are instead building everything on top of Alloy.

Liferay is a platform, first and foremost. As such, we want people building on that platform to use the tools they feel most comfortable building with, be it Icefaces, Vaadin, jQuery, dojo, etc.

Much in the same way that in OSX, developers can use other windowing and widget toolkits, such as Swing, to build applications. However, if you want to really leverage the power of the operating system, and really want to have the nicest looking applications, you're going to use Cocoa.

That's what we want to accomplish with Alloy.

Another question I've gotten is why YUI3? There are numerous other javascript libraries on the market, why build on top of one that is a relative newcomer?

One of the questions we definitely asked ourselves was how many existing components and widgets does the library have? But what was a much more important factor was how quickly could quality production level widgets be built?

How clear was the thinking behind the widgeting system? How much documentation was there for it?

These were questions that YUI3 had great answers for. Answers so great that Eduardo and I were able to build roughly 60 utilities and widgets in about 6 months time.

We also looked at the team behind the library, and the types of problems they were solving. Instead of being run by an ivory tower, or an unmanageably large committee, it's developed by a productive "pizza-sized" team thats renowned for leading front end innovation on the web.

They're truly solving problems ranging from the small to the large, which runs very closely to how Liferay works. We wanted a system that could be used on the small scale (let's say you only want to sprinkle in very simple interaction to a website for mainly displaying content), or on the large scale (as in an application interface).

YUI3 is designed to be stretched to those different scenarios seamlessly.

The other question I've gotten is "can it be used outside of Liferay?" and the answer is a resounding yes. We have actually developed Alloy in entirely different repository of our SVN, and maintain it as a third party project that Liferay consumes.

We're doing this because we feel that the patterns we're solving with this aren't specific to Liferay, but are common across the web, and are useful for multiple people.

But this also allows non-Liferay developers to get involved and contribute ideas and solutions so that the pool of ideas doesn't stagnate, but is continually refreshed with fresh input.

However, as great as all of this sounds, there is one area where people people may be concerned, which is documentation.

We are currently working on generated API documentation (another benefit of YUI is that they release many of their build tools, one of them being their documentation builder), and we're aiming to have those done this week.

We also have quite a few demos available in the downloadable zip that contains examples along with code on the demo page to get them to run, but in all honesty, we could do a lot to improve them (and are in fact working on them).

There's also the YUI documentation on http://developer.yahoo.com/yui/3 which all applies to Alloy (since Alloy is built on top of YUI, Alloy only adds to YUI and doesn't take anything away).

However, that can be a lot to wade through, so over the next two weeks, I'm going to post a series of 10 blog posts going over how to do tasks you're familiar with, as well as some that are brand new.

So here's what we'll learn:

1. Working with elements and events

2. Ajax

3. Plugins

4. Widgets

5. Utilities

6. Animation, Drag & Drop

7. Layouts & Forms in CSS

8. Using Alloy taglibs in JSPs

9. Advanced beauties (we'll look at DelayedTask, AOP, OverlayManager, Queue & AsyncQueue, and DataSet)

10. Odds and ends (a few like Char Counter, History, Cache, Profiler, and DataType parsing)

After all 10 posts, I'm hoping you'll have a more thorough understanding of Alloy, and hopefully some ideas on how it can help you.

These are going to be written from a very technical point of view, so if all of my talks so far have been too light on exact details, be prepared for actual code to make it's way in.

Looking forward to seeing you here!

A new Liferay Wallpaper

Company Blogs 19. August 2009 Von Nate Cavanaugh Staff

I'm sorry it's been so woefully long between updates here. I've been furiously working away on something that I'll have more details about in the next couple of weeks, but I think you will find that interesting.

Today's post, however, is of a different bent. You may remember the wallpaper I created back in December. It seems like it was pretty popular, at least internally (they're probably being nice ;), so I decided to create another one last night.

I was waiting for some code to compile and hopped into my Google Reader to check some feeds and try to clear down the unread feeds. Google stopped counting, but I'm guessing it's 3-4000 unread items (try 5,552 by current count...).
As I was cruising through my design feeds, inspiration struck, and I started sketching out the idea. It's so rare that I get to create artwork anymore, that I couldn't help thinking about how I wanted it to look.

So last night I went in and got it done, and ended up staying up until 2.30am, which reminds me a lot of how college used to be :)

For lack of a better name, I'm just calling it Liferay of Life. No reason, just pure exhaustion at this point, but here it is:

Liferay of Life wallpaper

Clicking the image will take you to the Large WideScreen version, but if you want more options, you can click here:

Liferay of Life Wallpapers

This link includes a wide range of resolutions for different ratios and even one formatted for an iphone. I hope you enjoy it :)

IE8 is out. What does it mean for your theme?

Company Blogs 19. März 2009 Von Nate Cavanaugh Staff

If you're as cutting edge as our own Jonathan Neal, you might have downloaded the IE8 final that was released this morning at 9am PST.

IE8 is a HUGE improvement towards standards, and while it still has not caught up with Firefox, Safari, Chrome or Opera, when it comes to IE, we will take what we can get :)

However, since it's more accurate with the standards, you may have noticed that your theme could be off in IE8 compared to IE7.
If this is the case, there's a good chance that any IE hacks that existed in your theme to fix problems that were in IE7 and below. A good example of this would be using the .ie selector in your CSS.

The problem with applying a hack to ALL IE's via .ie means that when future versions, such as IE8 fix the problems you were hacking, these will break your theme for the new IE.

So what's the fix? It turns out there are 2 good fixes, one for the short term and one for the long term.

The long term fix is to of course change your selectors from being broad to being specific.
For instance, if you are using a selector like this:
.ie #wrapper {
}

And that selector is causing issues, you should change it to be:

.ie7 #wrapper, .ie6 #wrapper {
}

This will make sure that the selector only applies to specific versions of IE that have the broken functionality you're addressing.

But this might take some time to get around to, and we all are a bit busy, so there is a quick short term fix that will solve the issues.

In the <head> of your theme, you can add this:
<meta http-equiv="X-UA-Compatible" content="IE=7" />

That is a new tag Microsoft has added to IE8 so that you can tell IE8 what rendering engine to use. In this case we're telling it to use the IE7 rendering engine instead of the more standards compliant IE8 engine.

Changes in Liferay's front end

Company Blogs 18. Februar 2009 Von Nate Cavanaugh Staff

If you follow our trunk as ardently as I do (and really, who doesn't) you might have seen some front end changes, and there might have been some theme changes that are a little different from what you were expecting.

There are some big happenings going on in the front end of Liferay, and I'll have some more details for you shortly, but I just made a commit that I wanted to talk about that I think will make developers quite happy.

I added a file called deprecated.js to portal.properties. This file is a place where we will deprecate our changing Javascript so that that the upgrade path between major versions is a lot smoother.
This file will be updated any time something from the Javascript API has been changed from the previous version.

Here's how it will work:

As soon as a piece of script is changed from a previous version, such as a variable being removed, certain options changing, etc, we will place code inside of deprecated.js that will make the Javascript work for the next version.

So let's go through an example. One thing we've been wanting to do is clean up the amount of room we take in the global namespace. We have a few outstanding global variables, such as LayoutConfiguration, or themeDisplay and we want to clean those up to protect from future namespace collisions.

So assuming we move LayoutConfiguration to be accessed from Liferay.LayoutConfiguration, we would add code inside of deprecated.js that will keep the backwards compatibility for 1 major version.

So in 5.3, the deprecated.js might look like this:

//To be removed in 5.4

// LPS-1234
LayoutConfiguration = Liferay.LayoutConfiguration;

// LPS-4567
themeDisplay = Liferay.ThemeDisplay;

 

However, in Liferay 5.4, this entire block would be commented out.
It will still be in the file, but it will be commented out. The reason behind this is so that developers who are running behind in upgrading between versions can still uncomment their code and have things work for the most part. It's kind of our way of letting you know what we recently removed.

In Liferay 5.5, this block would be removed completely.

So in any version from 5.3 on, you would be able to see what is scheduled to be removed and what, if anything, was removed in the previous version.

There are a couple of reasons for this change, and I wanted to talk about them.

One of the areas where I believe Liferay shines is in innovation. We are able to rapidly change faster than any organization I've worked for. However, whenever things change rapidly, not everyone is able to keep up. It's very easy as a developer to forget this, unless you're the developer who is trying to keep up :)

One of the reasons we introduced Liferay Enterprise Edition was because people, especially enterprises, need a stable and robust way to get bug fixes, security patches and performance enhancements, without having to upgrade an entire revision number.

In that same vein, we're adding the deprecated.js, not just as a service to EE customers, but as part of the general process.
So we have spent some time now trying to come up with creative ways to allow innovation, while making sure that we make upgrades a lot smoother.
I'm not trying to portray this as the end goal in and of itself, but more or less a piece of the process and a goal we're striving for.

We still have a lot of areas where we can improve, but this is more or less to let you know we are, and the steps we're taking in getting there.

What are some other ideas? Bryan Cheung and I were talking earlier today about communicating to folks that don't have the time or mental bandwidth to scour through commit logs and LPS tickets. What we were wondering is, similar to Liferay's twitter feed what if we had a Liferay Developer twitter account? Is that something anyone would be interested in?
Basically, it would contain short snippets and bursts on stuff we're working so you can keep a loose idea of the happenings going on with the development team.

For instance, if I add some awesome functionality to the JS, but it doesn't warrant an entire blog post, I could tweet something like "Added the ability to create ajaxable panels: LPS-12345" which would have a link to the ticket where I discuss what was added.

Would this be helpful to anyone?

Also, in the lead up to Liferay 5.3, there will be some very big UI changes coming, and I will blog in more detail about that, but the other plan is that as everything progresses, I will be blogging about the individual changes and milestones.

Another option that has completely changed my life and sanity is that Mike Young recently installed Fisheye to watch our commits. I think the feature I have most used is the RSS functionality. I subscribe to commits by directory and also by committer. For instance, I am usually interested in a couple of commits. Anything committed by my friends Eduardo Lundgren or Peter Shin, anything committed to the portal-web directory and anything committed to the themes or to the js directories (I know there is some duplication there, but I'd rather have a bit more info).

If you want to stay on top of what people are doing, it's an awesome way to keep your ear to the ground.

So there are some ideas. I would love to have feedback, especially on ways to better communicate with you guys that extends beyond our normal routes.

Even MORE Performance fixes

Company Blogs 12. Januar 2009 Von Nate Cavanaugh Staff

It's hard to contain my excitement about what I am going to share.

Brian Chan (along with Eduardo Lundgren) have just recently committed a change that I (along with any other person who has to deal with themes) have been pestering him for quite some time, and it is my great pleasure to finally be able to tell you guys he has implemented it.

What is it, and why should you care?

One of the less friendly aspects of how themes and plugins work in Liferay is the packing of CSS and Javascript. From 4.3.x and later, there has been some files that have caused an unending amount of confusion and extra work for people.
I'm talking specifically about *_unpacked.* and *_packed.*

Perhaps you've looked at your deployed theme and seen a everything_packed.css and everything_unpacked.css as well as seeing around files like packed.js, unpacked.js, everything_packed.js, etc.

These files have been created at build time and they are the optimized versions of those files so that you don't make as many http requests and don't download unnecessary whitespace and characters.

Well, the change that Brian committed now completely removes those files and they are handled for you automatically.

Think about something that happened to my good friend Ray Auge not too long ago while we were doing some work for one of our critically acclaimed clients. I can't say who, but practically everyone has heard of them. Anyways, they have a theme hot-deployed and Ray made some changes to the theme and IMed me one weekend wondering why the changes weren't being picked up. He kept changing the CSS in custom.css and nothing happening.
Only because I had been touched by this little bug had I known what was causing it.
But anyone that knows Ray knows that he's one of the smartest guys in Liferay. There's no reason in the world why he should have been banging his head against this, and I wish I could say it was a lone incident.

The reason he wasn't seeing his change is because the client was in production mode, with theme.css.fast.load and javascript.fast.load set to true (which is what it should be for a production environment, and I have to applaud everyone that uses these properties in production).

But what that meant was that since the packed css files were only being created when the theme was built, the changes were never actually being copied into everything_packed.css. So not only would he have to rebuild the theme from the plugins SDK, he would also have to redeploy the theme. And as we all know, sometimes we don't have the plugins theme available.
Sometimes we have to do maintenance on a theme or plugin someone else has developed.

So how is this fixed now?

Brian Chan has made it so now those files are automatically created when the server starts up. How would Ray resolve this if he were to do this now?
All Ray would have to do is restart the server or if that was too much, and he had the original theme WAR file, he could just redeploy the theme.

Either of those is far preferable to having to rebuild the theme from the source, and far less confusing.

Brian and Eduardo have been really hard at work doing this and even more performance fixes over the past couple of weeks and I've been amazed at how snappy our website has become.

So what's the information on getting these fixes? These changes will be available in 5.2 which is coming soon (really :), or, if you need an enterprise-level and robust way to get these fixes for your deployment of 5.1.x version of Liferay, then now is a great time to get Liferay Enterprise Edition. You can get long term bug fixes, security patches, and performance improvements like this in a safe, reliable manner with our Enterprise Edition, so I would highly recommend it.

And when you get a chance, shoot by Brian and Eduardo's pages and thank them for getting this in.

Liferay wallpaper

Company Blogs 28. Dezember 2008 Von Nate Cavanaugh Staff

There are a ton of amazingly creative people here at Liferay, and I'm always stunned with the stuff that people create.

Last night I was doing a ton of coding and some inspiration struck to make some Liferay "fan art". I don't get the time to do as much artwork as I used to, so it was a nice treat for me.

So, it totally is not "corporate" and doesn't really follow the Liferay branding guidelines, but like I said, it's "fan art" and normal rules don't apply. I wanted to make something a bit edgier than we're used to having.

I've created a package with common wallpaper sizes that you can download here.

Here is a brief preview:

Is this site running Liferay?

Company Blogs 20. November 2008 Von Nate Cavanaugh Staff

It's been a while, but trust me, we've been working like crazy to get 5.2 out for your using pleasure.

But, I arrive with a bit of a gift, especially for the marketing folks (or anyone curious). Have you ever wondered if a website was running Liferay?

Well guess no more. I've written a Greasemonkey script that will tell you if a website is running Liferay or not. What's Greasemonkey you ask?
Only about the absolute coolest extension for Firefox known to man (yes it requires Firefox).

So what do you do?

If you have Greasemonkey installed, you should just skip to Step 3.

Step 1: Go to https://addons.mozilla.org/en-US/firefox/addon/748

Step 2: Click the Add to Firefox button, and install the plugin. It will notify you to restart firefox.

Step 3: Once you've restarted, go here: http://userscripts.org/scripts/show/37259

Step 4: Press the Install button at the upper right

Step 5: Agree to the install

 

Step 6: Visit a website (like http://liferay.com)

 

Step 7: Enjoy the info:


And that's it. You can hide the display (and it will popup on every page load, or you can hide it for the domain, which will hide it for 1 year. If you want it to show up again, just clear your cookies, and it will popup again for any domain you have hidden it for.

 

Enjoi!

The new Liferay.com

Company Blogs 8. Oktober 2008 Von Nate Cavanaugh Staff

Hi all, it's been a while since I've blogged, and there has been good reasons. Liferay is taking off and we are growing like crazy, and as such, I've gotten less time to blog.
But there has been a special occasion for this blog, and that is the official release of Liferay.com, and I wanted to talk a bit about it.

So, back in May we started talking about our current website internally amongst a few of us planning it out. We have long known internally that there were some issues with our different sites that no matter how much Bmiller would polish it up, they wouldn't go away. And this wasn't Bmiller's fault, always gave us more than we asked for, but there were always limitations of time and resources that would keep us from making deeper changes.

So back in May, there wasn't any internal pressure to get a new website out, and we were content with the branding for the site, and instead of waiting until we HAD to redesign the site, Alice, Bmiller and I got on a conference call together on a cloudy May day and hashed out how we would design the site.
One thing we didn't like about the old site was that it would hit you with a lot of information when you first visited, and a common complaint was that people would visit and not quite sure what our company provided.

Part of this was due to information overload, part was due to just general information architecture issues.

So out of that first day came our initial wireframe for the front page.



We knew we wanted to be direct and to the point with our message and branding, and also consolidate our the amount of pertinent information on the front page. In fact, we wanted all top level pages to be succint and concise, and to help navigate most users to where they would like to go, but also make the deeper areas of the site discoverable.

So, in short order, Bmiller delivered this to my inbox to show me the progress:



A little after this, Bcheung and Cecilia got involved and we started getting all sorts of pressure to make all kinds of changes.

So one little tangent here that I'd like to touch on: Conflict is great.
In a lot of companies, organizations and even personal relationships, conflict is looked at as a bad thing, and avoided quite often. Let me just say Liferay is not that kind of company. Conflict can be constructive or destructive, depending on how it's handled.

And for this site, there was a lot of (good) conflict. There were literally days where we would argue for a couple of hours over the tiniest of details, each of us representing a different opinion, each of us having our own unique perspective.
And what came out of it was a truly superior product. I know for a fact that I argued for different things that, had they been unquestioned, would have kept the site from being as amazing as it now looks.

Or as Bchan told me during my interview almost 2 years ago: "The best idea wins."

Bcheung and Bchan both really pushed this version of the site and without their constant driving and pushing for improvements and changes, it wouldn't be a fraction as great as the final product is. It's because of their leadership, patience, high standards, and determination that we were able to get this site done.

Bmiller outdid himself again. Liferay is so blessed to have an art director on his level, and his passion for what he does is inspiring, and his patience with the rest of us whose eyes are less refined has made this process go so much smoother than it could have.

Alice deserves a TON of credit. She has been pushing us for the longest time to improve the site, and to do it right, and her input and marketing chops really provided so much to the site's development. She worked incredibly hard on the site's IA and put in a ton of time making sure the site's content was easy to find.

Ryan Park is a machine. I think he knows how to use Liferay's CMS better than most of us internally, and he has cranked away. Thanks so much Ryan for your hard work, insights and always upbeat attitude no matter how much IE6 was trying to beat you down.

And of course, there is so many more people to thank, and so many contributed ideas, opinions and constructive criticisms that truly brought out an amazing site.
If I'm forgetting to give proper credit, please let me know.

All I can say is awesome job everybody, this truly is the best version yet, and I am blown away with how great it is :)

Oh yeah, about the auto-save

Company Blogs 23. Mai 2008 Von Nate Cavanaugh Staff

I just wanted to drop a couple of notes about the auto-save that I didn't mention before....

1. I didn't do the bulk of the work
That honor goes to Jonathan Neal. Bchan did a lot and I made a couple of javascript changes and tweaks, but the work load credit goes to Jon for actually laying the foundation and getting it done. Go hit up his wall and tell him great job :)

2. There was a race condition
There was a bug where you had to time your save JUST so, otherwise it could stay in the state of perpetual draft. We've fixed it in trunk, and will be pushing it live here to the site very soon.

3. We changed the interval time
Instead of 10 seconds, it now saves every 30. This seems a bit more realistic to me, but we're willing to hear you guys out if someone HAS to have it saving every 10 (and can make a good general case for it).

4. It now is smarter
It now checks if you're editing a draft, to save only if the content or title has actually been edited. Otherwise it will patiently wait until you have edited.

It's stuff like this that really just makes my day, getting to work with so many smart people who can develop rapidly and get stuff out the door.
 

Autosave comes to blogs

Company Blogs 20. Mai 2008 Von Nate Cavanaugh Staff

This is a feature I know many of us have been desperately wanting for quite a while. In fact, it's something that many of us miss from other blogging apps, and we now have it.

So, to kind of show off what we have, I'll include screenshots from this very blogging session.
Isn't this awesome?!

Notice the time? Every ten seconds, we automatically save your draft until you click publish. This means that if your browser locks up (thanks a ton, Firefox 3 b5), or your whole system wonks out (*cough*Vista*cough*) you'll be assured that you'll have an up to date copy of your blog entry waiting for you to go back to it.

You know what else is great about it? Let's say you go back to your draft, and you decide the title isn't right, it automatically updates the friendly URL for you as well.

Is that more bang for your buck or what?

Now what happens if you go to see your blogs, how do you know which ones are your drafts? Wonder no longer. Take a look:

Man, Nate is one snazzy UI designer ;)

See how it has a grey box around it with the dark grey text and the icon? That's how you know it's not published yet.

We're contemplating a few ways to mark it as a draft manually, but we shall see.

Another question that might come up, what happens if you're editing an already published blog entry, does it autosave that? No. Mainly because of the big issue of let's say you accidentally type something up that the world wasn't meant to see, you definitely wouldn't want the world to see it, let alone see it in a state of flux. Or if you go in and edit, and accidentally delete everything you wrote, you sure wouldn't want that autosaved.

We have so much awesome stuff coming down the pipe, but I don't want to mention it just yet. But let's just say that I think it will just solidify even more why we're the number 1 open source portal.

 

How can jQuery help me today? pt. 1

Company Blogs 24. April 2008 Von Nate Cavanaugh Staff

So, when we adopted jQuery, I think I should have done more to evangelize it within Liferay. For most web developers in the world, it's really taken off in popularity because the concept is tied to an existing development paradigm, eg. CSS.

However, that doesn't mean everyone in Liferay is familiar with CSS. But the benefit for the backend folks is that it let's you focus on only having to learn one set of selectors based on an existing and widely supported standard, rather than having to relearn a whole new language paradigm.

So first things first, I'll do a quick lay of the land with jQuery that will help you get up to speed, and then I'll launch into examples that can show you some useful tips that can help you get stuff done today.

First, the concept behind jQuery is that you operate on the DOM (the structure that represents HTML elements on the page).
In normal Javascript development you do everything on the DOM elements directly.
For example:
document.getElementById('banner').style.display = 'none';
document.getElementsByTagName('body')[0].className += ' classic';

Those snippets there find the elements in the DOM, and modify their properties directly. Now, there are a lot of ways to do things like this, and honestly, a lot of them get confusing really quickly. For instance, if you want to change an attribute on an element, some people set the property directly, some people use setAttribute(attr), etc. And every browser has quirks related to this.

So the purpose behind jQuery is to "query" the DOM, and return you back a set of elements (even if its only one element) and operate on that collection.

You can think of every jQuery object as a bucket of DOM elements, and every jQuery method you do on that object is automatically done on every element in the bucket.

One thing commonly done in web dev is to get all elements that match some criteria (querying the DOM), and often, you want to grab everything with a certain class name.

In CSS you would do it like so:
.liferay-element {}

In normal JS you would do it like this:

var allElements = document.getElementsByTagName('*');
var matchedElements = [];
for (var i=0; i < allElements.length; i++) {
    var el = allElements[i];
    if (el.className.indexOf('liferay-element') > -1) {
        matchedElements[i] = el;
    }
};

And then you would have your collection of matched elements.

So how would you do this in jQuery?

var matchedElements = jQuery('.liferay-element');

Looks pretty familiar, right?

So now that we have matched elements, what could we do with this? A whole ton of good stuff.

Let's say we wanted to fire an alert box when we click on each of those elements, how would we do it?

matchedElements.click(function(){
    alert('you clicked me!');
})

Or what about adding another class to each element?

matchedElements.addClass('new-class');

Or wait, let's say that we have a collection, and we want to make all of them have a red border, BUT, if the element is a span, we want a blue border?
Easy-peasy-lemon-squeezy.

matchedElements.css('border', '1px solid #f00');
matchedElements.filter('a').css('border-color', '#00c');

Notice the filter portion? The filter method reduces a current collection down to a smaller set based on a jQuery selector (or other things, but you can look at the documentation [http://docs.jquery.com] for more info).

So you may be saying "Okay Nate, that's great and all, but you're really boring me here. I don't want to add ugly borders to my elements, and I don't want to hear you ever say that easy-peasy line again, I want to DO STUFF!".

So let's do stuff.

One common thing that we've all done numerous times is use a checkbox to select all checkboxes in a set, sort of a select/deselect all option.

So let's assume we have a group of checkboxes, that don't have a classname, don't have an id, and don't have the same name.
But we know the name attrbiute all starts with the same thing, in this case:
"<portlet:namespace />check"

So, we have our checkbox that acts as the trigger, and but it doesn't start with the same name.

Our example HTML would be this:

<input type="checkbox" id="<portlet: namespace />trigger" />
<input type="checkbox" name="<portlet: namespace />check1" />
<input type="checkbox" name="<portlet: namespace />check2" />
<input type="checkbox" name="<portlet: namespace />check3" />
<input type="checkbox" name="<portlet: namespace />check4" />

Here is how we would toggle all of the checkboxes in jQuery:

var trigger = jQuery('#<portlet:namespace />trigger');
trigger.click(
    function(event){
        jQuery('[@name^=<portlet:namespace />check]').attr('checked', this.checked);
    }
);


So let's go by that, line by line, so we know what we're doing:

var trigger = jQuery('#<portlet:namespace />trigger');

The # sign in CSS signifies an ID, so in this case, we're getting an element by it's ID.

trigger.click(

We're now assigning a click event to our trigger. The events that we add need a function passed in, and the function that is passed in has two special things about it, 1 is that the argument it gets is the event object. I won't go into detail here about what the event object has on it, but it let's you do all kinds of things.
2, however, is that the scope of the function is changed a bit so that "this" points to the element that you're working with. So in this case, this points to the DOM element of our trigger.

function(event){
As mentioned above, here is the start of our function, with the event parameter.

And here is where the magic happens:

jQuery('[@name^=<portlet:namespace />check]').attr('checked', this.checked);

That's kinda nuts right?

Well, jQuery lets you query objects based on parameters, and you can also do minor regular expressions in it. CSS also allows you to do this (in every browser, of course, except IE 6).
It's not a direct port of CSS in this case, but of xpath, in that you have to use the @ sign. The newer versions of jQuery don't require the @ sign, but in Liferay pre-5.1, we have to use the @ sign.

So I'll break this line up:

jQuery('[@name^=<portlet:namespace />check]')

Find every element whose name attribute begins with <portlet:namespace />check. The begins with is done by this part:
^=
if we wanted to say every element whos name ENDS with, we would do:
$=

.attr('checked', this.checked)
This sets the checked attribute of every element we found to whatever the checked state is of the current element.
So if the current element's checked attribute is set to false (unchecked) all these elements will be unchecked. If it is checked, so will all of those elements.

Okay, but if you ask me, that's kind of a lame example. That's something we've been doing since time immemorial(like since 1999 when Ben Franklin gathered all the animals on the Ark and crossed the Delaware river), and while it's fast, it's not like everyone is screaming "HELP ME CHECK LITTLE BOXES!"

But what if we wanted to do an ajax call on a page that updated a div with the results and show a loading animation so the user isn't wondering what's going on?
Well first, we need to make sure the URL that returning the HTML we need.
Secondly, let's assume the div we want to update has an id of portletBox, and the link we're clicking points to the URL resource, and has an ID of linkTrigger.

Here's our HTML:

<div id="portletBox">
    Existing Text is here....
</div>

<a href="http://liferay.com/test/?p_p_state=exclusive" id="linkTrigger">Click me to update our text</a>.

Here's how we'd do it:

var linkTrigger = jQuery('#linkTrigger');
var updateDiv = jQuery('#portletBox');

linkTrigger.click(
    function(event) {
        updateDiv.html('<div class="loading-animation"></div>').load(this.href);
        return false;
    }
);

Let's go down a bit at a time:

This of course grabs our elements to work with.

var linkTrigger = jQuery('#linkTrigger');
var updateDiv = jQuery('#portletBox');

Now we'll add a click handler
linkTrigger.click(
    function(event){}

This is where we do our work
updateDiv.html('<div class="loading-animation"></div>').load(this.href);
return false;

Let's analyze this a tiny bit. When we click the link, we're first grabbing updateDiv and replacing all of it's HTML with a div that handles the loading-animation.
Right on the end of it, we're doing .load(this.href), which performs an AJAX call and updates the jQuery elements with the results of the AJAX call.

Lastly, we have "return false;". What does this do exactly?
Well, in every browser event, there is a default action. In the case of a link, the browsers default action is to follow the link. However, in our case, we don't want to follow that link, but instead stay on the current page.
When you return false, it prevents the default action from ever taking place.

This also works with every event, for instance, with forms, if you want to do some stuff when you submit the browser, but want to prevent the actual form from submitting, you would return false.

So, that about does it for right now. I'm going to think up some more (useful) examples of things jQuery can do to make your development life a lot easier.

Is there anything you'd like me to cover, for instance, doing animations, or manipulating html elements, etc?

China

Company Blogs 10. April 2008 Von Nate Cavanaugh Staff

Hey there, O faithful reader...
Man, what a long time since actually posting. Since I last posted, quite a lot has happened, and included in that was the fact that my wife and I visited China with Brian and Caris, Bryan Cheung, Alice, and Dave, to go spend time with Mark, Ivan, and Shepherd.

What an experience! There are a few times in everyones life when travel truly changes you in ways you were completely not expecting.
The China trip was one of those times.

To begin with, nothing really changed my view of flying during this trip. Except maybe that I need a height-ectomy. Flights both ways were pretty much par for the course of my normal flight experiences: painful.

But the actual trip more than made up for it. I will also say that the pleasure of this trip is completely due to the grace of the people we were visiting with. Had Jessica and I gone by ourselves, we no doubt would have come back with a vastly different experience.
We were made to feel completely at home, and even though our Mandarin is limited to Ni hao and Xie Xie (Hello and Thank you), we were able to get by because of the patience of the people we were with, and the patience of the Chinese :)

The first day, I will say, was really rough. We landed with too little sleep, and the city was very overwhelming at first. Luckily though, we fell into sync with the local timezone the night we got there, and were not jet lagged at all. Jan--March2008 072



This was also the very first time in my life that I've traveled internationally and did not get sick, and that is quite a big deal to me :)

I got to me a lot of really great guys, and I will always remember Steven, Gavin, Sai, and Dale because their patience with the language barrier, and their hard work learning the Liferay theming system. They were incredibly friendly, and I do miss being out there.

Surprisingly, I don't think my comfort level was stretched too far. More than anything, I was really curious about things, but the odd thing was that while I thought the whole personal space thing would cause me grief, it's strange how oddly liberating it is to be squashed in a moving metal can with other people and have no concept of moving your arms, let alone "personal space".
DSC01246
The food there was quite good. Some things, not so much, but for the most part, most everything I tried, I really enjoyed (of course, the same is said when I eat anywhere, which suggests I'm not so much a worldly foodie, but more just a person who likes eating).
Of course, eating at a Chinese McDonalds and Pizza Hut were actually pretty strange.
The food outside of the chains were where the best food was found, though. Dumplings, wontons, and the normal Chinese fare we're all so familiar with was there, but so was live sashimi (where they cut off the flesh of the fish and leave it there on the plate moving its mouth...), chicken-head-kabobs, and all sorts of different and exciting items.
Jan--March2008 030
DSC01283
I DID manage to get my Diet Coke fix. It was glorious.
However, the first day went a bit rough in getting it, but due to the persistence of our friends, we did manage to complete the quest, as you can see below. The first machine I tried took my money, but wouldn't give me the Diet Coke. I had to settle for a freakin Fanta! But we would prevail:

Also, our office there is freaking AWESOME. I don't envy the internet speeds, but the office is so nice.
dalian, China Mar08 042
dalian, China Mar08 051
I could go on and on with vivid detail, and I might in future posts, but I will say that I learned some really amazing things while I was there.

One is that how I think I am coming across to people is often very different than how I actually am. I learned that when I interact with people, a lazy tongue and an eager ear go further than they're often given credit for.

I also learned that the people I am lucky enough to not only call co-workers, but friends, are insanely patient, loving, and kind, which humbles me that they deign to consider me a friend as well.
DSC01290 (2)
Lastly, I learned that my wife is much more adventurous and patient than I really knew, and I'm not only glad she got to go for this trip, but just overall incredibly grateful for the opportunity to experience it.
Jan--March2008 027

Random theme related tips

Company Blogs 11. Februar 2008 Von Nate Cavanaugh Staff

People seemed to like the "hey buddy, want to hear a secret?" nature of my last post, and in that tradition, I will try to drop some tips about some of the lesser known ways of playing around with the Liferay themes.

Browser Selectors
In Liferay since 4.3, we've used a method of browser detection called Browser Selectors. What this does is allows you to style your theme for different browsers, different operating systems, and whether or not the users have javascript on or not.

This allows you to style for not only a certain browser, but also a certain browser version.
 

The selector class always exists on the html tag, which means that the browser metadata used in the class name will always be first in the selector chain.
For example:

.js #navigation{}
This styles the navigation element only if the user has javascript turned on.

.ie #navigation{}
This would style the navigation element only if the user is using (any version of) Internet Explorer.

Also:

.win.firefox2 #navigation{}
This would style the navigation element only if the user is using windows, and Firefox version 2.

This is much cleaner than using hacks in your css to target a browser.
Mind you, these will only work if the user has Javascript turned on.

Turning off portlet title editing
Sometimes you don't want your theme to have editable titles (you know when you click on a title and the admin user can edit the name?)

The way to do this is to open up your portlet.vm in your theme's templates/ directory, and add this class name "not-editable"

So the HTML would look something like this:
<span class="portlet-title not-editable">

Turning off dragging and dropping of navigation, and turning off the Add Pages functionality
Sometimes you don't want this added functionality. It's great sometimes, but sometimes, it can be a pain. There are two classes that exist on the navigation div that control this functionality.
These classes are sort-pages and modify-pages.

"sort-pages" adds the ability to drag and drop your navigation elements, and if you remove this class, you'll be able to add new pages on the fly, but you won't be able to sort them.
"modify-pages" adds the ability to edit the page's title, and adds the "Add Page" button to the navigation as well.

Turning off the Drop Down dock
Sometimes, you don't want the interactive dock that drops down with a list of actions. In the Classic theme, for instance, you can have it display as a friendly horizontal list of icons and text. To do this, just open up dock.vm and remove the class of "interactive-mode" from the dock div.

I think that's about it for now. I'll try to blog a few more as they come to me, but overall, this might/should/maybe help out some people who have been curious :)

Zeige 1 - 20 von 35 Ergebnissen.
Elemente pro Seite 20
von 2