Corrrecting Anchoring Tags

Dear Friend,

Every one of my posts has a story with it, whether it is a request from an user, or a need of myself to tweak something to look more interesting or at least to make it right. This time is no different :)

Some of my readers will notice that I just recently abandon the comment approval option. You could post a comment, and will see it instantly. Well, that comes with a price. I have to monitor the Latest Comments Feed more often, or my blog will be bombarding with spamming comments.

One of the things I notice, because of this new change-of-mind, is that the comment link only sends me to the top of the post. However, I know that in HTML you could have a tag with an unique name and the link will lead to the right place. Come to find out, the link is correctly formatted, such as this:

http://hoctro.blogspot.com/2006/share.html#3190550298489538229

but the actual HTML code in the content of the comment looks like this

<a name='comment-3190550298489538229'></a>

where it should be coded as

<a name='3190550298489538229'></a>

Looks like the right hand doesn't talk to the left hand, or in this case the person who codes the post widget doen't talk to the guy who codes the feed widget. Just kidding, we love you Blogger guys, really!!!

Now that I know what to fix, I need to find where to fix. After a while, I find the culprit :-)

If you want to fix this problem too, then follow these steps:

1. Open Template->Edit HTML. Turn the option "Expand Widget Templates" on.

2a. Locate the widget named Blog1, by looking for this line:

<b:widget id='Blog1' locked='false' title='Blog Posts' type='Blog'>

2b. Locate this line (a function[includable]) with id='comments'

<b:includable id='comments' var='post'>

2c. Look for this line about 10+ lines below:

<a expr:name='"comment-" + data:comment.id'/>

and just simply take out the ["comment-" + ] for the line to become:

<a expr:name='data:comment.id'/>




and you're done.

You can see a sample by clicking on any "latest comment" in my blog's feed (or try this one out, it goes to the end of my "killer post") and it'll take you right to where the comment is located. Now I can check out the users' comment more effectively.

Cheers,

Hoctro

To Show or not to Show your template message



Dear Friend,

Phoenix, one of my readers, noticed that there is some sort of a message bar showing, whenever he clicks on one of the "Label" search. I noticed that too, and knowing Beta code, I thought they must have embedded this new piece of code somewhere on the Blog1 widget.




Indeed it does, and this new feature only happens to blogs that are created recently. I think this is some sort of a "wrapped-up" feature as part of their "Blogger Beta: Feature Complete!".

Here it is, the single function call which causes the message to display:



So you see, the line is inside the Blog1 widget, under the "main" includable. Turning it off is quite simple if you want to. You can either erase the line or put a pair of comment code like so:

<!-- <b:include data='top' name='status-message'/> -->

and the message will disappear from then on.

Cheers,

Hoctro



Update: Please see the comments of this post to have more info on this Beta's message usage. Thanks a bunch, Phydeaux3 & Bravery Onions!

Showing Hint under the Title to View a Page With Comments



Dear Friend,

My friend Carlos asks me how to let readers to see posts with comments. I think this is not possible while a viewer is seeing a label or archived query, or if he or she is looking at the home page. I guess the technical reason why Blogger in Beta didn't implement that, is just simply try to avoid a potentially large stream of data to arrive to the viewer's PC. Imagine a situation where you query a category that has 20 posts in each, each post with 20-30 comments. It'll take quite a while to display that page to your screen.

My work-around to this would be to give a hint to the reader to click on the title of the post in order for him or her to read both the full post and the comments.

If you like this solution, then follow these simple steps:

1. Locate the Blog1 widget

<b:widget id='Blog1' locked='false' title='Blog Posts' type='Blog'>



then the includable named "post"

<b:includable id='post' var='post'>


2. Insert the seven lines in bold to the code. What this does is to check if you're allowing comment for a post, your post has at least one comment ( !=0 ), and you are not on the full post (item) page, then insert the hint. Feel free to use your HTML expertise to alter the simple tags I have, or change the text to become clearer.


<b:includable id='post' var='post'>
  <div class='post uncustomized-post-template'>
    <a expr:name='data:post.id'/>
    <b:if cond='data:post.title'>
      <h3 class='post-title'>
      <b:if cond='data:post.url'>
        <a expr:href='data:post.url'><data:post.title/></a>
      <b:else/>
        <data:post.title/>
      </b:if>
      </h3>

      <b:if cond='data:post.allowComments'>
        <b:if cond='data:post.numComments != 0'>
          <b:if cond='data:blog.pageType != "item"'>
            <div><i>(For full text with comments please click on the title)</i></div>
          </b:if>
        </b:if>
      </b:if>

    </b:if>


I apply this fix to my webpage, so you can see how this works.

Have a wonderful day,

Hoctro

Blogger Hack: Adding Introductions



Dear Friend,

Sometimes a little introductory paragraph or two helps "break the ice" between you and your readers, so they know what they are about to read, rather than just guessing based on one or two "label" words. You can see the new hack in effect on my site. Select on "Blogger Beta"->"My Hacks", for example, will display a little introduction first, then all the hack articles.

Below are the steps on how to implement this hack. As usual, make a template backup copy, then try it on your test blog first. Also, remember to check on "Expand Widget Templates."

Step 1: Add "introduction" function to Blog1 widget

First, locate the widget by looking for this line:

<b:widget id='Blog1' locked='false' title='Blog Posts' type='Blog'>

Once you see it, look for this line:

<b:includable id='main' var='top'>

Then paste this code right in front of it:

<b:includable id='sub-introduction' var='data:posts'>
<!-- **Introduction Hack - by Hoctro 10/2006 hoctro.blogspot.com ** Step 1 **

**** -->
<b:if cond='data:blog.pageType != "item"'>
  <b:loop values='data:posts' var='post'>
  <!-- Check for Homepage case -->
    <b:if cond='data:blog.url == data:blog.homepageUrl'>
      <b:if cond='data:post.title == "Introduction"'>
        <div class='post'>
          <div class='post-body'>
            <p><data:post.body/></p>
          </div>
        </div>
      </b:if>
    </b:if>
  <!-- Check for matched labels -->
  <b:loop values='data:post.labels' var='label'>
    <b:if cond='data:post.title == data:label.name'>
      <div class='post'>
        <div class='post-body'>
          <p><data:post.body/></p>
        </div>
      </div>
    </b:if>
 </b:loop>
</b:loop>
</b:if>
</b:includable>


Step 2: Modify the main includable of Blog1 widget

Scroll down to this line again,


<b:includable id='main' var='top'>


then look for the portion of code that starts and ends with these lines,



  <div id='blog-posts'>


...

  <!-- navigation -->
  <b:include name='nextprev'/>





You are going to replace everything in between with these lines,


<!-- **Introduction Hack - by Hoctro 10/2006 hoctro.blogspot.com ** Step 2 **

**** -->
   <b:include name='sub-introduction' values='data:posts'/>
    <b:loop values='data:posts' var='post'>
      <b:if cond='data:post.title == "Introduction"'>
        <b:if cond='data:blog.pageType == "item"'>
          <b:include data='post' name='postQuickEdit'/> 
          <b:include data='post' name='post'/>
        </b:if>
      <b:else/>
        <b:if cond='data:post.dateHeader'>
          <h2 class='date-header'><data:post.dateHeader/></h2>
        </b:if>
        <b:include data='post' name='postQuickEdit'/> 
        <b:include data='post' name='post'/>
        <b:if cond='data:blog.pageType == "item"'>
          <b:if cond='data:post.allowComments'>
            <b:include data='post' name='comments'/>
          </b:if>
        </b:if>
       </b:if>
    </b:loop>
  </div>


Step 3: Name your new introductory posts with "special" titles

Name your "special" new introductory posts' titles with exactly the same label names. for example, for the introductory post of "My Hack" to show correctly on my homepage, I had to name the title as "Blogger_Beta*My_Hacks" (I use my own's two level tab hack, hence the star (*) symbol in the middle) Also, after composing this special post, you need to change the date to some time in the past, so it will not show up as the latest. Also, if your introductory post has more than one label (which you shouldn't do,) make sure the last label is the same as the post's title.



here is the result,




For an introduction in your homepage, you will need something different. You need to create a post with the title "Introduction", do not assign a label (leave it blank), and set the date to a distant future! (I owe Hans from "Beautiful Beta" for this "set-in-a-future-date", by the way. Thanks! Hans.) This will make sure the post always includes in the homepage's post collection, thus it will show up. When displayed, this post will not show a title nor allow any comment from the readers.



Here is the result,



That's all it takes to have introductory paragraphs to show up on your blog. I hope you will able to make use of this hack.


Technical Comments:


Below are my technical comments, explaining the hack's logic. If you are interested in then read on. You're not required to understand my explanations to make it work on your blog.

1. I'm glad I am finally able to write another hack without the help of JavaScript. There is nothing wrong with JavaScript, it's just that stretching beta's own simple language is really fun. Indeed, beta's template doesn't need any Javascript at all, and it still looks great.

2. In Step 1: the "introduction" function, I create a loop, thus in effect make Blog1 loops through its post collection twice. That's how the introductory post can bubble up to be the first in the list. Inside the loop, I have two cases:

-If the reader is at homepage, then check to see if one of the post has the title of "Introduction." If so, prints the body only. This post must not have a label assigned.

- Next, I create another loop to go thru all the labels, and if one of them match the post's title, then also prints the body.

3. In Step 2, the goal is completely different. We want the introduction not to show up twice, yet at the same time it must be visible in the item page, if someone clicks on it from the archived list, hence the if-then-else structure.

I hope you will find some good use of both the hack and its source code as well.

Have a wonderful day.

Hoctro
(10/8/2006)

How to add a new CSS parameter to your Blogger template



Dear Friend,

Cecilia left a comment asking me if I know anything about changing the default background of the blog. As usual, I go looking for answers in several of my CSS books (Bulletproof Web Design, CSS CookBook, Eric Meyer on CSS, etc.) For this one, the answer is right on Chapter 1 of the "Bulletproof Web Design" book. So here it is, if you have one of the Minima designs, please alter the equivalent tags of the template's CSS portion like this:



body {
/*background:$bgcolor;*/
margin:0;
color:$textcolor;
font:x-small Georgia Serif;
font-size/* */:/**/small;
font-size: /**/small;
text-align: center;
background: $bgcolor url(http://photos1.blogger.com/blogger2/7873/821550570340913/1600/bg.gif) repeat-x top left;
}


/* Header
-----------------------------------------------
*/

#header-wrapper {
width:660px;
margin:0 auto 10px;
border-bottom:1px solid $bordercolor;
}

#header {
margin: 5px;
/*border: 1px solid $bordercolor;*/ text-align: center;
color:$pagetitlecolor;
}


/* Outer-Wrapper
----------------------------------------------- */
#outer-wrapper {
width: 660px;
margin:0 auto;
padding:10px;
text-align:left;
border: 1px solid $bordercolor;
font: $bodyfont;
background: #fff;
}



Discussion:

* In CSS, if you want some effects to be temporary disabled, use this /* xxxxxx */ format. Notice that a comment inside another comment is not acceptable, such as

/* xxx /* xxxxxx */ xxx */


*This line, in particular, adds a faded yellow picture to the top-left of the screen, and repeats that picture in the horizontal (x) direction.

background: $bgcolor url(http://photos1.blogger.com/blogger2/7873/821550570340913/1600/bg.gif) repeat-x top left;


* In addition, I tweak some code so that instead of the default two boxes around my title, now I only have a line at the bottom. Since the line is added with same color as the border's, I can change it visually with the "Font and Color" Editing option.

#header-wrapper {
width:660px;
margin:0 auto 10px;
border-bottom:1px solid $bordercolor;
}




* You can add more font and color variables to the template, and they will show up in the "Font and Color" design tab. What this means is that you will have many more options to play around with, not just the default ones from Blogger templates.

For example, I could create a new color variable (inside the <b:skin> tag at the front of the template):

<Variable name="commenttextcolor" description="Comment Text Color"
type="color" default="#999" value="#999999">


and it would show up in the "Font and Color" tab,



This new variable has a name so you can add later as $commenttextcolor in the actual CSS code, a description name for it to show up in "Font and Color" design tab, the type of "color", and a default and actual value. I think Blogger will write whatever your choice back to this "value" so your template is always up-to-date and independent of Blogger's database.

Now that you have a variable, you can apply that to the CSS code, just like any other default variables.

* For my Vietnamese blog, I have used the same code showing in this article to modify its appearance. Please take a look.

Have a nice day,

Hoctro (10/2006)
Blog: http://hoctro.blogspot.com/

Showing a Hint on Where to Post a Comment



Dear Friend,

Nanny22girls from Google Help Groups: Customizing Templates
asks if she could reword the comment line at the end of each post in index pages to say "Leave a comment here", along with the number of comments.

After looking into it, here is my quick fix:

In Template->Edit Html, check on "Expand Widget Templates" to view the entire template. Then, locate <b:includable id='post' var='post'>, and keep scrolling down to see this:

<span class='post-comment-link'>
        <b:if cond='data:blog.pageType != "item"'>

          <b:if cond='data:post.allowComments'>
            <a class='comment-link' 
expr:href='data:post.addCommentUrl' expr:onclick='data:post.addCommentOnclick'>
          <b:if cond='data:post.numComments == 1'>1 
          <data:top.commentLabel/><b:else/><data:post.numComments/> 
          <data:top.commentLabelPlural/></b:if></a>
          </b:if>
        </b:if>

      </span> 

You are going to replace the code in bold with this code:

<b:if cond='data:post.allowComments'>
  <b:if cond='data:post.numComments == 1'>1 
Comment<!--<data:top.commentLabel/>-->
  <b:else/><data:post.numComments/> 
<data:top.commentLabelPlural/>
  </b:if>
  <a class='comment-link' expr:href='data:post.addCommentUrl' 
expr:onclick='data:post.addCommentOnclick'>Leave your comment here</a>
</b:if>

Notice there is a space at the end of "1" before the code goes to the next line, and the same thing happens with the <data:post.numComments/>. If you don't leave a space, then there won't be a space between the number and the word comment (or comments.) Notice I fix the "1 Comments" bug too.

Here is the result:



and the "1 comments" bug:



I think this enhancement really makes commenting more easier, since the link on where to add your comment now is easier to look for.

Have a nice day,

Hoctro (9/2006)
Blog: http://hoctro.blogspot.com/

On Celebrating 10 000 Visitors Milestone




Dear Friend,

It's October already! About a month ago, Blogger in Beta released its feature of "Edit HTML", revealing how powerful and more straight-forward the new templating design is. Not being a hacker of the Classic Blogger, I had the advantage of looking at the new template design with a fresh, un-bias mind frame, and was able to find some good usage of the new label feature.

And that "hacking" mentality that is always in my mind, again, twist the very existence of this blog to a complete unpredicted direction. You see, this blog at the very beginning was a humble idea for me as a (controllable) place to talk about the music of one of my most admired musician, Pham Duy. I think, and many if not most of my fellow Vietnamese will agree, that Pham Duy is the most famous and influenced of 20th Vietnamese music. His 86th birthday will be on October 5th. Happy Birthday to you, Bác Phạm Duy, may your music and your legend will live for the next 300 years (300 năm nữa có ai còn khóc Tố Như?) and many more. You can sample his music here.


Back to the new template design. In my opinion, it was such an elegant and far more simpler design than the one I knew and wrote an application at work - XSLT. You can't really compare between the two actually, since XSLT is a generic one, it works for every xml file, while Blogger template is specifically designed for Blogger Beta. The new design is much simpler with far less tags to learn than the old one, and the logic of the code is far easier to understand, I felt I should start tweaking it the way I think it should deserved to be. Being a programmer with the "constructive laziness" mind frame, it's so tempting yet amazed to see new labels magically incarnated as real-time tabs. I still think it's wonderful everyday that labels can be shown as all kinds of tabs and navigation widgets, even though I know exactly how it works.

So you see, from a humble first post with a comment from my vietnamese friend, chị Minh-Thanh từ dactrung.net:



to my first two non-vietnamese friends, Ramani and Vivek:



after a bit more than a month (with the help from Buzz and Blogs of Note) this blog will reach 10000 visitors' milestone shortly with people visiting from all over the world:



specifically:



indeed the blog is reaching the 10000 posts milestone fast:



As you observe, the vietnamese readers are only accounted for 3%, and my site since that first hack geared its contents more and more toward the globally english-based readers. Yesterday, I finally decided to create a separate blog (http://hoctroviet.blogspot.com) to put my vietnamese posts there. I think it's fair for all of us.

To all my readers, thank you so much for your visits. Your insightful comments and constructive opinions are what keep me going.

I find it so much enjoyable every time I visit a blog that implements one of my hacks (so please show me your blog once you complete with the hack.) It's true. I'm not in this for the fame, as I rather go with my nick name hoctro (it means pupil in vietnamese, btw) than my real name. Yet, I care about this blog's professional and technical qualities as the same level as my programs I have been writing at work. I'm in this because I truly believe that Google really wants to bring us closer together by offering us an effective, yet fun and free, communicating tool. I'm here to make that service from Blogger more enjoyable for you. And no, i'm not a Google employee :-). That's it. As long as it's still fun to do, and I can still learn a thing or two, i'm going to hack some more, i think :-)

Have a wonderful first day of October to you all and thank you for reading.

Hoctro

(10/1/2006)

Hacking Technique: On Making a New Hack



Dear Friend,

This weekend I was working on a brand new hack code name "Site Map", that would let you see all of your posts (theoretically up to 999 possible posts) as a list of all labels and their belonging posts. Here is an example of such a list:

...
Art
    *Picasso's Guernica
    *Da Vinci's Mona Lisa
    *Dali: What do we know about his works?
Math
    *How to solve a quadratic equation?
    *Math in College: how tough it is?
    *Math in everyday use.
Soccer
    *The Italians Wins the World Cup for the fourth time!

and so on.

Well, things go really well, and I'm at a good stopping point. What I have at the moment is a list of all the possible posts in a blog, in chronological order. You can click on this link, or look at my beta playground and click on the "Listing of All Posts" to see the effect.

I would not consider this as a hack, but I'd like to make some comments on the code on the technical side of things, in a hope that some of my readers would be interested in this and turn it into completely unexpected new hack(s). So, if you are interested, please read on.


Here is my logic. First, I'm going to get all the posts in a blog. I think Blogger allows to retrieve up to 999 posts on a query.

Now, the return data would be as a part of the Blog in the form of data:posts. This means I cannot create a new widget to retrieve and process the data, but I have to get it from the Blog1 widget itself. To have a clean cut between the new code and the old one, I will sub-out all the code to a function (Blogger calls this an includable), then in Blog1's main includable I will call this function to do the job. Later on I have to do something so the rest of the full (999) posts' contents will not show up and slow down your computer, but for testing purpose, it's fine to see both new and current code.

Here is the call to the new "siteMap" function:

... 

<b:includable id='main' var='top'>
  <div id='blog-posts'>
    <b:include name='siteMap' values='data:posts'/>

...


Below is the current listing of the function. I will explain more in details. (If you want to try this of course you must edit away all the numbers at the front of each line.)


1 :<b:includable id='siteMap' var='data:posts'>
2 :
3 :   <div class='sitemapHack'>
4 :     <div id='siteMapList'>
5 :     </div>
6 :   </div>
7 :<script language='javascript' type='text/javascript'>
8 :
9 :  var currentUrl = "<data:blog.url/>";
10:  if (currentUrl.search("max-results=999") != -1) {
11:  var firstLine = "";
12:  var tempLabel = "";
13:  <b:if cond='data:blog.pageType == "index"'>
14:    <b:if cond='data:blog.url != data:blog.homepageUrl'>
15:<!-- second part : filling in data based on tags -->
16:       firstLine = firstLine + "<ul>";
17:      <b:loop values='data:posts' var='post'>
18:          <b:if cond='data:post.title'>
19:              firstLine = firstLine + "<li><a expr:href='data:post.url'><data:post.title/></a></li>";
20:          </b:if>
21:      </b:loop>
22:      firstLine = firstLine + "</ul>";/*1st line*/
23:    </b:if>
24:  </b:if>
25:     obj = document.getElementById('siteMapList');
26:     obj.innerHTML = firstLine;
27:}
28:</script>
29:</b:includable>


Line 1 declares the function as siteMap, with the passing value is the collection of all the posts.

Line 3 thru 6
contain the placeholder of the hack's contents. We will keep adding text to a variable declared in line 11, and at the end write all data to this div. As mentioned in my previous article entitled "JavaScript, you completed me!", this portion of the code I follow exactly from Ramani of Hackosphere.

Line 7 declares that we are entering Javascript, and line 27 exits this effect. While we are in JavaScript, you cannot write something like <li> and expect the code to pick this up and turn it into real html code. What you have to do is to write all of that HTML code to a string variable, as we shall see shortly.

Line 9 contains the secret of JavaScript/Beta integration. Whatever inside the two double quotes will turn into a string and is assigned to the variable currentURL. A look at the runtime code confirms this fact.




In fact, by reading the generated source code, I was able to find out many bugs, while programming the code in Beta's HTML edit tool.

Line 10 performs a search to see if the text max-results=999 is inside the currentURL variable (if it's different than -1 then it is, because the returning value indicated the location of the first occurence if there is a match.)

Now that it's matched, line 11 & 12 define some blank texts, while line 13 & 14 perform the checks to see if this is the archive/label kind of returned posts. As you might have known, there are three possible kinds of data that data:posts collection represents. If you are in homepage, data:posts represents the contents of all the posts you decided to show in the front page, whether it is one or ten posts. On the other hand, if you select on a label or a month/day/year archived link, data:posts represents all the possible posts resulting from that query. Finally, if you click on a link to the post, data:posts represents the actual post, so there would be exactly one (1) post in the collection.

In Beta templating language, there aren't any AND or OR logic for you to combine the two conditions into one if statement, so I had to make two if statements as shown in line 13 & 14. If the current page is an indexed type AND it's not at the same time the front page, then we hit the gold mine!

Line 16 starts the string text with an ul tag.

Line 17 thru line 21 set up a nice loop where each post's name is tugged inside its link, in order for you to be able to click on it to lead you to the right item post. In line 19 firstLine variable keeps adding more titles onto itself. In the generated code, the result is super lightweight chunk of code, since we only add Blogger to pass back titles and url only. You will also notice that the b:loop logic is executed on the server side, since what we see on the client side (our generated code) is the result of the content of each loop.





Noticed in line 18, the title is drawn only if it exists.

Once the loop is completed, we close the ul tag and the ifs as in line 21 thru 24.

As previously noted, we look for the unique id siteMapList, then write the content to it in line 25 & 26.

Now that the code is completed, to call it we use this link:

http://blogger-beta-from-ground-up.blogspot.com/search/label/?max-results=999

That was all I did for the posts to show. My next step for this little hack project is to look for all the unique labels contained in the posts, then have a for-loop going through all the found labels and prints all all the posts that match it. Because of this, if you have multiple labels for a particular post, it will show up many times, one for each label. That is acceptable, I think. I personally only assign one label per post right now in my main blog, but in the future, if I find a good benefit, I won't hesistate to add more labels.

Wow! I finally did it! I'm at the end of my explanations!

Have a wonderful day and see you next time, when I present you the completed "Site map" hack.

Hoctro (9/30/2006)
Blog: http://hoctro.blogspot.com/

My Blog Has Just Made It to "Blogs Of Note"!


Dear Friend,

I'm happy to tell you that I have just made it to the Blogs of Note. This is quite an honor and unexpected. I feel like I just have got my own star on Hollywood Boulevard!



I would like to take this opportunity to thank my friends (Ramani, Vivek, Hans, Avatar, John (Freshblog), and others) who have been helping one way or another in shaping the contents of my site. You can check out their sites from my "interesting links" on the sidebar.

Ramani is right. My simple, free counter I set up from "Sitemeter" jumped drastically from 100 a day, which is not a small number by any means, to 2400 (!) in just one day! This is incredible!

For my new readers, welcome!!!! Feel free to check out what's on my site, and leave your comments here on what other topics you would like me to write about: more hacks, hack suggestions, more Blogger Beta how-to tutorials, etc. I'm planning to write an in-depth series on the internal working of the new beta template. This will take 4, 5 articles or more. So stay tuned.

Have a wonderful day and check back often at my site and my friends', as we have quite a few interesting Beta hacks to offer.

Hoctro (9/2006)
Blog: http://hoctro.blogspot.com/

Adding Map, Text and Video Google Searches to your Blog





Update: For people whom are looking for three-column templates, I have 8 new Minima templates, using the same CSS code from Blogger

Look for them from this article: Ready-to-use Three-Column Template: Minima Templates

Thanks,






Dear Friend,

Below is my straight-to-the-point instructions on how to install the API Google Search types (video, text, and map) to your blog. The strong point of my fully revised articles is to provide you with more options to change the search parameters without going much inside the template.

Step 1: Get the API key from http://code.google.com/apis/ajaxsearch/signup.html. It's quite simple to get one, really.

Step 2: Add the CSS and code to the header, right after the ] ] ></b:skin> tag.
Replace the text in bold in 2 places with you own API key.

To make your installation/test easier, I upload my template here, so you can download to your Pc, then upload it to your blog in Edit HTML->Upload, and safely skip step 2->4. (But you still have to replace the key to your own.)


<script

src='http://www.google.com/maps?file=api&amp;v=2&amp;key=wW9cday6o7K3g' type='text/javascript'/>
<script

src='http://www.google.com/uds/api?file=uds.js&amp;v=1.0&amp;key=wW9cday6o7K3g' type='text/javascript'/>
<link href='http://www.google.com/uds/css/gsearch.css' rel='stylesheet' type='text/css'/>
<link href='http://www.google.com/uds/css/gsearch_darkgrey.css' rel='stylesheet' type='text/css'/>

<!-- video search solution -->
<script src='http://www.google.com/uds/solutions/videosearch/gsvideosearch.js' type='text/javascript'/>
<link href='http://www.google.com/uds/solutions/videosearch/gsvideosearch.css' rel='stylesheet' type='text/css'/>

<!-- videoBar solution -->
<script src='http://www.google.com/uds/solutions/videobar/gsvideobar.js' type='text/javascript'/>
<link href='http://www.google.com/uds/solutions/videobar/gsvideobar.css' rel='stylesheet' type='text/css'/>

<!-- map search solution -->
<script src='http://www.google.com/uds/solutions/mapsearch/gsmapsearch.js' type='text/javascript'/>
<link href='http://www.google.com/uds/solutions/mapsearch/gsmapsearch.css' rel='stylesheet' type='text/css'/>

<style type='text/css'>
/* primary colors */
.app_gsvsc { color : rgb(153, 170, 221); }
.search-form-complete_gsvsc div.search-form-save_gsvsc { color : rgb(170, 221, 153); }
div.search-form-save_gsvsc { color : #202020; }

/* selected tag */
div.tag-selected_gsvsc { color : rgb(170, 221, 153); }

/* hover colors */
div.more_gsvsc:hover { color : rgb(170, 221, 153); }
div.tag-control_gsvsc:hover { color : rgb(170, 221, 153); }
.search-form-complete_gsvsc div.search-form-save_gsvsc:hover { color : rgb(170, 221, 153); }
.footerBox_gsvsc a:hover { color : rgb(170, 221, 153); }
.playerBox_gsvsc a.title_gsvsc:hover { color : rgb(170, 221, 153); }

/* secondary colors */
div.more_gsvsc { color : rgb(204, 204, 204); }
div.tag-control_gsvsc { color : rgb(204, 204, 204); }
.searchForm_gsvsc { color : rgb(204, 204, 204); }
.search-form-input_gsvsc { color : rgb(204, 204, 204); }
td.edit-form-input_gsvsc input { color : rgb(204, 204, 204); }
div.edit-form-submit-box_gsvsc { color : rgb(204, 204, 204); }
.footerBox_gsvsc a { color : rgb(204, 204, 204); }
.playerBox_gsvsc a.title_gsvsc { color : rgb(204, 204, 204); }
div.edit-tag_gsvsc { color : rgb(204, 204, 204); }

/* special settings, not called out in standard color overrides */
.tiny-results_gsvsc div.tiny-video-result_gsvsc { border-color : #000000; }
.results_gsvsc div.video-result_gsvsc { border-color : #000000; }
.search-form-input_gsvsc {
color : #676767;
background-color : #e0e0e0;
}

/* mapsearch styles */
#mapsc { width : 100%; }
#mapsc .gsmsc-idleMapDiv { height : 260px; }
#mapsc .gsmsc-mapDiv { height : 260px; }
#mapsc .gs-localResult { color : #676767; }

#mapsc a:link,
#mapsc a:visited { color : #0000cc; }

#hotspots {
padding-left : 8px;
}

#hotspots ul.hotspots {
margin : 0px;
margin-bottom : 4px;
padding : 0px;
}

#hotspots ul.hotspots li {
text-indent : 0px;
font:bold 70%/1.4em "Trebuchet MS",Trebuchet,Arial,Verdana,Sans-serif;
text-transform:uppercase;
display : inline;
padding : 0px;
margin-right : 8px;
cursor : pointer;
}

#hotspots ul.hotspots li:hover {
text-decoration : underline;
}

h3.sidebar-subtitle {
margin:.25em 0 .25em;
font:bold 75%/1.4em "Trebuchet MS",Trebuchet,Arial,Verdana,Sans-serif;
text-transform:uppercase;
letter-spacing:.2em;
color:#777;
}

/* center column search stuff */
#searchform { width : 100%; margin-bottom : 8px;}
#searchControl .gs-result .gs-title,
#searchControl .gs-result .gs-title * {
color : rgb(153,170,221);
}

#searchControl .gsc-resultsHeader {
border-bottom-color : rgb(99,99,99);
}

#searchControl .gsc-control {
width : 100%;
}

#searchControl .gsc-tabHeader.gsc-tabhActive {
border-left: 1px #636363;
border-right: 1px solid #636363;
border-top: 2px solid rgb(153,170,221);
background: #e9e9e9;
}

#searchControl .gsc-tabHeader.gsc-tabhInactive {
border-left: 1px solid black;
border-right: 1px solid black;
border-top: 2px solid #636363;
background: black;
}
</style>
  <script type='text/javascript'>
    var mapSearch;
    var videoSearch;
    var coreSearch;
    var videoBar;

    function SolutionLoad() {

      var videoOptions = {
        twoRowMode : true,
        startupDelay : 150
      }
      videoSearch = new GSvideoSearchControl(
        document.getElementById("videosc"),         // container
        defaultTags,                                // default tag array
        null,                                       // optional load function
        null,                                       // optional save function
        videoOptions                                // optional options
        );


      // video bar
      var vbOptions = {
          largeResultSet : true,
          master : videoSearch
          }
      videoBar = new GSvideoBar(document.getElementById("videoBarDiv"), null, vbOptions);
      videoBar.execute("mac vs pc ad");

    // set title to the Googleplex and the link to
    // the Google corporate information page
    // set the hotspot list to the list above
    var mapOptions = {
      title : "Hoctro's Hangout Place",
      url : "http://en.wikipedia.org/wiki/Huntington_Beach,_California",
      hotspots : hotspotsList
      };

    // create the map search control
    mapSearch = new GSmapSearchControl(
          document.getElementById("mapsc"),
          "Huntington Beach, CA",
          mapOptions
          );


    var controlRoot = document.getElementById("searchControl");

    // create the search control
    coreSearch = new GSearchControl();
    coreSearch.setLinkTarget(GSearch.LINK_TARGET_SELF);
    coreSearch.setResultSetSize(GSearch.LARGE_RESULTSET);

    // prep for decoupled search form
    var searchFormElement = document.getElementById("searchform");
    var drawOptions = new GdrawOptions();
    drawOptions.setSearchFormRoot(searchFormElement);
    drawOptions.setDrawMode(GSearchControl.DRAW_MODE_TABBED);

    // populate - web, this blog, all blogs
    var searcher = new GwebSearch();
    searcher.setUserDefinedLabel("The Web");
    coreSearch.addSearcher(searcher);

    searcher = new GblogSearch();
    searcher.setUserDefinedLabel("Blogsphere");
    coreSearch.addSearcher(searcher);

    searcher = new GblogSearch();
    searcher.setSiteRestriction("http://googleajaxsearchapi.blogspot.com/");
    searcher.setUserDefinedLabel("Ajax Search Blog");
    coreSearch.addSearcher(searcher);

    searcher = new GblogSearch();
    searcher.setSiteRestriction("http://googleblog.blogspot.com/");
    searcher.setUserDefinedLabel("Google Blog");
    coreSearch.addSearcher(searcher);

    searcher = new GwebSearch();
    searcher.setSiteRestriction("http://www.blogger.com/");
    searcher.setUserDefinedLabel("Blogger");
    coreSearch.addSearcher(searcher);

    coreSearch.draw(controlRoot, drawOptions);

    }

    function doCoreSearch(q) {
      coreSearch.execute(q);
    }

    function doMapSearch(q) {
      mapSearch.newSearch(q);
    }

    function doVideoSearch(q) {
      videoSearch.vsf.input.value = q;
      videoSearch.searchByString(videoSearch.vsf);
    }

    registerLoadHandler(SolutionLoad);

    function registerLoadHandler(handler) {
      var node = window;
      if (node.addEventListener) {
        node.addEventListener("load", handler, false);
      } else if (node.attachEvent) {
        node.attachEvent("onload", handler);
      } else {
        node['onload'] = handler;
      }
      return true;
    }
  </script>



Step 3: Add these new 5 widgets to your blog, following instructions here, section B.4.


<b:widget id='HTML31' locked='false' title='Search Bar' type='HTML'>
<b:includable id='main'>
  <b:if cond='data:title != ""'>
    <h2 class='title'><data:title/></h2>
  </b:if>
  <div class='widget-content'>
    <div id='searchform'>Loading...</div>
  </div>
  <b:include name='quickedit'/>
</b:includable>
</b:widget>
<b:widget id='HTML32' locked='false' title='Video Bar' type='HTML'>
<b:includable id='main'>
  <b:if cond='data:title != ""'>
    <h2 class='title'><data:title/></h2>
  </b:if>
  <div class='widget-content'>
    <div id='videoBarDiv'>Loading...</div>
  </div>
  <b:include name='quickedit'/>
</b:includable>
</b:widget>
<b:widget id='HTML33' locked='false' title='Search Results' type='HTML'>
<b:includable id='main'>
  <b:if cond='data:title != ""'>
    <h2 class='title'><data:title/></h2>
  </b:if>
  <div class='widget-content'>
    <div id='searchControl'>Loading...</div>
  </div>
  <b:include name='quickedit'/>
</b:includable>
</b:widget>
<b:widget id='HTML34' locked='false' title='Map Search' type='HTML'>
<b:includable id='main'>
  <b:if cond='data:title != ""'>
    <h2 class='title'><data:title/></h2>
  </b:if>
  <div class='widget-content'>
    <div id='mapsc'>Loading...</div>
    <data:content/>
  </div>

  <b:include name='quickedit'/>
</b:includable>
</b:widget>
<b:widget id='HTML35' locked='false' title='Google Video Search' type='HTML'>
<b:includable id='main'>
  <b:if cond='data:title != ""'>
    <h2 class='title'><data:title/></h2>
  </b:if>
  <div class='widget-content'>
    <div id='videosc'>Loading...</div>
    <data:content/>
  </div>

  <b:include name='quickedit'/>
</b:includable>
</b:widget>


Step 4: Move around the widgets in Page Elements so that it looks like this. This is how we have a little freedom in moving the search widgets around if need be.



Notice sometimes you'll see this prompt, don't worry, just click OK.



To enhance our flexibilities in changing search items for the map and the video bars, two more steps are required.

Step 5: Adding contents to the Map Widget: cut and paste this code to the content of the Map Search widget:


<font color="brown">
<h3 class="sidebar-subtitle">A few great places</h3>
    <div id="hotspots">
      <ul class="hotspots">
          <li id="hs01">Sushi</li>
          <li id="hs02">Pho</li>
          <li id="hs03">Book store</li>
          <li id="hs04">Italian Food</li>
          <li id="hs05">Jeannine's</li>
          <li id="hs06">Cava</li>
          <li id="hs07">SanYsidroRanch</li>
          <li id="hs08">Sunstone</li>
          <li id="hs09">Melville</li>
          <li id="hs10">PhysicalFocus</li>
      </ul>
    </div>
    <script type="text/javascript">
      var hotspotsList = [
        { element : document.getElementById("hs01"), query : "Sushi" },
        { element : document.getElementById("hs02"), query : "Pho" },
        { element : document.getElementById("hs03"), query : "Book Stores" },
        { element : document.getElementById("hs04"), query : "Italian Food" },
        { element : document.getElementById("hs05"), query : "Jeannine's" },
        { element : document.getElementById("hs06"), query : "Cava Restaurant & Bar" },
        { element : document.getElementById("hs07"), query : "San Ysidro Ranch" },
        { element : document.getElementById("hs08"), query : "Sunstone Winery" },
        { element : document.getElementById("hs09"), query : "Melville Winery" },
        { element : document.getElementById("hs10"), query : "Physical Focus" }
        ];
    </script>
</font>




Step 6: Adding contents to the Video Widget: cut and paste this code to the content of the Video Search widget:


<font color="brown">
<script type="text/javascript">
  var defaultTags = [
    {query : "feed:top100",label : "itshot"},
    { query : "feed:top100new",label : "itsnew"},
    { query : "monkey boy ballmer", label : "steveb"},
    {query : "mac pc ads",label : "mac"},
    {query : "mtv"},
    { query : "big wave surfing",label : "waves"},
    {query : "christina aguilera type:music_video",label : "christina"},
    { query : "jessica simpson",label : "jessica"},
    {query : "vw gti"},
    {query : "ferrari f1",label : "f1"},
    { query : "abba",label : "abba"},
    { query : "type:gpick",label : "picks"
    }
  ];
</script>
</font>




That's it! We're done!

Here's my test site with the new search widgets: http://hoctro-stretchminima.blogspot.com/



To add new locations, go to step 5 and 6 and change at will. I'll let you exercise your brain on how to add a new line item to the search.

Discussion:

- This map uses a central location to look for the searches. For example, I use the city of Huntington Beach, California as my center point, then clicking on Starbuck link, I was able to locate all the coffee shops around the city.


    // create the map search control
    mapSearch = new GSmapSearchControl(
          document.getElementById("mapsc"),
          "Huntington Beach, CA",
          mapOptions
          );

-Under the main map, you will see the forward or backward buttons. Clicking on these will give you all the locations near the blog's central point.



- The Core Search is also really cool, in that you can do many kind of searches from many different servers, the results will be shown in tabs in front of your main content (that is where I put in the div in step 3.) Make sure you change the CSS code to make the tabs look more decent.



I would like to thank Mr. Mark Lucovsky for putting together these hacks and making all of the above code available for us bloggers to use.

Cheers,

Hoctro
Blog: http://hoctro.blogspot.com/




Update:

My article is mentioned in Buzz today (9/26/06 11:30 AM Pacific time). Thanks, Eric and the Beta Blogger team. Keep up your great work.





Update 2 (11/24/06):


My blog is mentioned in the API Search Blog. Thank you very much!

Hacking Technique: Feeds on Front Page Only




Dear Friend,

It seems to me that the Blogger Beta team fixed the problems with comment feeds already. I tested it and it works fine. I noticed something really cool and logical too. Instead of using the blog ID (do you know off the top of your hat your site ID? I don't,) they utilize your homepage address now. So, for now my feeds are, in case anyone cares to know:

  • posts: http://hoctro.blogspot.com/feeds/posts/full

  • Comments: http://hoctro.blogspot.com/feeds/comments/full


Now, here's how I let these feeds shown (by using 2 feed widgets) on my front page only, and not my label/archived/item pages. A very simple but useful trick is employed: put an "if" check to see if the current page is the homepage or not, if so then proceed, otherwise nothing got shown.

Add two lines right inside the main includable:

* Right after the line: <b:includable id='main'>
add this line: <b:if cond='data:blog.url == data:blog.homepageUrl'>
* Right before the line: </b:includable>
add this line: </b:if>

Here is the example I put on the posts feed:


<b:widget id='Feed1' locked='false' title='Latest Posts' type='Feed'>
<b:includable id='main'>
  <b:if cond='data:blog.url == data:blog.homepageUrl'>
    <h2><data:title/></h2>
    <div class='widget-content'>
    <ul id='feedItemListDisplay'>
      <b:loop values='data:feedData.items' var='i'>
        <li>
          <span class='item-title'>
            <a expr:href='data:i.alternate.href'>
              <data:i.title/>
            </a>
          </span>
          <b:if cond='data:showItemDate'>
            <b:if cond='data:i.str_published != ""'>
              <span class='item-date'>
                 - <data:i.str_published/>
              </span>
            </b:if>
          </b:if>
          <b:if cond='data:showItemAuthor'>
            <b:if cond='data:i.author != ""'>
              <span class='item-author'>
                 - <data:i.author/>
              </span>
            </b:if>
          </b:if>
        </li>
      </b:loop>
    </ul>
    <b:include name='quickedit'/>
    </div>
    </b:if>
  </b:includable>
</b:widget>


For that purpose, any widget you want to show only on the front page you can use this trick.

Have a nice day,

Hoctro (9/2006)
Blog: http://hoctro.blogspot.com/




PS: While we're on the subject, if we don't want to see something in the front page, then just change the equal (==) check to become NOT equal (!=),

<b:if cond='data:blog.url != data:blog.homepageUrl'>



Hacking Technique: Navigation Tabs




Dear Friend,

Continuing on my quest of maximizing the usage of Beta's new Label feature, this time I will show you how to turn Labels into Navigation Tabs.

The Yahoo! Design Pattern Library identifies the Navigation Tabs pattern as "the" solution when the site has only some few main categories with their titles shorts and predictable, etc. A prime, everyday-use example for us, as you might have guessed, is no other than Blogger's (classic and beta) navigation tabs!



Based on an online example from that pattern's description, I was succeeded in turning our labels into similar navigation tabs:



As you can see, the prominent advantage of this tab over my first "horizontal tab" hack is the clear indication of what the user is seeing, with the active tab in light grey color, and the sub-label in bold.

Following are the steps on how to convert:


Step 1: Adding CSS code to the end of the template's CSS portion




/* base styles for extending/overriding */

/* primary tabs */
.navset {border-bottom:1px solid #999;}
.navset .hd li em {font-weight:bold;}
.navset .hd li a {color:#666;}
.navset .hd li.on strong, .navset .hd li.on strong a {color:#999;} /* selected tab */
.navset .hd li.on strong {background-color:#F2F3F5;border-bottom:1px solid #F2F3F5;} /* border-color should match selected color */
.navset .hd li.orphan, .navset .hd li.orphan a {color:#999;}

/* bg images, defaults to #999 border-color on white bg */
.navset .hd li a, .navset .hd li strong {background:#ccc url(http://us.i1.yimg.com/us.yimg.com/i/us/nt/el/tb/tr_999.gif) no-repeat top right;}
.navset .hd li em {background:transparent url(http://us.i1.yimg.com/us.yimg.com/i/us/nt/el/tb/tl_999.gif) no-repeat;}

/* secondary tabs */
.navset .bd ul {background-color:#F2F3F5;border-color:#999;}
.navset .bd li, .navset .bd li a {color:#06c;}
.navset li.on, .navset li.on strong {font-weight:bold;}
.navset .bd li {border-color:#ccc;} /* pipe divider */

/* end base styles */

/* Network tab standards, shouldn't change */

/* shared pri and sec */
.navset {width:100%;} /* IE: width */
.navset a {text-decoration:none;}
.navset ul, .navset li {margin:0;padding:0;list-style:none;}
.navset li {float:left;display:inline;}
.navset li a:hover {text-decoration:underline;}
.navset ul:after {clear:both;content:'.';display:block;height:0;visibility:hidden;} /* clear non-IE */
.navset ul {zoom:1;} /* clear IE */

/* primary tabs */
.navset .hd ul {font:bold 78%/1.2em verdana;margin-bottom:-1px;padding-left:.3em;position:relative;} /* IE quirks mode: relative */
.navset .hd li {margin-right:.33em;padding:0;}
.navset .hd li.on strong a {cursor:default;}
.navset .hd li a, .navset .hd li strong, .navset .hd li em {display:block;}
.navset .hd li a, .navset .hd li strong {*display:inline-block;} /* IE: 100% clickable */
.navset .hd li em {font-style:normal;padding:.5em .6em;}
.navset .hd li.orphan, .navset .hd li.orphan a, .navset .hd li.orphan em {background:transparent none;border-width:0;margin:0;}

/* secondary tabs */
.navset .bd ul {border-top-width:1px;border-top-style:solid;font:78%/1.2em verdana;margin:0;padding:.6em 0 .6em .4em;}
.navset .bd li {border-left-style:solid;border-left-width:1px;display:inline;padding:0 1em;}
.navset .bd li.first {padding-left:0;border:0;}


Step 2: Add the Label Widget to the content (blog) area

First, make sure "Expand Widget Templates" is checked. Then, locate the widget Blog1, and cut-n-paste this code (seen below) right in front of it.




<b:widget id='Label2' locked='false' title='Labels' type='Label'>
<b:includable id='main'>
   <div class='menuHack'>
     <div id='menuList'>
     </div>
   </div>
   
   <script language='javascript' type='text/javascript'>
     var label = &quot;&quot;;
     var text2 = &quot;&quot;;
     var currentLabel = &quot;&quot;;
     var activeLabel = &quot;&quot;;
     var firstLine = &quot;&quot;;
     var secondLine = &quot;&quot;;
     var drawTop = false;
     var firstSub = false;
     var currentUrl = &quot;<data:blog.url/>&quot;;
     var arr = new Array(2);

     firstLine = firstLine + &quot;<div class='navset' id='nav'>&quot;;
     firstLine = firstLine + &quot;<div class='hd'><ul>&quot;;
      <b:if cond='data:blog.url == data:blog.homepageUrl'>
firstLine = firstLine + &quot;<li class='on' title='selected'><a expr:href='data:blog.homepageUrl'><strong><em>Home</em></strong></a></li>&quot;;
     <b:else/>
firstLine = firstLine + &quot;<li><a expr:href='data:blog.homepageUrl'><em>Home</em></a></li>&quot;;
     </b:if>
     secondLine = secondLine + &quot;<div class='bd'><ul>&quot;;
     <b:loop values='data:labels' var='label'>
       label = &quot;<data:label.name/>&quot;;
       text2 = label.charAt(label.length - 1);
       if (text2 != '.') {
          arr = label.split(&quot;*&quot;,2);
          if (arr[0] != currentLabel) {
            currentLabel = arr[0];
            /*var arr2 = new Array(2);
            arr2 = arr[0].split(&quot;&quot;,2);*/
            if (currentUrl.search(arr[0]) != -1) {
              activeLabel = label;
              if (drawTop == false) drawTop = true;
              firstLine = firstLine + &quot;<li class='on' title='selected'><a expr:href='data:label.url + "?max-results=100"'><strong><em>&quot; + arr[0].replace(/_/g,' ') + &quot;</em></strong></a></li>&quot;;

          }
          else
              firstLine = firstLine + &quot;<li><a expr:href='data:label.url + "?max-results=100"'><em>&quot; + arr[0].replace(/_/g,' ') + &quot;</em></a></li>&quot;;
          }
         if (arr[1] != undefined &amp;&amp; currentUrl.search(arr[0]) != -1) {
         var secClass = &quot;&quot;;
         if (firstSub == false) {
           firstSub = true;
           secClass = &quot;class='first'&quot;;
         }
         if (currentUrl.search(arr[1]) != -1) 
           secClass = &quot;class='on'&quot;;
           secondLine = secondLine + &quot;&lt;li &quot;  + secClass + &quot;&gt;<a expr:href='data:label.url + "?max-results=100"'>&quot; + arr[1].replace(/_/g,' ') + &quot;</a>&lt;/li&gt;&quot;;
}
}
     </b:loop>
     firstLine = firstLine + &quot;</ul></div>&quot;;/*1st line*/
     secondLine = secondLine + &quot;</ul></div>&quot;;/*2nd line*/
  
     arr = activeLabel.split(&quot;*&quot;,2);
     if (arr[1] != undefined &amp;&amp; currentUrl.search(arr[0]) != -1)
       firstLine = firstLine + secondLine;

     firstLine = firstLine + &quot;</div>&quot;;/*navset*/
     obj = document.getElementById('menuList');
     obj.innerHTML = firstLine;
      </script>
</b:includable>
</b:widget>


Step 3: Label/Re-label your posts

To make this hack works, you need to re-label your posts. There are three rules:

1. If you don't want to see a label or sub-label, put a period (".") at the end.

2. Labels/Sub-labels are separated by a star ("*"). For example, if you like to have a common tab named "Art", with sub-menu such as "Paintings", "Architectural", "Decor", etc., then you should have posts with labels sucha as Art*Paintings, Art*Architectural, Art*Decor, etc. The key is to keep the tab name the same ("Art".)

3. Words are separated by underlines ("_"), this step must be followed so that the current selected label could be shown in bold.
For example, this is a valid tag: "Modern_Art*Pablo_Picasso", and not this "Modern Art*Pablo Picasso".


Discussion:

-By defaults, labels are sorted alphabetically. Don't change this default (using widget visual editing.)

-Even though underscores are in your labels, the main or sub labels won't show them but replace with spaces instead.

-As always, create a new blog to test this hack before applying it to your main blog. Don't forget to make a back-up of your main blog's template regularly.

- I have my test blog here for you to reference on how to name labels the correct way in order for the labels to show as tabs:

http://blogger-beta-from-ground-up.blogspot.com/

-If you can follow my logic in Javascript, you will see that I try my best in making the hack a "one-loop" algorithm. Originally, I had 2 loops, one for the upper tabs, and one for the sub-labels. Eventually, I realize I could use two strings for two divs, and only append the second string if the sub-labels present. I also learned quite a few JavaScript tricks from this one, such as one to replace underscore with a space, searching a string inside another, etc. Overall, it's a noteworthy hack to look at and make improvements upon.

That's it. I hope you will find this hack useful.

Have a wonderful day,

Hoctro (9/24/2006)
Blog: http://hoctro.blogspot.com/