Nate CavanaughNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/rss2016-12-10T01:38:17Z2016-12-10T01:38:17ZThe status and direction of the frontend infrastructure in Liferay 7 & DXPNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/the-status-and-direction-of-the-frontend-infrastructure-in-liferay-7-dxp2016-09-12T14:28:14Z2016-09-08T18:13:08Z<p>
It&rsquo;s been awhile since I&rsquo;ve had a chance to clarify both our current status as well as our future direction, so I wanted to do that here.</p>
<p>
This blog post will be mainly a high level overview of what's new and different in Liferay 7, but over the coming weeks, different folks from our team will publish blog posts going into detail about the different areas regarding Liferay's front-end engineering).</p>
<h2>
<strong>Where we are currently at in Liferay Portal</strong></h2>
<p>
The first thing I'd like to mention is an update to my last post about AlloyUI in Liferay 7. If you remember, our plan was to release AlloyUI 4 and base it upon jQuery.</p>
<p>
We ended up deciding that this would lead to a very confusing situation for people building on top of Liferay, as it would be breaking an existing API dramatically, but still having it under the same namespace (which would also break for anyone wanting to use the version based on YUI).</p>
<p>
So rather than causing developers unnecessary grief, we've upgraded AlloyUI to version 3 (which brings with it an update to Bootstrap 3 and other bug fixes), but we've also decided to sunset AlloyUI and are deprecating it as of Liferay 7.</p>
<p>
It's still included by default, and your existing AlloyUI based code will still continue to work (and we'll also still continue to support it for our EE and DXP customers as well).</p>
<p>
Of course, the next question might be &quot;What should we use for our code going forward?&quot;</p>
<p>
Before answering that, I need to introduce a new library that we've created.</p>
<h3>
<strong>Metal.js</strong></h3>
<p>
<a href="http://metaljs.com/">Metal.js</a> is a small and fast library for creating and managing stateful components that is templating-language agnostic, and also supports server-side rendering.</p>
<p>
It uses Google's <a href="http://google.github.io/incremental-dom/#about">incremental-dom</a> to ensure incredibly fast rendering, as well as an easy to use API that makes creating and reusing components incredibly easy.</p>
<h3>
<strong>What should we use for our code going forward?</strong></h3>
<p>
We're making of a bit of a strategy change in how we both leverage JS libraries as well as how we advise their use in Liferay.</p>
<p>
The direction we're heading in is one in which people have full control over the frontend libraries that are loaded onto the page, as well as making the individual components more reusable and modular.</p>
<p>
This means that if you're really into React, Angular or Ember/Vue/etc, and your entire team has been trained on it, then not only can you use that library within the portal (which you can already do), but if you need to reuse an existing Liferay component from your library of choice, then you should be able to import it there as well.</p>
<p>
So while we do recommend using Metal.js for your development, as we have found that it solves many of our needs, we want people to feel comfortable using any library they like.</p>
<p>
Part of that will involve changing our modules to have minimal dependencies while still maintaining a focus on performance.</p>
<p>
The coming blog posts will also touch on some of the highlights of frontend engineering in Liferay 7, such as compiling of ES6 2015, how we're leveraging Senna to progressively enhance existing portlet applications to support single page applications, as well as architectural details and how you can leverage all of these new features in your development.</p>
<h3>
<strong>CSS and Themes</strong></h3>
<p>
There have been a lot of changes with regard to Theming and CSS in general in Liferay 7, and I'll touch on the big ones.</p>
<h4>
<strong>Bootstrap 3</strong></h4>
<p>
As I mentioned above, we've upgraded to Bootstrap 3 for Liferay 7. We didn't upgrade to Bootstrap 4, because as of the Liferay 7 release (April of 2016) and as of this writing (August 2016), Bootstrap 4 is still in alpha.</p>
<p>
When it is released, if the API changes are still minimal, we should be able to provide a compatibility layer for people to easily upgrade.</p>
<h4>
<strong>Lexicon</strong></h4>
<p>
Also in Liferay 7, we've introduced our new design language for creating applications, called Lexicon.</p>
<p>
<a href="http://liferay.github.io/lexicon/">Our implementation of Lexicon</a> consists of two parts: a set of components and extensions to Bootstrap and a visual skin that styles it.</p>
<p>
These are all still managed by your theme, so you can extend and customize it on a per-site basis.</p>
<h4>
<strong>Sass and SCSS parsing</strong></h4>
<p>
One change we've made for Liferay 7 that has gotten quite the positive response is that we have removed runtime compilation of Sass/SCSS.</p>
<p>
Related to that change, we've also renamed all of our files from using the .css extension to the .scss extension to ensure that IDEs and editors can properly display the right syntax highlighting, and also to prevent any confusion for newcomers.</p>
<p>
Lastly, we've also enhanced our build-time Sass parser to use libsass, which means that we will only use jRuby for Sass parsing if your system doesn't support running the libsass parser.</p>
<p>
How much of an improvement is this?</p>
<p>
One set of SCSS files that we had would often take around 50 seconds to over a minute with jRuby.</p>
<p>
With libsass, that time cut down to roughly 3-5 seconds (not an exaggeration).</p>
<h4>
<strong>New and Enhanced Theme Concepts</strong></h4>
<p>
We've added two new concepts for themes, which are <a href="https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/themelets">Themelets</a> and <a href="https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/theme-contributors">Theme Contributors</a>.</p>
<p>
You can think of themelets as small reusable chunks of frontend resources, be it (S)CSS, JavaScript, images, icons, etc, but without any theme boilerplate involved. A theme can then leverage these packages in any way they like, and can share them publicly.</p>
<p>
Let's say that some of your themes all share very similar functionality or styling. Maybe some parallax scrolling effect, or a dynamic CSS animation. Instead of having to duplicate your code across every theme, you can publish them to the NPM registry, and other developers can reuse them as well.</p>
<p>
While you could achieve similar results by extending a theme, this approach allows for maximum modularity.</p>
<p>
The other new concept, Theme Contributors, are small, targeted modules that contain JavaScript and CSS that are persistently included across all themes.</p>
<p>
This is useful in cases where you have some element(s) that exist on the page no matter what site you're on and whatever theme is selected, and you wish for it to be consistently styled.</p>
<p>
The most commonly styled elements are the Product Menu, Control Menu, Simulation Menu, etc, but it really could be anything.</p>
<p>
Lastly, we also enhanced how themes can be extended. With the new theme tools (which I'll cover in the next section), themes can be easily extended from some other theme, but the tools also allow you to extend from any themes that developers may publish on NPM.</p>
<p>
This opens the door for community members to create their own base theme layer, as well as many other possibilities.</p>
<h4>
<strong>New Theme Tools</strong></h4>
<p>
As some of you maybe have heard, we have a new set of tools designed for theme developers.</p>
<p>
One pain-point we've wanted to improve was that for many modern front-end developers, the old plugins SDK model of using Java based tools didn't fit their preferred workflow.</p>
<p>
This led us to examine ways we could really improve theme building experience. These new tools, which leverage Node.js and the NPM ecosystem, provide developers with a way to create themes, layout templates, themelets as well as importing themes into the new structure.</p>
<p>
We also provide quick tasks for watching your theme for changes and instantly deploying, quickly kickstarting a new theme, and extending from other themes.</p>
<p>
More blog posts will be coming soon that go into detail about new theming features from the Bootstrap and Lexicon side, as well as exploring the new tooling in depth.</p>
<p>
Again, this is all very high level, specifically because if I tried to go into detail about all of the great stuff in Liferay 7, this blog post would be quite a bear to read.</p>
<p>
Look out for the follow up blog posts coming soon, but in the meantime, please feel free to ask any questions you have in the comments.</p>Nate Cavanaugh2016-09-08T18:13:08ZThe future of UI Development and AlloyUI in Liferay 7Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/the-future-of-ui-development-and-alloyui-in-liferay-72014-09-17T15:33:11Z2014-09-17T14:54:59Z<p>
<span>Since</span><a href="https://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/yui-liferay-and-the-future"><span> </span><span>my last blog post</span></a><span>, I have gotten quite a few emails and questions about what the future of front end engineering looks like now that Yahoo has ended new development on YUI.</span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>What I'd like to cover here is a general overview of where we've decided to go and the reasoning behind the direction.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>After much thought and discussion, we've decided that AlloyUI 3 will be our final release that is based upon YUI. However this does not mean the end of AlloyUI at all, but is in fact an exciting new chapter.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>We have decided that we will be changing our underlying DOM/ajax/animation toolkit to using jQuery. For areas where jQuery doesn't have coverage, we will either leverage third-party libraries as much as possible, or when those are not sufficient, writing and maintaining our own components (about which, I'll go into a bit more detail later in this post). This set of UI components and utilities will be exposed as jQuery plugins, and will comprise AlloyUI 4.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Naturally, there are some questions, but the first one I'd like to address is:</span></span></p>
<h1>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>What does this mean for code written with AlloyUI in Liferay 6.2?</span></span></h1>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>For Liferay 7, we will be using AlloyUI 4 and also bundling Alloy UI 3 for backwards compatibility (which can be opted into by setting a property on your portlet, or if needed, included from your theme). This will behave as it has and the code you've invest time into now should work as it did previously. While AlloyUI 4 will not be backwards compatible with AlloyUI 3, they'll be able to safely co-exist within the same page.</span></span></p>
<h1>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>What if I'm already using jQuery in Liferay 6.2?</span></span></h1>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>You are also safe going forward, and in Liferay 7.0, you will have many benefits since AlloyUI's components will be exposed as jQuery plugins.</span></span></p>
<h1>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>AlloyUI 4 Overview</span></span></h1>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>From a high level, our goals and plans for AlloyUI 4 are to have a strong focus on the needs of modern web development, both in a portal context, and from a general web development context. Things such as mobile-first web development, single page applications and modular components.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>There are also a lot of exciting things coming into JavaScript with the ECMAScript 6 specification coming closer to a reality, and we want to help enterprises leverage these new features as much as possible.</span></span></p>
<h2>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Some history (a.k.a. &quot;Didn't you guys used to be based on jQuery?&quot;)</span></span></h2>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><a href="https://www.liferay.com/web/eduardo.lundgren"><span>Eduardo Lundgren</span></a><span> &nbsp;and I started working on AlloyUI over 5 years ago. At that time, there were shortcomings that we bumped into within jQuery and jQueryUI, and there was a dearth of options for building enterprise applications within the jQuery space. Since then, jQuery has not only filled in many of the issues we had, but it has also become the de facto API of the web.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Even other DOM/ajax/animation micro-frameworks have implemented the jQuery API, and even many specialized libraries, such as</span><a href="http://d3js.org/"><span> </span><span>d3.js</span></a><span> or</span><a href="http://raphaeljs.com/"><span> </span><span>RaphaelJS</span></a><span> have adopted jQuery-like APIs.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Also, the general front-end ecosystem has grown quite a bit since that time as well. Utilities such as</span><a href="http://lodash.com/"><span> </span><span>LoDash</span></a><span>, or</span><a href="http://underscorejs.org/"><span> </span><span>Underscore</span></a><span> have stepped in to provide common and useful functionality. There is a plethora of MVC/SPA frameworks (and while</span><a href="https://angularjs.org/"><span> </span><span>AngularJS</span></a><span> sure has a lot of momentum,</span><a href="http://backbonejs.org/"><span> </span><span>Backbone</span></a><span>,</span><a href="http://emberjs.com/"><span> </span><span>Ember</span></a><span>, and</span><a href="http://knockoutjs.com/"><span> </span><span>Knockout</span></a><span> are still going strong). CSS frameworks such as Bootstrap (which we started leveraging in Liferay 6.2), Foundation, PureCSS, etc., have gotten a lot of traction and maturity, even in many cases including their own set of JavaScript components based on jQuery.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Basically, for a wide range of UI infrastructure pieces, there are a lot of options out there, far more than was the case back in 2009.</span></span></p>
<h2>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>How does AlloyUI 4 fit into this ecosystem?</span></span></h2>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>YUI was great because it provided multiple options under one library, with a consistent API and a shared set of foundational utilities and components.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>What we want for AlloyUI is to leverage those best of breed options wherever possible, and focus on providing value to our Liferay customers where we feel we can contribute to most.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Not all of these third-party frameworks will fill our needs, or they may not be of the enterprise quality that we expect and our clients demand. We can provide value in these cases by either contributing fixes or enhancements to these libraries, or, if they're not interested in accepting those, in still providing those to the community at large.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>There are also some areas that are not covered at all by current libraries. For example, while lazy-loading of JavaScript has become quite popular, there is still nothing at all like YUI's Combo Loader, which dynamically lazy-loads modules. These sorts of enhancements we can write, and provide them as plugins that work with third-party libraries, rather than re-inventing the wheel.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>We also want to provide the components we create in a much more modular way, so that people can leverage them individually as well.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>Overall, while there are still a lot of details that we need work out, and a LOT of work to be done, we are quite excited about this direction.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>As we flesh things out, my aim is for the team and I to blog as much as we can about the decisions and how they may affect you and get feedback from you.</span></span></p>
<p>
<span id="docs-internal-guid-2bf3308c-841a-5481-f595-5da00b1e197a"><span>So with that, I'd like to thank you all for your support and passion. If there's anything we can do, or think we should do, please let us know.</span></span></p>Nate Cavanaugh2014-09-17T14:54:59ZYUI, Liferay, and the futureNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/yui-liferay-and-the-future2014-08-30T03:13:21Z2014-08-29T21:34:29Z<p class="p1">
As many of you <a href="http://yahooeng.tumblr.com/post/96098168666/important-announcement-regarding-yui"><span class="s1">may have read</span></a>, Yahoo is immediately stopping development on the YUI library.</p>
<p class="p1">
This decision, while it will have a significant impact, is <a href="https://twitter.com/tivac/status/504149075579920385"><span class="s1">not</span></a> <a href="https://groups.google.com/forum/#!category-topic/yui-support/everything-else/B4WYzgD3I3Y"><span class="s1">news</span></a> <a href="http://www.yuiblog.com/blog/2014/06/16/yui-gallery-deprecation-announcement/"><span class="s1">to</span></a> <a href="https://twitter.com/yuilibrary/status/489521567320117248"><span class="s1">us</span></a>. Given our close relationship with the YUI team, we knew this was coming, and have been discussing for a couple of months different plans of action.</p>
<p class="p1">
In the spirit of transparency, I do want to say that we don't yet have an official direction decided, but we are looking at all of our options, as well as discussing with our partners, and other companies/organizations with a vested interest in keeping YUI alive.</p>
<p class="p1">
I'd like to address two things in this blog post: what this means for existing EE customers, and what options are we looking towards.</p>
<p class="p1">
&nbsp;</p>
<p class="p2">
<b>What does this mean for existing EE customers?</b></p>
<p class="p1">
Nothing will change for our existing customers. We have long had a fork of YUI that we apply our patches and changes to, and we will continue to deliver bug fixes and needed changes for as long as that version of EE is supported.</p>
<p class="p2">
&nbsp;</p>
<p class="p2">
<b>What options are we looking towards?</b></p>
<p class="p1">
I'd like to list off a few options that we're discussing, partially to reinforce that we are seriously thinking about it, but also to solicit feedback and ideas regarding your needs or concerns.</p>
<p class="p1">
Of course, the most obvious possibility is to take over stewardship of YUI, whether that is us on our own, or with any number of the other large companies that leverage YUI heavily.</p>
<p class="p1">
Another possibility is to of course migrate off of YUI, and while still keeping AlloyUI as the wrapping library, but move to some other library internally, and still keep the functionality as close as possible.</p>
<p class="p1">
One more idea we've discussed is to possibly take our fork of YUI, and branch off a next generation version, like a YUI4, that cleans out the legacy code, streamlining and simplifying the library.</p>
<p class="p1">
Ultimately, we want to take the path that best serves our community and clients, and helps deliver amazing experiences as quickly and easily as possible.</p>
<p class="p1">
As we get more info, and have more info to provide, we'll make sure to keep you in the loop on the path we're going to go with.</p>
<p class="p1">
If you have ideas, thoughts, concerns or questions, don't hesitate to let us know.</p>Nate Cavanaugh2014-08-29T21:34:29ZThe Nitty-Gritty: Theme Improvements and Bootstrap in Liferay 6.2Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/the-nitty-gritty-theme-improvements-and-bootstrap-in-liferay-6-62014-01-14T21:14:48Z2014-01-10T20:18:13Z<p>
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.</p>
<p>
<a href="https://www.liferay.com/web/jorge.ferrer/blog/-/blogs/new-in-6-2-bootstrap-even-in-web-content?_33_redirect=https%3A%2F%2Fwww.liferay.com%2Fweb%2Fjorge.ferrer%2Fblog%3Fp_p_id%3D33%26p_p_lifecycle%3D0%26p_p_state%3Dnormal%26p_p_mode%3Dview%26p_p_col_id%3Dcolumn-2%26p_p_col_count%3D1">Jorge's</a> discussed a lot of the benefits, and the feedback we've gotten from the community has definitely been great.</p>
<p>
If I had to sum-up, here are the most common questions I've gotten from developers about Bootstrap in 6.2:</p>
<ul>
<li>
Why did you choose version 2.3.2 instead of 3?
<li>
How do I use my Bootstrap theme?
<li>
Do you support Bootstrap's JavaScript plugins?
<li>
Why do all of the Bootstrap rules have .aui in front of them?
</ul>
<p>
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.</p>
<h3>
Why did you choose version 2.3.2 instead of 3?</h3>
<p>
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:</p>
<ol>
<li>
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.
<li>
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.
</ol>
<p>
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).</p>
<h3>
How do I use my Bootstrap theme?</h3>
<p>
A common case is that someone has taken a generated theme (from a site such as <a href="http://bootswatch.com/2/">Bootswatch</a>) 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):</p>
<ol>
<li>
Inside of your theme's _diffs/css/ directory, create a file called aui.css.
<li>
Open the aui.css file and do a find/replace with the following values: <strong>find</strong>: <code>../img/</code> <strong>replace</strong>: <code>../images/aui/</code> (and of course, deploy your theme).
</ol>
<p>
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:</p>
<pre>
<code>
$FontAwesomePath: &quot;aui/alloy-font-awesome/font&quot; !default;
@import &quot;aui/alloy-font-awesome/scss/variables&quot;;
@import &quot;aui/alloy-font-awesome/scss/mixins-alloy&quot;;
@import &quot;aui/alloy-font-awesome/scss/path-alloy&quot;;
@import &quot;aui/alloy-font-awesome/scss/core&quot;;
body {
@import &quot;aui/alloy-font-awesome/scss/bootstrap&quot;;
@import &quot;aui/alloy-font-awesome/scss/extras&quot;;
@import &quot;aui/alloy-font-awesome/scss/icons-alloy&quot;;
@import &quot;aui/alloy-font-awesome/scss/icons-alloy-extra&quot;;
}
</code></pre>
<h3>
Do you support Bootstrap's JavaScript plugins?</h3>
<p>
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 <a href="http://alloyui.com/examples/modal/">Modal</a>, <a href="http://alloyui.com/examples/tooltip/">Tooltips</a>, <a href="http://alloyui.com/examples/pagination/">Pagination</a>, <a href="http://alloyui.com/examples/popover/">Popovers</a>, <a href="http://alloyui.com/examples/tabview/">Tabs</a>, and <a href="http://alloyui.com/examples/">more</a>. If there are ones you would like, <a href="https://issues.liferay.com/browse/AUI">please let us know</a>, and we'll definitely prioritize getting them in :)</p>
<h3>
Why do all of the Bootstrap rules have .aui in front of them?</h3>
<p>
This is one of those changes that doesn't seem like much, but is actually really powerful.</p>
<p>
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.</p>
<p>
Bootstrap is a very opinionated framework, which is what many people love, and many people can be frustrated by.</p>
<p>
Previously, we always prefixed our CSS classes with <code>.aui-</code>, 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.</p>
<p>
What we decided to do instead was to place a selector of <code>.aui</code> before all of Bootstrap's rules, so that the rules look like <code>.aui .btn</code>, etc.</p>
<p>
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).</p>
<p>
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 <code>$root_css_class</code> variable, and edit your portlet.vm file and add it there.</p>
<p>
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).</p>
<p>
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.</p>
<h2>
What else is new in 6.2?</h2>
<h3>
Bootstrap is completely controllable from the theme level</h3>
<p>
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).</p>
<p>
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).</p>
<h3>
An easier way to do media queries</h3>
<p>
We have also added a mixin called &quot;respond-to&quot; that allows you to easily target certain types of devices without having to remember the media queries. For example:</p>
<pre>
<code>/* 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; }
}
</code></pre>
<p>
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:</p>
<pre>
<code>@include respond-to(phone) {
body { background: red; }
input { width: 100%; }
}
</code></pre>
<p>
or, you can use it in the middle of an already deeply nested SCSS structure, such as:</p>
<pre>
<code>body {
input {
background: white;
@include respond-to(phone) {
background: blue;
}
}
}
</code></pre>
<h3>
A new Dockbar display mode</h3>
<p>
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:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar.png/c15226a6-92c5-47f4-b6bb-bc2542769eb6?t=1389384944372"></p>
<p>
It may not be obvious, but in this mode, the dockbar is actually split, as you can see here:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar_highlighted.png/a8361b8a-723c-4fa3-9670-32203d8e1993?t=1389385001670"></p>
<p>
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.</p>
<p>
So what we did was add a new mode for the Dockbar. Basically, if you add a CSS class onto your body element called <code>dockbar-split</code>, it will trigger the new display mode.</p>
<p>
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:</p>
<pre>
<code>#set ($css_class = &quot;${css_class} dockbar-split&quot;)
</code></pre>
<p>
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:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar_plain.png/b22fd4a8-410e-47c0-bd07-a768cbac7ed2?t=1389385045345"></p>
<p>
And here's what the Split Dockbar looks like with no customizations:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar_split_plain.png/2374a489-b7b4-4794-9ead-b86e0221d059?t=1389385068235"></p>
<p>
As you can see, it's much less targeted to any one specific design.</p>
<h3>
We are bundling in FontAwesome by default</h3>
<p>
If you haven't heard of <a href="http://fortawesome.github.io/Font-Awesome/3.2.1/">FontAwesome</a>, you should definitely check it out. It offers a wide range of icons and a lot of flexibility.</p>
<p>
But the main benefits can be summed up as:</p>
<ul>
<li>
Completely scaleable icons, even for high resolution devices
<li>
Easily change the size and the color of the icons via CSS
</ul>
<h2>
Final words</h2>
<p>
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.</p>
<p>
As always, feel free to ask any questions you may have here, or in the forums :)</p>Nate Cavanaugh2014-01-10T20:18:13ZThe Nitty-Gritty: Theme Improvements and Bootstrap in Liferay 6.2Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/the-nitty-gritty-theme-improvements-and-bootstrap-in-liferay--112015-10-30T23:58:14Z2014-01-10T20:21:57Z<p>
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.</p>
<p>
<a href="https://www.liferay.com/web/jorge.ferrer/blog/-/blogs/new-in-6-2-bootstrap-even-in-web-content?_33_redirect=https%3A%2F%2Fwww.liferay.com%2Fweb%2Fjorge.ferrer%2Fblog%3Fp_p_id%3D33%26p_p_lifecycle%3D0%26p_p_state%3Dnormal%26p_p_mode%3Dview%26p_p_col_id%3Dcolumn-2%26p_p_col_count%3D1">Jorge's</a> discussed a lot of the benefits, and the feedback we've gotten from the community has definitely been great.</p>
<p>
If I had to sum-up, here are the most common questions I've gotten from developers about Bootstrap in 6.2:</p>
<ul>
<li>
Why did you choose version 2.3.2 instead of 3?
<li>
How do I use my Bootstrap theme?
<li>
Do you support Bootstrap's JavaScript plugins?
<li>
Why do all of the Bootstrap rules have .aui in front of them?
</ul>
<p>
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.</p>
<h3>
Why did you choose version 2.3.2 instead of 3?</h3>
<p>
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:</p>
<ol>
<li>
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.
<li>
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.
</ol>
<p>
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).</p>
<h3>
How do I use my Bootstrap theme?</h3>
<p>
A common case is that someone has taken a generated theme (from a site such as <a href="http://bootswatch.com/2/">Bootswatch</a>) 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):</p>
<ol>
<li>
Inside of your theme's _diffs/css/ directory, create a file called aui.css.
<li>
Open the aui.css file and do a find/replace with the following values: <strong>find</strong>: <code>../img/</code> <strong>replace</strong>: <code>../images/aui/</code> (and of course, deploy your theme).
</ol>
<p>
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: &quot;aui/alloy-font-awesome/font&quot; !default;</p>
<pre>
<code>@import &quot;aui/alloy-font-awesome/scss/variables&quot;;
@import &quot;aui/alloy-font-awesome/scss/mixins-alloy&quot;;
@import &quot;aui/alloy-font-awesome/scss/path-alloy&quot;;
@import &quot;aui/alloy-font-awesome/scss/core&quot;;
body {
@import &quot;aui/alloy-font-awesome/scss/bootstrap&quot;;
@import &quot;aui/alloy-font-awesome/scss/extras&quot;;
@import &quot;aui/alloy-font-awesome/scss/icons-alloy&quot;;
@import &quot;aui/alloy-font-awesome/scss/icons-alloy-extra&quot;;
}
</code></pre>
<h3>
Do you support Bootstrap's JavaScript plugins?</h3>
<p>
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 <a href="http://alloyui.com/examples/modal/">Modal</a>, <a href="http://alloyui.com/examples/tooltip/">Tooltips</a>, <a href="http://alloyui.com/examples/pagination/">Pagination</a>, <a href="http://alloyui.com/examples/popover/">Popovers</a>, <a href="http://alloyui.com/examples/tabview/">Tabs</a>, and <a href="http://alloyui.com/examples/">more</a>. If there are ones you would like, <a href="https://issues.liferay.com/browse/AUI">please let us know</a>, and we'll definitely prioritize getting them in :)</p>
<h3>
Why do all of the Bootstrap rules have .aui in front of them?</h3>
<p>
This is one of those changes that doesn't seem like much, but is actually really powerful.</p>
<p>
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.</p>
<p>
Bootstrap is a very opinionated framework, which is what many people love, and many people can be frustrated by.</p>
<p>
Previously, we always prefixed our CSS classes with <code>.aui-</code>, 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.</p>
<p>
What we decided to do instead was to place a selector of <code>.aui</code> before all of Bootstrap's rules, so that the rules look like <code>.aui .btn</code>, etc.</p>
<p>
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).</p>
<p>
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 <code>$root_css_class</code> variable, and edit your portlet.vm file and add it there.</p>
<p>
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).</p>
<p>
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.</p>
<h2>
What else is new in 6.2?</h2>
<h3>
Bootstrap is completely controllable from the theme level</h3>
<p>
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).</p>
<p>
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).</p>
<h3>
An easier way to do media queries</h3>
<p>
We have also added a mixin called &quot;respond-to&quot; that allows you to easily target certain types of devices without having to remember the media queries. For example:</p>
<pre>
<code>/* 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(phone, tablet) {
body { background: green; }
}
</code></pre>
<p>
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:</p>
<pre>
<code>@include respond-to(phone) {
body { background: red; }
input { width: 100%; }
}
</code></pre>
<p>
or, you can use it in the middle of an already deeply nested SCSS structure, such as:</p>
<pre>
<code>body {
input {
background: white;
@include respond-to(phone) {
background: blue;
}
}
}
</code></pre>
<h3>
A new Dockbar display mode</h3>
<p>
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:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar.png/c15226a6-92c5-47f4-b6bb-bc2542769eb6?t=1389384944372" style="width: 562.0px;height: 135.0px;"></p>
<p>
It may not be obvious, but in this mode, the dockbar is actually split, as you can see here:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar_highlighted.png/a8361b8a-723c-4fa3-9670-32203d8e1993?t=1389385001670" style="width: 562.0px;height: 135.0px;"></p>
<p>
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.</p>
<p>
So what we did was add a new mode for the Dockbar. Basically, if you add a CSS class onto your body element called <code>dockbar-split</code>, it will trigger the new display mode.</p>
<p>
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:</p>
<pre>
<code>#set ($css_class = &quot;${css_class} dockbar-split&quot;)
</code></pre>
<p>
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:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar_plain.png/b22fd4a8-410e-47c0-bd07-a768cbac7ed2?t=1389385045345" style="width: 562.0px;height: 92.0px;"></p>
<p>
And here's what the Split Dockbar looks like with no customizations:</p>
<p>
<img alt="image" src="https://web.liferay.com/documents/10822/180299/dockbar_split_plain.png/2374a489-b7b4-4794-9ead-b86e0221d059?t=1389385068235" style="height: 133.0px;width: 562.0px;"></p>
<p>
As you can see, it's much less targeted to any one specific design.</p>
<h3>
We are bundling in FontAwesome by default</h3>
<p>
If you haven't heard of <a href="http://fortawesome.github.io/Font-Awesome/3.2.1/">FontAwesome</a>, you should definitely check it out. It offers a wide range of icons and a lot of flexibility.</p>
<p>
But the main benefits can be summed up as:</p>
<ul>
<li>
Completely scaleable icons, even for high resolution devices
<li>
Easily change the size and the color of the icons via CSS
</ul>
<h2>
Final words</h2>
<p>
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.</p>
<p>
As always, feel free to ask any questions you may have here, or in the forums :)</p>Nate Cavanaugh2014-01-10T20:21:57ZLiferay.com, mobile sites and responsive layoutsNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/liferay-com-mobile-sites-and-responsive-layouts2011-05-23T05:40:52Z2011-05-23T05:06:14Z<p>
<a href="http://www.liferay.com/web/bryan.cheung">Bryan</a> recently <a href="http://www.liferay.com/web/bryan.cheung/blog/-/blogs/8805884">blogged about the new site design</a> 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.</p>
<p style="text-align: center; ">
<img alt="Liferay.com can resize from desktop to mobile dynamically" src="http://www.alterform.com/liferay/liferay_responsive.png" title="Liferay.com can resize from desktop to mobile dynamically" /></p>
<p>
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 <a href="http://unstoppablerobotninja.com/">Ethan Marcotte</a> published his <a href="http://www.alistapart.com/articles/responsive-web-design/">article</a> 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.</p>
<p>
As far as mobile strategies go, it’s probably the easiest to implement, and there are a lot of quick wins with it. However, <a href="http://www.liferay.com/web/jonathan.neal">Jon Neal</a> 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.</p>
<p>
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).</p>
<p>
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.</p>
<p>
What do those numbers mean? 960px is based on the uber-popular <a href="http://960.gs">960 grids</a> 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.</p>
<p>
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.</p>
<p>
If your target device is 600x800, you can still target that device with the css classes.</p>
<p>
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).</p>
<p>
For a quick and simple demo, open up <a href="http://alloyui.com/latest/demos/viewport">the viewport demo</a> (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).</p>
<p>
To get the module to run, all you need to do is call: <code>AUI().use('aui-viewport')</code> in a script tag anywhere on your page.</p>
<p>
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:</p>
<pre>
<code>#navigation li {
display: inline;
float: left;
}
.aui-view-lt720 #navigation li {
display: block;
float: none;
}
</code></pre>
<p>
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</p>
<pre>
<code>.aui-view-720 img {
max-width: 300px
}
</code></pre>
<p>
Or perhaps we want to target just portrait mode of smartphones and tablets:</p>
<pre>
<code>.aui-view-720 body, .aui-view-320 body {
background: #ccc;
}
</code></pre>
<p>
You can even combine this with our other browser selectors to target very specific browsers:</p>
<pre>
<code>.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 */
</code></pre>
<p>
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: <code>aui-view-{WIDTH}</code>. And, if the screen is greater than any of our widths, it will also get: <code>aui-view-gt{WIDTH}</code>. Lastly, if the screen is less than any of our widths, it will also get: <code>aui-view-lt{WIDTH}</code>.</p>
<p>
So a window size of 1900x1200 would get:</p>
<pre>
<code>aui-view-gt320
aui-view-gt480
aui-view-gt720
aui-view-gt960
aui-view-960
</code></pre>
<p>
whereas a window size of 800x600 would get:</p>
<pre>
<code>aui-view-gt320
aui-view-gt480
aui-view-gt720
aui-view-720
aui-view-lt960
</code></pre>
<p>
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.</p>
<p>
<strong>Caveats</strong></p>
<p>
Life is not all <a href="http://www.flickr.com/photos/carolcani/3229321105/in/set-72157617152853594/">cheese and bananas</a> with this system, as there are some caveats to be aware of:</p>
<ul>
<li>
<p>
You are delivering the same content to all devices.</p>
<p>
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.</p>
</li>
<li>
<p>
Designs can require more planning for each of the design layouts</p>
<p>
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.</p>
</li>
</ul>
<p>
<strong>How can you get this module?</strong></p>
<p>
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.</p>
<p>
<strong>Conclusions</strong></p>
<p>
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.</p>
<p>
<strong>More information</strong></p>
<p>
<a href="http://www.alistapart.com/articles/responsive-web-design/">Responsive Web Design by Ethan Marcotte</a><br />
<a href="http://mediaqueri.es">MediaQueri.es</a><br />
<a href="http://blog.bloop.co/the-practicalities-of-css-media-queries-lesso">The practicalities of CSS Media Queries, lessons learned.</a></p>Nate Cavanaugh2011-05-23T05:06:14ZUsing jQuery (or any Javascript library) in Liferay 6.0Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/using-jquery-or-any-javascript-library-in-liferay-6-02010-07-21T15:21:24Z2010-07-20T16:03:16Z<p>&nbsp;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).</p> <div>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.</div> <div>By moving off of jQuery, it's also allowed us to step out of the way of developers who wish to &nbsp;use any version of jQuery that they need without worrying about conflicts with the core portal javascript.</div> <div>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.</div> <div>So instead of calling YUI() in the portal, we created AUI(). By creating the &quot;AUI&quot; 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.</div> <div>&nbsp;</div> <div>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.</div> <div>Or perhaps there is some other Javascript library (such as YUI2, Dojo, qooxdoo, ExtJS, etc) that you need to include for the same reason.</div> <div>&nbsp;</div> <div>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 &quot;production&quot; version: http://code.jquery.com/jquery-1.4.2.min.js</div> <div>&nbsp;</div> <div>There are a couple of ways you can include jQuery onto the page.</div> <div><a href="http://www.liferay.com/web/jonas.yuan/profile">Jonas</a> has covered a great way in his blog post on <a href="http://www.liferay.com/web/jonas.yuan/blog/-/blogs/building-jquery-based-plugins-in-liferay-6">building jQuery portlets in Liferay 6</a>.</div> <div>&nbsp;</div> <div>First, using the same basic principle, is including it in your portlet.</div> <div>In your liferay-portlet.xml add this entry:</div> <pre>
&lt;header-portlet-javascript&gt;http://code.jquery.com/jquery-1.4.2.min.js&lt;/header-portlet-javascript&gt;<br /></pre> <div>That will add jQuery 1.4.2 onto the page wherever that portlet happens to be rendered.</div> <div>&nbsp;</div> <div>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:</div> <pre>
&lt;script src=&quot;http://code.jquery.com/jquery-1.4.2.min.js&quot;&gt;&lt;/script&gt;<br /></pre> <div>This will make jQuery available for everywhere in Liferay, including plugins that you deploy.</div> <div>&nbsp;</div> <div>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.</div> <div>&nbsp;</div> <div>In any Javascript that gets added to the page, you can do:</div> <pre>
AUI().use('get', function(A){
<meta charset="utf-8"><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; "> </span>A.Get.script('http://code.jquery.com/jquery-1.4.2.min.js', {</meta></pre> <p><meta charset="utf-8" /></p> <p><meta charset="utf-8" /></p> <pre><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>onSuccess: function(){</pre> <pre><meta charset="utf-8"><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>// jQuery() can be used in here...</meta></pre> <p><meta charset="utf-8" /></p> <pre>
&nbsp;<span class="Apple-tab-span" style="white-space:pre">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; ">&nbsp;</span>}</pre> <pre><span class="Apple-tab-span" style="white-space:pre"> </span>});
});</pre> <div>&nbsp;</div> <div>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.</div> <div>&nbsp;</div> <div>This allows people who don't want to upgrade the JS portion of their app, they can easily include the previous version.</div> <div>The way that would look different is that it would just point to the different path, like so:</div> <pre>
&lt;header-portlet-javascript&gt;/html/js/jquery/jquery.js&lt;/header-portlet-javascript&gt;<br /></pre> <div>The path to the previous version is:</div> <div><em>/html/js/jquery/jquery.js</em> and the <em>/html/js/jquery/</em> directory contains all of the plugins from 5.2 that work with jQuery 1.2.6.</div> <div>&nbsp;</div> <div>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 :)</div>Nate Cavanaugh2010-07-20T16:03:16ZAlloyUI - Working with Widgets, Part 1Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-widgets-part-12010-04-14T15:33:35Z2010-04-14T15:00:00Z<p><strong>&nbsp;What is an Alloy widget?</strong></p> <div>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.</div> <div>That would be a pretty complex widget.</div> <div>A simple widget would be the Tooltip widget, which just shows users some content when they hover over an item on the page.</div> <div>&nbsp;</div> <div>In Alloy, there's a large collection of widgets, as well as a robust framework for building your own.</div> <div>&nbsp;</div> <div>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.</div> <div>&nbsp;</div> <div>So let's go ahead and get our sandbox ready:</div> <div>&nbsp;</div> <pre>
AUI().use('', function(A){
<span class="Apple-tab-span" style="white-space:pre"> </span>// Our code will go here
});
</pre> <div>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.</div> <div>&nbsp;</div> <div>So let's go ahead and create a TreeView of a user directory that has drag and dropping of elements, and expandable folders.</div> <div>&nbsp;</div> <div>Here's the demo: <a href="http://alloy.liferay.com/deploy/sandbox/tree_widget_example.html">TreeWidget</a></div> <div>Here's the code you would have to write:</div> <div>&nbsp;</div> <div>You would use the <code>aui-tree-view</code> module</div> <div>&nbsp;</div> <pre>
var users = new A.TreeViewDD({
<span class="Apple-tab-span" style="white-space:pre"> </span> children: [{
<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>label: 'Users',<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>expanded: true,<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>children: [{<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>label: 'Nate Cavanaugh',<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>expanded: true,<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>children: [<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>{label: 'Documents', leaf: false},<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>{label: 'Downloads', leaf: false},<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>{label: 'Movies', leaf: false},<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>{label: 'todo_list.txt'}<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> &nbsp;<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>]<br /></pre><pre><span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>}]
<span class="Apple-tab-span" style="white-space:pre"> </span>}]
}).render();</pre> <div>&nbsp;</div> <div>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 &quot;users&quot;.</div> <div>&nbsp;</div> <div>So what's with that <code>.render()</code> at the end?</div> <div>&nbsp;</div> <div>That <code>render()</code> 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.</div> <div>&nbsp;</div> <div>But if you don't care about waiting, you can just do it all inline (render will still return a reference to the widget).</div> <div>&nbsp;</div> <div>We've just created a tree widget, and have this users variable, so what? Why is this exciting if you're just *using* widgets?</div> <div>&nbsp;</div> <div>Because even if you're just using widgets, you can still do VERY interesting stuff with these widgets. Remember how we talked about <a href="http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-elements-and-events">working with elements</a>?</div> <div>&nbsp;</div> <div>The widget API is VERY similar to the Node API, meaning that in the same way that you can do <code>get/set</code> for properties on a Node, you can also do that on a Widget. And in the same way that you can use <code>.on()</code> for nodes, you can also do the same thing for Widgets.</div> <div>&nbsp;</div> <div>But before we jump into that, let's look at another widget, something like Tooltip.</div> <div>&nbsp;</div> <div>So let's go ahead and create a Tooltip. We'll use the <code>aui-tooltip</code> module:</div> <pre>
var tooltip = new A.Tooltip({
<span class="Apple-tab-span" style="white-space:pre"> </span>trigger: '.use-tooltip',
<span class="Apple-tab-span" style="white-space:pre"> </span>bodyContent: 'Hello World!'
}).render();</pre> <div>&nbsp;</div> <div>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 &quot;Hello World!&quot;. 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.</div> <div>&nbsp;</div> <div>So now we have our tooltip object, what can we do to it? Well, all of those things we passed into it, like <code>trigger</code>, and <code>bodyContent</code> can all be read or changed using <code>get/set</code>, and not only that, but you can listen in to when they're changed as well.</div> <div>&nbsp;</div> <div>This might take a second to realize just how cool this is, but take my word for it, it's insanely powerful.</div> <div>&nbsp;</div> <div>Let's take a look. We'll go ahead and change the message of the tooltip to instead say &quot;Hey there Earth!&quot;.</div> <div>&nbsp;</div> <pre>
tooltip.set('bodyContent', 'Hey there Earth!');</pre> <div>&nbsp;</div> <div>Now when you hover over it, the tooltip will say 'Hey there Earth!'.</div> <div>&nbsp;</div> <div>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.</div> <div>So in this case, the event that got fired was &quot;<code>bodyContentChange</code>&quot;. Every attribute on the widget fires a &quot;change&quot; event when it gets changed, and you can listen to it in two phases.</div> <div>&nbsp;</div> <div>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.</div> <div>&nbsp;</div> <div>This took me a few minutes to sink in, but here are the benefits:</div> <div>&nbsp;</div> <div>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.</div> <div>&nbsp;</div> <div>Here's an example:&nbsp;</div> <div>All widgets have an attribute called &quot;<code>visible</code>&quot;, and when this attribute is changed, the widget hides or shows. Usually you just call <code>widget.hide()</code> or <code>widget.show()</code>.</div> <div>&nbsp;</div> <div>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.</div> <div>&nbsp;</div> <div>We would do:</div> <div>&nbsp;</div> <pre>
tooltip.on('visibleChange', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>if(!A.one('#myEl')){
&nbsp; <span class="Apple-tab-span" style="white-space:pre"> </span>event.preventDefault();<br /></pre><pre>
<span class="Apple-tab-span" style="white-space:pre"> </span>}
});<br /></pre> <div>So now, the tooltip will only show up if an element with the id of myEl is on the page.</div> <div>&nbsp;</div> <div>Or here are some more practical examples:</div> <div>&nbsp;</div> <div>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?</div> <div>&nbsp;</div> <div>You would use the <code>aui-button-item</code> widget, and the <code>aui-dialog</code> widget, and do:</div> <div>&nbsp;</div> <pre>
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){
<span class="Apple-tab-span" style="white-space:pre"> </span>dialog.set('visible', event.newVal);
});
<br /></pre> <div>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.</div> <div>&nbsp;</div> <div>You would use the <code>aui-color-picker</code></div> <div>&nbsp;</div> <pre>
new A.ColorPicker({
<span class="Apple-tab-span" style="white-space:pre"> </span>after: {
<meta charset="utf-8"><meta charset="utf-8"> <span class="Apple-tab-span" style="white-space:pre"> </span>hexChange: function(event){ <meta charset="utf-8"><meta charset="utf-8"> &nbsp; <span class="Apple-tab-span" style="white-space:pre"> </span><meta charset="utf-8"> &nbsp; <span class="Apple-tab-span" style="white-space: pre; "> </span>A.one('#myInputNode').val(event.newVal);</meta></meta></meta></meta></meta></pre><p><meta charset="utf-8" /></p><p><meta charset="utf-8"><meta charset="utf-8" /></meta></p><pre><meta charset="utf-8"> <span class="Apple-tab-span" style="white-space:pre"> </span>} <meta charset="utf-8"> <span class="Apple-tab-span" style="white-space:pre"> </span>} }).render();<br /></meta></meta></pre> <div>Another thing I mentioned last time was that Widgets can be plugged just like Nodes. Well, remember our handy dandy <a href="http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-plugins">IO plugin</a>? We can plug any widget with it, and it will automatically know what elements it should grab internally.</div> <div>&nbsp;</div> <div>For instance, let's create another dialog again, and let's plug it:&nbsp;</div> <pre>
var dialog = new A.Dialog({title: 'Hello World'}).plug(A.Plugin.IO, {uri: 'test.html'}).render();</pre> <div>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.</div> <div>&nbsp;</div> <div>A couple of notes:</div> <div>&nbsp;</div> <div>The <code>.render()</code> 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.</div> <div>&nbsp;</div> <div>So I could have easily done this:</div> <pre>
new A.ColorPicker().render('#myContainer');</pre> <div>&nbsp;For Widgets, if you attach a listener &quot;<code>on</code>&quot; that event, it fires before the change has been made, so you can think of it as a &quot;before&quot; stage. It's kind of confusing, but it works.</div> <div>If you listen to the &quot;<code>after</code>&quot; stage, it's after the work has already been done.</div> <div>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.</div> <div>90% of the time, I use &quot;<code>after</code>&quot; to listen to widget events.</div> <div>&nbsp;</div> <div>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.</div> <div>See you then!</div>Nate Cavanaugh2010-04-14T15:00:00ZAlloyUI - Working with PluginsNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-plugins2010-03-24T15:55:48Z2010-03-24T15:39:19Z<p>&nbsp;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.</p><div>So first, I'll show you how to use it, then we'll talk more about how to make your own plugin.</div><div>&nbsp;</div><div>So let's set up our sandbox:</div><pre>
AUI().use('io-plugin', function(A){
<span class="Apple-tab-span" style="white-space:pre"> </span>// Our code will go here
});</pre><div>&nbsp;</div><div>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.</div><div>&nbsp;</div><div>There is a common pattern we kept seeing in our ajax calls which was:</div><div>&nbsp;</div><ol><li>Insert loading icon into region</li><li>Do ajax call</li><li>On success, update region with new content</li></ol><div>Here is what the code would look might like before:</div><div>&nbsp;</div><pre>
var contentNode = A.one('#contentNode');
if(contentNode) {
<span class="Apple-tab-span" style="white-space:pre"> </span>contentNode.html('&lt;div class=&quot;loading-animation&quot;&gt;&lt;/div&gt;');
<span class="Apple-tab-span" style="white-space:pre"> </span> var myRequest = A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre"> </span> on: {
<span class="Apple-tab-span" style="white-space:pre"> </span> success: function(event, id, xhr) {
<span class="Apple-tab-span" style="white-space:pre"> </span> contentNode.html(this.get('responseData'));
<span class="Apple-tab-span" style="white-space:pre"> </span> }
<span class="Apple-tab-span" style="white-space:pre"> </span> }
<span class="Apple-tab-span" style="white-space:pre"> </span> });
}
</pre><div>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.</div><div>&nbsp;</div><div>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.</div><div>&nbsp;</div><pre>
var contentNode = A.one('#myNode');
if(contentNode) {
<span class="Apple-tab-span" style="white-space:pre"> </span>contentNode.plug(A.Plugin.IO, { uri: 'test.html' });
}<br /></pre><div>And that's it.</div><div>&nbsp;</div><div>Here's what it looks like:<br /><img alt="" src="http://alterform.com/liferay/loading_mask.png" /></div><div>&nbsp;</div><div>It basically will mask any existing content, and add a loading indicator that is centered above it.</div><div>&nbsp;</div><div>Here is some of the cool stuff about the plugin (and all plugins in Alloy):</div><div>&nbsp;</div><h3>1. It has it's own namespace</h3><div>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 &lt;a href=&quot;http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-ajax?_33_redirect=%2Fweb%2Fnathan.cavanaugh%2Fblog&quot;&gt;last post&lt;/a&gt;.</div><div>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.</div><div>So instead, it's placed in a namespace, and you can access that like so:</div><div>&nbsp;</div><pre>
contentNode.io</pre><div>&nbsp;</div><div>So if you want to set the dataType on the plugin to json, you can do:</div><div>&nbsp;</div><pre>
contentNode.io.set('dataType', 'json');<br /></pre><div>or if you want to stop the connection:</div><pre>
contentNode.io.stop();<br /></pre><h3>2. Plugins can be &quot;unplugged&quot;</h3><div>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).</div><div>&nbsp;</div><div>You would just call:</div><pre>
contentNode.unplug(A.Plugin.IO);</pre><div>&nbsp;</div><h3>3. Plugins can be plugged to NodeLists as well as Nodes</h3><div>So this would work as well:</div><div>&nbsp;</div><pre>
var contentNodes = A.all('.content-nodes');
contentNodes.plug(A.Plugin.IO, { uri: 'test.html' });</pre><div>&nbsp;</div><div>Then we could grab the first item in the NodeList and access the plugin namespace</div><pre>
contentNodes.item(0).io.set('cache', false);</pre><div>&nbsp;</div><h3>4. Plugins can also be on Widgets</h3><div>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.</div><div>&nbsp;</div><h3>5. Plugging doesn't have to be a per instance affair.</h3><div>You can do this:</div><pre>
A.Node.plug(A.Plugin.IO, {autoLoad: false, uri: 'test.html'});</pre><div>Now you could do:</div><pre>
var contentNode = A.one('#contentNode');
if(contentNode) {
<span class="Apple-tab-span" style="white-space:pre"> </span>contentNode.io.start();
}</pre><div>&nbsp;</div><div>The difference is that since we called <code>A.Node.plug()</code> (which is a static method on the Node class), it plugs all newly created instances with that plugin.</div><div>&nbsp;</div><div>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.</div><div>&nbsp;</div><div>6. You can plug with multiple plugins at once.</div><div>So for instance, you can do this:</div><div>&nbsp;</div><pre>
contentNode.plug([
<span class="Apple-tab-span" style="white-space:pre"> </span>{ fn: A.Plugin.IO, cfg: {uri: 'test.html'} },
<span class="Apple-tab-span" style="white-space:pre"> </span>{ fn: A.Plugin.MyOtherPlugin }
]);<br /></pre><div>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.</div><div>&nbsp;</div><h3><strong>Creating a plugin</strong></h3><div>&nbsp;</div><div>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 <code>NS</code> which will be it's namespace.</div><div>&nbsp;</div><div>So for this example, I'm going to create a plugin that takes an input field, and inserts a &quot;<code>defaultValue</code>&quot;. When you focus the field, if the value matches the &quot;<code>defaultValue</code>&quot;, 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.</div><div>&nbsp;</div><div>If you wish to jump to the demo, go ahead and take a look here: <a href="http://alloy.liferay.com/deploy/sandbox/plugin_example.html">Plugin Demo</a>.</div><p>&nbsp;</p><div>&nbsp;</div><div>I'm going to start with this markup:</div><div>&nbsp;</div><pre>
&lt;input data-defaultValue=&quot;Enter Text&quot; id=&quot;myInput&quot; type=&quot;text&quot; value=&quot;&quot; /&gt;</pre><div>&nbsp;</div><div>HTML5 allows for custom attributes if you prefix the attribute with &quot;data-&quot;, so you'll notice I added a new attribute called &quot;<code>data-defaultValue</code>&quot;, which our plugin will read.</div><div>&nbsp;</div><div>So I'll create the javascript:</div><div>&nbsp;</div><pre>
var defaultValuePlugin = function(config) {
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>var node = config.host;
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>var defaultValue = node.getAttribute('data-defaultValue');
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>var startingValue = node.val();
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>if (!startingValue) {
&nbsp; <span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>node.val(defaultValue);<br /></pre><pre><span class="Apple-tab-span" style="white-space:pre">&nbsp;</span> <span class="Apple-tab-span" style="white-space: pre; "> </span>}<br /></pre><pre>
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>node.on('focus', function(event) {
<span class="Apple-tab-span" style="white-space: pre; "> </span> <span class="Apple-tab-span" style="white-space:pre"> </span>var value = node.val();
&nbsp; <span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>if (value == defaultValue) {<br /></pre><pre>
<span class="Apple-tab-span" style="white-space:pre">&nbsp;</span> <span class="Apple-tab-span" style="white-space: pre; "> </span>node.val('');</pre><p><meta charset="utf-8" /></p><pre><span class="Apple-tab-span" style="white-space:pre"> </span> &nbsp; <span class="Apple-tab-span" style="white-space: pre; "> </span>}</pre><p><meta charset="utf-8"><meta charset="utf-8" /></meta></p><pre>
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>});
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>node.on('blur', function(event) {
&nbsp; <span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span>var value = node.val();</pre><p><meta charset="utf-8" /></p><pre>
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span> if (value == '') {
<span class="Apple-tab-span" style="white-space:pre"> </span>&nbsp; <span class="Apple-tab-span" style="white-space: pre; "> </span> node.val(defaultValue);</pre><p><meta charset="utf-8"><meta charset="utf-8" /></meta></p><pre>
<span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space:pre"> </span> }
<span class="Apple-tab-span" style="white-space:pre"> </span>});
};
defaultValuePlugin.NS = 'defaultValue';
</pre><div>Now all we have to do to get it working is simply plug it onto a node:</div><div>&nbsp;</div><pre>
A.one('#myInput').plug(defaultValuePlugin);</pre><div>&nbsp;</div><div>I'll go over some points of the code above.</div><div>&nbsp;</div><div>One is that, notice that the first line points to <code>config.host</code>. The argument <code>config</code> is the configuration object that is passed into the plugin, but by default the <code>host</code> is always passed into the plugin, so you always have access from the plugin to whatever is being plugged.</div><div>It's like a magic link to whatever item you're plugging.</div><div>&nbsp;</div><div>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.</div><div>&nbsp;</div><div>On the last line, I'm attaching a property called <code>NS</code> 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.</div><div>&nbsp;</div><div>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 <a href="http://developer.yahoo.com/yui/3/plugin">YUI3</a> page&nbsp;also offers a lot more info if you would like to investigate further as well.</div><div>&nbsp;</div><div>Until next time, see you guys later!</div><p>&nbsp;</p>Nate Cavanaugh2010-03-24T15:39:19ZAlloyUI - Working with AjaxNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-ajax2010-03-18T06:21:18Z2010-03-18T06:12:26Z<div>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.</div><div>&nbsp;</div><div>Let's prep our sandbox, but this time, the module we're going to use is called &quot;aui-io-request&quot;.</div><div>&nbsp;</div><pre>
AUI().use('aui-io-request', function(A){
<span class="Apple-tab-span" style="white-space:pre"> </span>// Our code will run here
});</pre><div>&nbsp;</div><div>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 &quot;test.html&quot;.</div><div>&nbsp;</div><pre>
A.io.request('test.html');</pre><div>&nbsp;</div><div>That's all there is to it. However, that's not very interesting because it doesn't do anything.</div><div>&nbsp;</div><div>Let's say want to send a POST request to the server:</div><div>&nbsp;</div><pre>
A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre"> </span> method: 'POST',
<span class="Apple-tab-span" style="white-space:pre"> </span>data: {
<span class="Apple-tab-span" style="white-space:pre"> </span> key1: 'value'
<span class="Apple-tab-span" style="white-space:pre"> </span> }
});</pre><div>&nbsp;</div><div>How about responding to the server? There are 5 possible callbacks: <code>start</code>, <code>complete</code>, <code>success</code> (or) <code>failure</code>, and <code>end</code>.</div><div>&nbsp;</div><div>If I wanted to alert the response from the server, here's what I would do:&nbsp;</div><pre>
A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre"> </span>on: {
<span class="Apple-tab-span" style="white-space:pre"> </span>success: function() {
<span class="Apple-tab-span" style="white-space:pre"> </span> alert(this.get('responseData'));
<span class="Apple-tab-span" style="white-space:pre"> </span> }
<span class="Apple-tab-span" style="white-space:pre"> </span> }
});
<br /></pre><div>What is <code>this.get('responseData')</code>? 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.</div><div>For instance, if your server returns JSON like <code>{&quot;myProperty&quot;: 2}</code>, you could do something like:</div><div>&nbsp;</div><pre>
A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre"> </span> dataType: 'json',
<span class="Apple-tab-span" style="white-space:pre"> </span> on: {
<span class="Apple-tab-span" style="white-space:pre"> </span> success: function() {
<span class="Apple-tab-span" style="white-space:pre"> </span> alert(this.get('responseData').myProperty); //alerts 2
<span class="Apple-tab-span" style="white-space:pre"> </span> }
<span class="Apple-tab-span" style="white-space:pre"> </span> }
});</pre><div>&nbsp;</div><div>You can also work with XML that way. Assuming your server returns something like: &lt;name&gt;AlloyUI&lt;/name&gt; you could do:</div><pre>
A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre"> </span> dataType: 'xml',
<meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span>on: {
<meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span>success: function() {
<meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span>alert(A.all(this.get('responseData')).all('name').text()); // alerts AlloyUI
<meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span>}
<span class="Apple-tab-span" style="white-space:pre"> </span> }
});
<br /></pre><div>You can also submit all of the data in a form via ajax as well. Here's the simplest version:&nbsp;</div><pre>
A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre">&nbsp;</span> form: {</pre><p><meta charset="utf-8" /></p><pre><span class="Apple-tab-span" style="white-space:pre">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; "> </span> id: 'myFormId'</pre><p><meta charset="utf-8" /></p><pre><meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span>}
});
</pre><div>That will serialize all of the data in the form, and send it to &quot;test.html&quot;.</div><div>&nbsp;</div><div>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.</div><div>Here's an example:&nbsp;</div><pre>
var myAjaxRequest = A.io.request('test.html', {
<meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span><span class="Apple-tab-span" style="white-space:pre"> </span>method: 'POST',
<meta charset="utf-8" /><span class="Apple-style-span" style="line-height: 18px; font-size: 12px; "><span class="Apple-tab-span" style="white-space: pre; "> </span> </span><span class="Apple-tab-span" style="white-space:pre"> </span>data: {
<span class="Apple-tab-span" style="white-space:pre">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; "> </span> key1: 'value1'</pre><p><meta charset="utf-8" /></p><pre><span class="Apple-tab-span" style="white-space:pre">&nbsp;</span><span class="Apple-tab-span" style="white-space: pre; "> </span> }</pre><p><meta charset="utf-8" /></p><pre>});
<br /></pre><div>Now later on, if I want to make that same ajax call again, all I have to do is call:</div><div>&nbsp;</div><pre>
myAjaxRequest.start();</pre><div>&nbsp;</div><div>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:</div><div>&nbsp;</div><pre>
var myAjaxRequest = A.io.request('test.html', {
<span class="Apple-tab-span" style="white-space:pre"> </span>autoLoad: false,
<span class="Apple-tab-span" style="white-space:pre"> </span>...
});</pre><div>&nbsp;</div><div>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:</div><div>&nbsp;</div><pre>
myAjaxRequest.set('cache', false);</pre><div>&nbsp;</div><div>Or if you wanted to change from POST to GET</div><div>&nbsp;</div><pre>
myAjaxRequest.set('method', 'GET');</pre><div>&nbsp;</div><div>Or change the dataType to JSON:</div><div>&nbsp;</div><pre>
myAjaxRequest.set('dataType', 'json');</pre><div>&nbsp;</div><div>Or even change the URI at the last moment:</div><div>&nbsp;</div><pre>
myAjaxRequest.set('uri', 'new_test.html');</pre><div>&nbsp;</div><div>Then when you're ready you would call:</div><div>&nbsp;</div><pre>
myAjaxRequest.start();</pre><div>&nbsp;</div><div>And if at any time after you have started the request, you want to stop the whole request, you can call:</div><div>&nbsp;</div><pre>
myAjaxRequest.stop();</pre><div>&nbsp;</div><div>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.</div><div>&nbsp;</div><div>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.</div><div>&nbsp;</div><div>I'll go into more details in the Plugins post, but it's really handy.</div><div>&nbsp;</div><div>See you then!</div>Nate Cavanaugh2010-03-18T06:12:26ZAlloyUI - Working with elements and eventsNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-elements-and-events2010-03-16T16:21:51Z2010-03-15T06:02:33Z<h3>Getting Started</h3> <p>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 &quot;sandboxes&quot;. 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.</p> <p>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.</p> <p>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.</p> <p>How do you create a sandbox?</p> <p>Simple:</p> <pre><code>AUI().use(function(A) { <span class="Apple-tab-span" style="white-space:pre"> </span>// Your code goes here });</code>
</pre> <p>Let's look at that real quick.</p> <p><code>AUI()</code> is a function call, and you attach a <code>.use</code> on it. Inside of that <code>.use()</code>, you can pass 1-n number of arguments, but the last argument *must always be a function*.</p> <p>You'll notice that the callback gets an argument passed to it called &quot;A&quot;. That A is *the* Alloy object. It's where all of Alloy objects and classes are stored.</p> <p>&nbsp;</p> <p>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:</p> <pre>
AUI().use('event', 'node', function(A) {
<span class="Apple-tab-span" style="white-space:pre"> </span>// Your code goes here
});
</pre> <p>When you see me write code samples where I do something like:</p> <pre>
A.one('body');<br /></pre> <p>assume that I am inside of the sandbox.</p> <h3>Working with elements and events</h3> <p>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.</p> <p>There are two methods you can use to get elements on the page, and each will return something different depending on the method.</p> <p>The methods are called <code>A.one()</code> and <code>A.all()</code>.</p> <p><code>A.one()</code> will return a Node object if it finds the element on the page (null otherwise), and <code>A.all()</code> will always return a NodeList (if it doesn't match any elements, it will return an empty collection).</p> <p>&nbsp;</p> <p>Here's a few examples of how <code>A.one()</code> would be used:</p> <pre>
var el_1 = A.one('#myCustomElement1');
var el_2 = A.one('.custom-element');
var el_3 = A.one('input[type=checkbox]');<br /></pre> <p>Notice how <code>A.one()</code> 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.</p> <p>If the selector cannot be matched on the page, then <code>A.one()</code> 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.</p> <p>For instance:</p> <pre>
var el_1 = A.one('#myCustomElement1');
if(el_1) {
<span class="Apple-tab-span" style="white-space:pre"> </span>el_1.setStyle('height', 50);
}
</pre> <p>This can seem a bit verbose to some people, so it could be avoided if you wish. You could write the above like so:</p> <pre>
A.all('#myCustomElement1').setStyle('height', 50);
</pre> <p>without risk of throwing an error.</p> <p>So why do I prefer <code>A.one()</code>? Mainly because of performance. <code>A.one()</code> 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).</p> <p>But both methods are there for you.</p> <p>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.</p> <p>However, there is a CSS3 module, and if you need to do something like:</p> <p><code>A.all('.custom-element &gt; div:nth-of-type(even)')</code>, just add the &quot;selector-css3&quot; module to your sandbox, and the selectors are available to you.</p> <p>It's pretty rare that we've actually *needed* these selectors, though, but again, they're there if you need them.</p> <p>That covers the basics on getting the elements, what about doing something with them?</p> <p>So, let's cover some common tasks:</p> <h3>Setting styles</h3> <p>I'm going to grab my element:</p> <pre>
var nodeObject = A.one('#myElement');<br /></pre> <p><strong>Setting a background color:</strong></p> <pre>
nodeObject.setStyle('backgroundColor', '#f00'); //Sets the background color to red<br /></pre> <p><strong>Setting a border&nbsp;</strong></p> <pre>
nodeObject.setStyle('border', '5px solid #0c0'); //Sets a large green border<br /></pre> <p>But what if I want to set multiple styles all at once? Just simply use the setStyles method (notice the &quot;s&quot; on the end of the name?).</p> <pre>
nodeObject.setStyles({
<span class="Apple-tab-span" style="white-space:pre"> </span>height: 200,
<span class="Apple-tab-span" style="white-space:pre"> </span>width: 400
});
</pre> <p>You can also get the current style for an element by doing something like:</p> <pre>
nodeObject.getStyle('border');<br /></pre> <p>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.</p> <p>For instance, let's say we wanted to move something to exactly 100px from the left, and 200px from the top.</p> <p>Usually we might do something like:</p> <pre>
nodeObject.setStyles({
<span class="Apple-tab-span" style="white-space:pre"> </span>left: 100,
<span class="Apple-tab-span" style="white-space:pre"> </span>position: 'absolute'
<span class="Apple-tab-span" style="white-space:pre"> </span>top: 200
});
</pre> <p>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.</p> <p>Instead, here's how you would do it now:</p> <pre>
nodeObject.setXY([100, 200])<br /></pre> <p>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.</p> <p>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:</p> <pre>
nodeObject.center();<br /></pre> <h3>Working with class names</h3> <p>All of the most convenient ways of working with class names, and then some, are here:</p> <pre>
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');<br type="_moz" /></pre> <p><meta charset="utf-8">In that last line, <code>radioClass()</code> 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. </meta></p> <p>&nbsp;</p> <h3>Manipulating elements</h3> <p><strong>Appending a new element to the nodeObject:</strong></p> <pre>
nodeObject.append('&lt;span&gt;New Text&lt;/span&gt;');<br /></pre> <p><strong>Appending the nodeObject to another element already on the page:</strong></p> <pre>
nodeObject.appendTo('body');<br /></pre> <p><strong>Updating the innerHTML of an element:</strong></p> <pre>
nodeObject.html('&lt;b&gt;new text&lt;/b&gt;');<br /></pre> <p><strong>Removing an element:</strong></p> <pre>
nodeObject.remove();<br /></pre> <p><strong>Creating a brand new node from scratch:</strong></p> <pre>
var newNodeObject = A.Node.create('&lt;div id=&quot;myOtherElement&quot;&gt;Test&lt;/div&gt;');<br /></pre> <h3>Moving up and down the elements</h3> <p>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).</p> <p><strong>Finding the first parent of nodeObject with the class name of .custom-parent:</strong></p> <pre>
nodeObject.ancestor('.custom-parent');<br /></pre> <p><strong>Finding the first child with the class name of .custom-child:</strong></p> <pre>
nodeObject.one('.custom-child');<br /></pre> <p><strong>Finding all children with the class name of .custom-child:</strong></p> <pre>
nodeObject.all('.custom-child');<br /></pre> <p>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.</p> <p>Meaning this: it makes sense to have a collection, like nodeListObject, which contains 5 div elements, and when you call <code>nodeListObject.setStyle()</code> for that style to be applied to all 5 elements, or if you call <code>nodeListObject.append('&lt;b&gt;test&lt;/b&gt;')</code> for it to append a new b element to every item.</p> <p>But it doesn't make much sense to do: <code>nodeListObject.getStyle('backgroundColor')</code>. What should it return? The first item in the collection? The last item?</p> <p>And since it's insanely easy to do this instead:</p> <pre>
nodeListObject.item(0).getStyle('backgroundColor')</pre> <p>it just makes more sense not to add the methods onto the NodeList to avoid confusion when getting data out of an element.</p> <h3>Getting properties</h3> <p>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.</p> <p>Instead, we do <code>nodeObject.get('id')</code> or <code>nodeObject.get('parentNode')</code>.</p> <p>Here's what is REALLY cool about using the getter: <code>nodeObject.get('parentNode')</code> will return another wrapped Node object, and if it's a collection, like <code>nodeObject.get('childNodes')</code>, it will be a wrapped NodeList object.</p> <p>So all of the DOM properties are available.</p> <p>EVEN cooler:</p> <p>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:</p> <pre>
nodeObject.get('parentNode.parentNode.parentNode').setStyle('backgroundColor', '#f00');<br /></pre> <h3>Interaction time</h3> <p>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?</p> <p>It's actually pretty simple. Every Node and NodeList has a method called <code>on()</code> that let's you, appropriately enough, do something &quot;on&quot; that event.</p> <p>Let's say I want to alert &quot;Hello&quot; when a user clicks on the nodeObject</p> <pre>
nodeObject.on('click', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>alert('hello');
});
</pre> <p>Or let's say we want to add a border when a user first moves their mouse over an item:</p> <pre>
nodeObject.on('mouseenter', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>this.setStyle('border', '5px solid #555');
});
</pre> <p>Notice how the &quot;this&quot; object is used without wrapping it? It's automatically wrapped for you to be a Node object, which is incredibly convenient.</p> <p>But what if, on the off chance, you *must* get the original DOM object. You can do <code>nodeObject.getDOM()</code> and it will return you the underlying DOM element.</p> <p>This also applies to NodeList objects as well.</p> <p>So if you do <code>A.all('div').getDOM()</code> it will return you an array of plain DOM elements.</p> <p>What if about if you need to remove an event?</p> <p>Let's say you do this:</p> <pre>
nodeObject.on('click', myFunc);<br /></pre> <p>you can detach the event by simply doing:</p> <pre>
nodeObject.detach('click', myFunc);<br /></pre> <p>or you could even just remove all events by not passing a second argument, like this:</p> <pre>
nodeObject.detach('click');<br /></pre> <p>What if you want to do some work on document ready?</p> <p>You can do:</p> <pre>
A.on('domready', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>// More work here
});
</pre> <p>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:</p> <pre>
AUI().ready('event', 'node', function(A){
<span class="Apple-tab-span" style="white-space:pre"> </span>// This code will fire on DOM ready
<span class="Apple-tab-span" style="white-space:pre"> </span>// and when this modules are ready
});
</pre> <p>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.</p> <p>Here's how you would listen to it:</p> <pre>
nodeObject.on('key', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>// escape + shift has been pressed on this node
}, 'down:27+shift');
</pre> <p>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?</p> <pre>
nodeObject.on('click', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>event.preventDefault();
});
</pre> <p>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.</p> <p>You can stop your event from bubbling though, here's how:</p> <pre>
nodeObject.on('click', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>event.stopPropagation();
});
<br type="_moz" /></pre> <p>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.</p> <p>But there's also a shortcut if you want to just preventDefault and stopPropagation, which is like so:</p> <pre>
nodeObject.on('click', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>event.halt();
});
<br type="_moz" /></pre> <h3>Event delegation</h3> <p>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.</p> <p>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.</p> <p>And let's say you add elements via ajax, it's a pain to have to reattach events after every update as well.</p> <p>So let's say go through an example. Let's say that we have this HTML:</p> <pre>
&lt;ul id=&quot;myList&quot;&gt;&lt;li&gt;Test&lt;/li&gt;&lt;/ul&gt;<br /></pre> <p>Here's how we would use delegation:</p> <pre>
var myList = A.one('#myList');<br /></pre> <pre>
myList.delegate('click', function(event){
<span class="Apple-tab-span" style="white-space:pre"> </span>alert(event.currentTarget.html());
}, 'li');
<br type="_moz" /></pre> <p>Notice a few things. One, we're calling a method called delegate, but it's very similar to &quot;on&quot;. In fact, the only difference to the &quot;on&quot; method is that the third parameter is a selector that we will test to make sure the element matches before firing the function.</p> <p>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 &quot;on&quot; method, so I recommend using it.</p> <p>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:</p> <pre>
myList.append('&lt;li&gt;Test 2&lt;/li&gt;');
</pre> <p>It will add another list item, and when you click on this new item, it will alert &quot;Test 2&quot;, without having to reattach the event.</p> <h3>Conclusion</h3> <p>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.</p>Nate Cavanaugh2010-03-15T06:02:33ZAlloyUINate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui2010-03-15T05:25:10Z2010-03-15T05:14:27Z<p>&nbsp;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 <a href="http://www.liferay.com/video?v=4588292">the webinar</a>, the reasons behind the long silence in my blog postings has been due to the work on the AlloyUI framework.</p><p>Over the past 6 months, <a href="http://liferay.com/web/eduardo.lundgren">Eduardo Lundgren</a> and I have been furiously working away building a unified UI library on top of the revolutionary YUI3.</p><p>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.</p><p>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.</p><p>One of the most common questions I get is: &quot;So what about jQuery and jQuery UI?&quot; You will still be able to use jQuery and jQuery UI (or any javascript or front end library of your choice) in Liferay.</p><p>We have however stopped using it for our portlets and plugins, and are instead building everything on top of Alloy.</p><p>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.</p><p>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.</p><p>That's what we want to accomplish with Alloy.</p><p>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?</p><p>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?</p><p>How clear was the thinking behind the widgeting system? How much documentation was there for it?</p><p>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.</p><p>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 &quot;<a href="http://37signals.com/svn/posts/995-if-youre-working-in-a-big-group-youre-fighting-human-nature">pizza-sized</a>&quot; team thats renowned for leading front end innovation on the web.</p><p>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).</p><p>YUI3 is designed to be stretched to those different scenarios seamlessly.</p><p>The other question I've gotten is &quot;can it be used outside of Liferay?&quot; 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.</p><p>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.</p><p>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.</p><p>However, as great as all of this sounds, there is one area where people people may be concerned, which is documentation.</p><p>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.</p><p>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).</p><p>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).</p><p>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.</p><p>So here's what we'll learn:</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>1. Working with elements and events</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>2. Ajax</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>3. Plugins</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>4. Widgets</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>5. Utilities</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>6. Animation, Drag &amp; Drop</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>7. Layouts &amp; Forms in CSS</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>8. Using Alloy taglibs in JSPs</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>9. Advanced beauties (we'll look at DelayedTask, AOP, OverlayManager, Queue &amp; AsyncQueue, and DataSet)</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>10. Odds and ends (a few like Char Counter, History, Cache, Profiler, and DataType parsing)</p><p>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.</p><p>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.</p><p>Looking forward to seeing you here!</p>Nate Cavanaugh2010-03-15T05:14:27ZA new Liferay WallpaperNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/a-new-liferay-wallpaper2009-08-20T04:58:06Z2009-08-19T15:56:44Z<p>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.</p><p>Today's post, however, is of a different bent. You may remember <a href="http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/1845875;jsessionid=E2AB43AA12BEAC4E3EA0EAA501FFF700">the wallpaper I created</a> 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.</p><p>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.&nbsp;Google stopped counting, but I'm guessing it's <strike>3-4000 unread items</strike> (try 5,552 by current count...).<br />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&nbsp;couldn't help thinking about how I wanted it to look.</p><p>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 :)</p><p>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:</p><p><a target="_blank" href="http://alterform.com/liferay/liferay_of_life_1920x1200.jpg"><img alt="Liferay of Life wallpaper" src="http://alterform.com/liferay/liferay_of_life_preview.jpg" /></a></p><p>Clicking the image will take you to the Large&nbsp;WideScreen version, but if you want more options, you can click here:</p><p><a href="http://alterform.com/liferay/liferay_of_life.zip">Liferay of Life Wallpapers</a></p><p>This link includes a wide range of resolutions for different ratios and even one formatted for an iphone. I hope you enjoy it :)</p>Nate Cavanaugh2009-08-19T15:56:44ZIE8 is out. What does it mean for your theme?Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/ie8-is-out-what-does-it-mean-for-your-theme-2009-03-19T17:42:44Z2009-03-19T17:19:50Z<p>If you're as cutting edge as our own <a href="http://www.liferay.com/web/jneal">Jonathan Neal</a>, you might have downloaded the IE8 final that was released this morning at 9am PST.</p><p>IE8 is a HUGE improvement towards standards, and while it still has not caught up with Firefox, Safari,&nbsp;Chrome or Opera, when it comes to IE, we will take what we can get :)</p><p>However, since it's more accurate with the standards, you may have noticed that your theme could be off in&nbsp;IE8 compared to IE7.<br />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.</p><p>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.</p><p>So what's the fix?&nbsp;It turns out there are 2 good fixes, one for the short term and one for the long term.</p><p>The long term fix is to of course change your selectors from being broad to being specific.<br />For instance, if you are using a selector like this:<br />.ie #wrapper {<br />}</p><p>And that selector is causing issues, you should change it to be:</p><p>.ie7 #wrapper, .ie6 #wrapper {<br />}</p><p>This will make sure that the selector only applies to specific versions of IE that have the broken functionality you're addressing.</p><p>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.</p><p>In the &lt;head&gt; of your theme, you can add this:<br />&lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=7&quot; /&gt;<br /><br />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.</p>Nate Cavanaugh2009-03-19T17:19:50ZChanges in Liferay's front endNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/changes-in-liferay-s-front-end2009-02-19T02:42:47Z2009-02-19T01:54:05Z<p>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.</p><p>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.</p><p>I added a file called <strong>deprecated.js</strong> 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.<br />This file will be updated any time something from the Javascript API has been changed from the previous version.</p><p>Here's how it will work:</p><p>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.</p><p>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.</p><p>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.</p><p>So in 5.3, the deprecated.js might look like this:</p><p>//To be removed in 5.4<br /><br />// LPS-1234<br />LayoutConfiguration = Liferay.LayoutConfiguration;</p><p>// LPS-4567<br />themeDisplay = Liferay.ThemeDisplay;</p><p>&nbsp;</p><p>However, in Liferay 5.4, this entire block would be commented out.<br />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.</p><p>In Liferay 5.5, this block would be removed completely.</p><p>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.</p><p>There are a couple of reasons for this change, and I wanted to talk about them.</p><p>One of the areas where I&nbsp;believe Liferay shines is in innovation. We are able to rapidly change faster than any organization&nbsp;I've worked for. However, whenever things change rapidly, not everyone is able to keep up.&nbsp;It's very easy as a developer to forget this, unless you're the developer who is trying to keep up :)</p><p>One of the reasons we introduced <a href="http://www.liferay.com/web/guest/products/ee">Liferay Enterprise Edition</a> 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.</p><p>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.<br />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.<br />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.</p><p>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.</p><p>What are some other ideas? <a href="http://www.liferay.com/web/bcheung">Bryan Cheung</a> 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 <a href="http://twitter.com/liferay">Liferay's twitter feed</a> what if we had a Liferay Developer twitter account?&nbsp;Is that something anyone would be interested in?<br />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.</p><p>For instance, if I add some awesome functionality to the JS, but it doesn't warrant an entire blog post, I&nbsp;could tweet something like &quot;Added the ability to create ajaxable panels:&nbsp;LPS-12345&quot; which would have a link to the ticket where I discuss what was added.</p><p>Would this be helpful to anyone?</p><p>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.</p><p>Another option that has completely changed my life and sanity is that <a href="http://liferay.com/web/myoung">Mike Young</a> recently installed <a href="http://svn.liferay.com">Fisheye</a> to watch our commits. I think the feature I&nbsp;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 <a href="http://www.liferay.com/web/elundgren">Eduardo Lundgren</a> or <a href="http://liferay.com/web/elundgren">Peter Shin</a>, anything committed to the <a href="http://svn.liferay.com/browse/portal/trunk/portal-web/docroot/html">portal-web</a> directory and anything committed to the <a href="http://svn.liferay.com/browse/portal/trunk/portal-web/docroot/html/themes">themes</a> or to the <a href="http://svn.liferay.com/browse/portal/trunk/portal-web/docroot/html/js">js</a> directories (I know there is some duplication there, but I'd rather have a bit more info).</p><p>If you want to stay on top of what people are doing, it's an awesome way to keep your ear to the ground.</p><p>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.</p>Nate Cavanaugh2009-02-19T01:54:05ZEven MORE Performance fixesNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/even-more-performance-fixes2009-01-13T04:36:45Z2009-01-13T04:17:14Z<p>It's hard to contain my excitement about what I am going to share.<br /><br /><a href="http://liferay.com/web/bchan">Brian Chan</a> (along with <a href="http://liferay.com/web/elundgren">Eduardo Lundgren</a>) 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.<br /><br />What is it, and why should you care?<br /><br />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.<br />I'm talking specifically about *_unpacked.* and *_packed.*<br /><br />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.<br /><br />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.<br /><br />Well, the change that <a href="http://liferay.com/web/bchan">Brian</a> committed now completely removes those files and they are handled for you automatically.<br /><br />Think about something that happened to my good friend <a href="http://liferay.com/web/rauge">Ray Auge</a> not too long ago while we were doing some work for one of our critically acclaimed clients.&nbsp;I can't say who, but practically everyone has heard of them. Anyways, they have a theme hot-deployed and <a href="http://liferay.com/web/rauge">Ray</a> 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.<br />Only because I had been touched by this little bug had I known what was causing it.<br />But anyone that knows <a href="http://liferay.com/web/rauge">Ray</a> 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.<br /><br />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).<br /><br />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.<br />Sometimes we have to do maintenance on a theme or plugin someone else has developed.<br /><br />So how is this fixed now?<br /><br /><a href="http://liferay.com/web/bchan">Brian Chan</a> 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?<br />All <a href="http://liferay.com/web/rauge">Ray</a> 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.<br /><br />Either of those is far preferable to having to rebuild the theme from the source, and far less confusing.<br /><br /><a href="http://liferay.com/web/bchan">Brian</a> and <a href="http://liferay.com/web/elundgren">Eduardo</a> have been really hard at work doing this and <a href="http://www.liferay.com/web/elundgren/blog/-/blogs/best-practices-for-speeding-up-liferay">even more performance fixes</a> over the past couple of weeks and I've been amazed at how snappy our website has become.<br /><br />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 <a href="http://www.liferay.com/web/guest/products/portal/getitnow">Liferay Enterprise Edition</a>. 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.<br /><br />And when you get a chance, shoot by <a href="http://liferay.com/web/bchan">Brian</a> and <a href="http://liferay.com/web/elundgren">Eduardo's</a> pages and thank them for getting this in.</p>Nate Cavanaugh2009-01-13T04:17:14ZLiferay wallpaperNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/liferay-wallpaper2008-12-29T03:14:03Z2008-12-29T02:34:20Z<p>There are a ton of amazingly creative people here at Liferay, and I'm always stunned with the stuff that people create.</p><p>Last night I was doing a ton of coding and some inspiration struck to make some Liferay &quot;fan art&quot;. I don't get the time to do as much artwork as I used to, so it was a nice treat for me.</p><p>So, it totally is not &quot;corporate&quot; and doesn't really follow the Liferay branding guidelines, but like I said, it's &quot;fan art&quot; and normal rules don't apply. I&nbsp;wanted to make something a bit edgier than we're used to having.</p><p>I've created a package with common wallpaper sizes that you can <a href="http://alterform.com/liferay/liferay_wall_package.zip">download here</a>.</p><p>Here is a brief preview:</p><p><a href="http://alterform.com/liferay/liferay_wall_preview_large.jpg"><img alt="" src="http://alterform.com/liferay/liferay_wall_preview.jpg" /></a></p>Nate Cavanaugh2008-12-29T02:34:20ZIs this site running Liferay?Nate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/is-this-site-running-liferay-2008-11-21T03:02:28Z2008-11-21T02:42:18Z<p>It's been a while, but trust me, we've been working like crazy to get 5.2 out for your using pleasure.</p> <p>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?<br /> <img alt="" style="width: 284px; height: 198px;" src="http://alterform.com/liferay/install_complete.png" /></p> <p>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?<br /> Only about the absolute coolest extension for Firefox known to man (yes it requires Firefox).</p> <p>So what do you do?<br /> <br /> If you have Greasemonkey installed, you should just skip to Step 3.</p> <p>Step 1: Go to <a href="https://addons.mozilla.org/en-US/firefox/addon/748" target="_blank">https://addons.mozilla.org/en-US/firefox/addon/748</a></p> <p>Step 2: Click the Add to Firefox button, and install the plugin. It will notify you to restart firefox.<br /> <img alt="" src="http://alterform.com/liferay/install_gm.png" /></p> <p>Step 3: Once you've restarted, go here: <a href="http://userscripts.org/scripts/show/37259" target="_blank">http://userscripts.org/scripts/show/37259</a></p> <p>Step 4: Press the Install button at the upper right<br /> <img alt="" src="http://alterform.com/liferay/install_script.png" /></p> <p>Step 5: Agree to the install<br /> <img alt="" src="http://alterform.com/liferay/install_agree.png" /><br /> &nbsp;</p> <p>Step 6: Visit a website (like <a target="_blank" href="http://liferay.com">http://liferay.com</a>)</p> <p>&nbsp;</p> <p>Step 7: Enjoy the info:<br /> <img alt="" src="http://alterform.com/liferay/install_complete.png" /><br /> <br /> 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.</p> <p>&nbsp;</p> <p>Enjoi!</p>Nate Cavanaugh2008-11-21T02:42:18ZThe new Liferay.comNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/the-new-liferay-com2008-10-09T05:32:37Z2008-10-09T05:21:01Z<p>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.<br />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.<br /><br />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 <a href="http://liferay.com/web/bmiller/blog">Bmiller</a> 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.<br /><br />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, <a href="http://liferay.com/web/acheng/blog">Alice</a>, <a href="http://liferay.com/web/bmiller/blog">Bmiller</a> and I got on a conference call together on a cloudy May day and hashed out how we would design the site.<br />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.<br /><br />Part of this was due to information overload, part was due to just general information architecture issues.<br /><br />So out of that first day came our initial wireframe for the front page.<br /><br /><a href="http://alterform.com/liferay/lr_prog_website_ia_wireframe_5_26_08.png"><img width="500" height="375" src="http://alterform.com/liferay/lr_prog_website_ia_wireframe_5_26_08.png" alt="" /></a><br /><br />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.<br /><br />So, in short order, Bmiller delivered this to my inbox to show me the progress:<br /><br /><a href="http://alterform.com/liferay/lr_prog_LR_Homepage_v03.06.jpg"><img width="500" height="591" src="http://alterform.com/liferay/lr_prog_LR_Homepage_v03.06.jpg" alt="" /></a><br /><br />A little after this, <a href="http://liferay.com/web/bcheung/blog">Bcheung</a> and <a href="http://liferay.com/web/clam/blog">Cecilia</a> got involved and we started getting all sorts of pressure to make all kinds of changes.<br /><br />So one little tangent here that I'd like to touch on: Conflict is great.<br />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.<br /><br />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.<br />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.<br /><br />Or as Bchan told me during my interview almost 2 years ago: &quot;The best idea wins.&quot;<br /><br /><a href="http://liferay.com/web/bcheung/blog">Bcheung</a> and <a href="http://liferay.com/web/bchan/blog">Bchan</a> 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.<br /><br /><a href="http://liferay.com/web/bmiller/blog">Bmiller</a> 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.<br /><br /><a href="http://liferay.com/web/acheng/blog">Alice</a> 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.<br /><br /><a href="http://liferay.com/web/rpark/blog">Ryan Park</a> 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.<br /><br />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.<br />If I'm forgetting to give proper credit, please let me know.<br /><br />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 :)</p>Nate Cavanaugh2008-10-09T05:21:01ZOh yeah, about the auto-saveNate Cavanaughhttps://web.liferay.com/web/nathan.cavanaugh/blog/-/blogs/oh-yeah-about-the-auto-save2008-05-23T21:57:40Z2008-05-23T21:48:32Z<p>I just wanted to drop a couple of notes about the auto-save that I didn't mention before....</p><p><b>1. I didn't do the bulk of the work</b><br />That honor goes to <a href="http://liferay.com/web/jneal/profile">Jonathan Neal.</a> <a href="http://liferay.com/web/bchan/profile">Bchan</a> 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 <a href="http://liferay.com/web/jneal/profile">wall</a> and tell him great job :)</p><p><b>2. There was a race condition</b><br />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.</p><p><b>3. We changed the interval time</b><br />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).</p><p><b>4. It now is smarter</b><br />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.</p><p>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.<br />&nbsp;</p>Nate Cavanaugh2008-05-23T21:48:32Z