Friday, May 29, 2009

Creating deb packages manually/Using alien to convert RPMs

Problem:

Although you can create debian packages using an automated installer at compile time using checkinstall at compile time. In this example, we will be using precompiled binary files that need to be ported from RPM packages to deb packages to port a program to a debian based system

Background:

I work for a Managed Service Provider, and we use a product called N-Able to do monitoring for our clients. We monitor servers on all platforms (Windows, Linux and Mac) and accordingly have to be able to install software for this on each operating system. The only issue was, for Linux, there was no Debian agent, (only red hat) so I decided to port it to Debian systems.

Solution:

In order to do this, you will need to have 2 tools installed: 1) alien and 2) ar. I used an Ubuntu 9.04 system for this process, though it should work on any system

Step 1: Convert the RPM to deb

to do this, simply type "alien packagename" You should now have a deb file with the same name as the origional (save for the extension)

Step 2: Extract the data from the deb archive

In this example, most of the files were converted and no additional work was necessary, however 1 of the packages required additional modifications, as the startup scripts it used were written for the red hat style startup/shutdown system using chkconfig, etc. This is very incompatible with Debian based systems. To correct this, I had to extract the deb package, so that I could modify these packages.


Start by creating a directory for each file and copying the deb into the folder:

mkdir nagent
cp nagent-rhel5.1_6.7.0.2169-3_i386.deb ./nagent


Then move into the directory and extract the data from the deb package:

cd nagent
ar x nagent-rhel5.1_6.7.0.2169-3_i386.deb


You should now have 3 additional files: control.tar.gz data.tar.gz and debian-binary
debian-binary can be ignored, as it only has a version # used by ar. This leaves us with data.tar.gz and control.tar.gz. data.tar.gz is all of the binary and configuration files needed by the program. If you extract this file, using tar, you will note that it mirrors the file structure of your base system. This is because dpkg just extracts this file with a working directory of root. In other words, if you have a file in ~/nagent/var, when you run dpkg nagent-rhel5.1_6.7.0.2169-3_i386.deb, after it has been repackaged, whatever files in ~/nagent/var will be extracted to /var on your system. I would reccomend creating a directory in your working directory, called data and extracting the data.tar.gz file in there. For the purposes of my project, I needed to update /etc/init.d/n-agent so that it worked correctly on a Debian system. I will not go into detail on bash scripting here, as that is well beyond the scope of this blog post. You can get some help by checking out /etc/init.d/skeleton on a debian system to get an idea of what your startup script might look like, or any of the other startup scripts in the /etc/init.d directory. After you are done making changes, simply re-compress the files to data.tar.gz and overwrite the one in your nagent (or comparable) directory. Next, comes the control.tar.gz file. In this archive are 6 files. conffiles contains a listing of configuration files. If removed using the dpkg --remove command, config files will be left behind (to remove said files, --purge wold be used.) control contains package information such as a description and dependancies that dpkg should check for before installing. alien should have already updated this for you. The md5sums file should be self explanatory and already taken care of for you by alien as well. The remaining files are used to configure non-static elements of you system at install/removal time. The preinst is a BASH script which runs before the installation of the debain package. Likewise postinst is run after installation. Finally prerm is used before removal. For my purposes, I only needed to modify postinst to add a user used by my program. This was because red hat and debian use different programs to add users.

Step 3: Repackageing your deb file

After updating and rearchiving you data.tar.gz and control.tar.gz directories, and cleaning up any work files/directories you used by deleting them, you must then repackage you deb file. This can be done with the command:

ar cr nagent-rhel5.1_6.7.0.2169-3_i386.deb


You can now run dpkg -i filename to verify that the changes you made worked correctly.

This should be everything you need to port a RPM package to a deb package. Good Luck!

Thursday, May 28, 2009

Dual Head Display

While many mistakenly call Linux servers with dual monitors using xinetrama or twin view "dual head," few know that dual head indicates a setup different than dual monitors. A dual head system has two monitors running two separate window managers or even two separate x sessions.

Problem:
I have two monitors at work, and while I am a Mac and Linux Solutions Engineer with Staples Technology Solutions, Our ticketing system is based on the .net framework, which means that I have to run windows somehow in order to enter tickets. While we can use a terminal server for this, there are other things which are also windows only which I sometimes need to test, and so I really can't do without running 3 OSs daily. I have a Mac laptop, so that takes care of one, but I only have 1 desktop and need two more OSs. Enter VirtualBox (virtualbox.org). With VirtualBox, I am able to run a windows VM on my Linux installation (or vice versa if I wanted). Now, rather than trying to set up two seperate windows managers to run and then run virtualbox on top of one of them, I figured I would save CPU cycles by running KDE on display :0.0 and starting virtualbox directly on display :0.1 without a window manager. I also wanted this because I LOVE compiz--Especially the cube with edge flip. But, I didn't want windows disappearing when I rotated the cube (which would have happened with twinview or xinetrama), so a dual head setup seemed like the perfect solution. Getting this setup however, proved to be harder than one might think...

Solution:
First, the computer I was using was a bit old, and as a result I had a bit of trouble initially when I was trying to use an old Radeon X600 card. Supposedly it was supported by the latest ATI driver, but no such luck. I never could get it to work with the proprietary driver and since the free (as in speech) driver did not support doing dual monitors through the single DVI port (with splitter) I had problems. I work around it by robbing an NV285 card out of one of the other workstations we had lying around and I was in business. Sort of. I didn't have too much trouble configuring my xorg file using the nvidia-xconfig tool. I had to then go into my xorg.conf file and disable twinview (and xinetrama) and I was good to go. Sort of. I fired up KDE and got KDE on one monitor and a blank monitor on next to it. I could move my mouse between them. Excellent. Now to set my startup options. In case you are looking for where to set programs to run at start up in KDE, creating an executable BASH script in ~/.kde/Autostart/ will cause this bash script to be executed at login. So, I wrote this little diddy and I was all set:

compiz &
DISPLAY=":0.1" VBoxManage startvm 'Windows XP' -type gui
Well, sort of all set. Now I noticed that I was getting compiz started on both monitors, even though I did not have a WM on :0.1. With a little research, I figured out that if I edited /usr/bin/compiz edited the COMPIZ_OPTIONS variable to read "COMPIZ_OPTIONS=--replace --ignore-desktop-hints --only-current-screen", it would start on just display :0.0. You can also run "compiz.real --replace --ignore-desktop-hints --only-current-screen &" in your Autostart BASH script. Be careful because compiz may autostart on Ubuntu Jaunty 9.04, so if you start it again from an Autostart script, theings are going to get confusing. I had this problem at first. I forget how I would up stopping KDE from starting compiz at startup. I found the post on the ubuntu forms, but it's not really important. What is important is that this was the absolute wrong thing to do. After doing this, I could not type on :0.1. I could move my mouse over there and select windows, but no keyboard and compiz effects were appalingly slow. So I hit the ubuntu bug tracker and came across bug https://bugs.launchpad.net/ubuntu/+source/compiz/+bug/173517 and bug https://bugs.launchpad.net/ubuntu/+source/desktop-effects/+bug/209229. But how to configure :0.1 to stop that annoying cube behavior? I tried selecting screen 1 in ccsm (Compiz Config Settings Manager) but this just configured compiz on display :0.0. Fortunately, I came across a forum post with a user with the same problem. He was able to resolve his issue by starting ccsm on display :0.1 and then configuring. So I tried to start ccsm on display :0.1. For the record, if you need to start any program on a different display, simply prepend "DISPLAY=":X.X" to your command. I fired up my terminal and ran "DISPLAY=":0.1"ccsm" and it hung trying to load the icons for the program becuase there was no window manager on that monitor to manage the icons. Super annoying. Finally, I edited my /etc/kde4/kdm/kdmrc file and switched which display kdm started on by changing the "StaticServers=:0" to read "StaticServers=:0.1" and restarting kdm (sudo killall kdm followed by sudo kdm). Now, if you do this, be sure to disable compiz on display :0.0 and then calling compiz.real directly using the command "DISPLAY=:0.1 compiz.real --replace --ignore-desktop-hints --only-current-screen &" otherwise, you may mess things up. After this I was able to start ccsm and configure this display so that my cube only had 1 side and no loger rotated (it was appearing as a two sided cube) For some reason some of the options were global (such as edge flip) while others were localized (like the number of sides on the cube). Whatever. I got what I needed. Anyway, after setting everything back so that it displayed correctly and windows started at login, I had what I wanted. And with both monitors running compiz effects (even though :0.1 didn't use them) I was now getting the performance I wanted out of my video card. So if you are having trouble with compiz performance with your nvidia NV285 card (or other card) and are running compiz on only one screen, try running it on both screens.

Ahhh. Much better.

Here is my xorg.conf file in case you are having trouble configuring dual head:
# nvidia-settings: X configuration file generated by nvidia-settings
# nvidia-settings: version 1.0 (buildd@palmer) Sun Feb 1 20:21:04 UTC 2009

# xorg.conf (X.Org X Window System server configuration file)
#
# This file was generated by dexconf, the Debian X Configuration tool, using
# values from the debconf database.
#
# Edit this file with caution, and see the xorg.conf manual page.
# (Type "man xorg.conf" at the shell prompt.)
#
# This file is automatically updated on xserver-xorg package upgrades *only*
# if it has not been modified since the last upgrade of the xserver-xorg
# package.
#
# Note that some configuration settings that could be done previously
# in this file, now are automatically configured by the server and settings
# here are ignored.
#
# If you have edited this file but would like it to be automatically updated
# again, run the following command:
# sudo dpkg-reconfigure -phigh xserver-xorg

Section "ServerLayout"
Identifier "Default Layout"
Screen 0 "Screen0" 0 1024
Screen 1 "Screen1" Above "Screen0"
InputDevice "Keyboard0" "CoreKeyboard"
InputDevice "Mouse0" "CorePointer"
Option "AllowEmptyInput" "On"
EndSection

Section "Module"
Load "dbe"
Load "extmod"
# Load "type1"
# Load "freetype"
Load "glx"
EndSection

Section "ServerFlags"
Option "Xinerama" "0"
EndSection

Section "InputDevice"
# generated from default
Identifier "Keyboard0"
Driver "keyboard"
EndSection

Section "InputDevice"
# generated from default
Identifier "Mouse0"
Driver "mouse"
Option "Protocol" "auto"
Option "Device" "/dev/psaux"
Option "Emulate3Buttons" "no"
Option "ZAxisMapping" "4 5"
EndSection

Section "Monitor"
Identifier "Configured Monitor"
EndSection

Section "Monitor"
Identifier "Monitor0"
VendorName "Unknown"
ModelName "DELL 1704FPT"
HorizSync 30.0 - 81.0
VertRefresh 56.0 - 76.0
EndSection

Section "Monitor"
Identifier "Monitor1"
VendorName "Unknown"
ModelName "DELL 1704FPT"
HorizSync 30.0 - 81.0
VertRefresh 56.0 - 76.0
EndSection

#Section "Device"
# Identifier "Configured Video Device"
# Driver "nvidia"
#EndSection

Section "Device"
Identifier "Device0"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BoardName "Quadro NVS 280 PCI-E"
BusID "PCI:1:0:0"
Screen 0
# Force one level when AC power
Option "RegistryDwords" "PowerMizerLevelAC=0x3"
#Enable overclocking
Option "Coolbits" "1"

Option "RenderAccel" "True"
Option "TripleBuffer" "True"
Option "DamageEvents" "True"
Option "NoDRI"
EndSection

Section "Device"
Identifier "Device1"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BoardName "Quadro NVS 280 PCI-E"
BusID "PCI:1:0:0"
Screen 1
# Force one level when AC power
Option "RegistryDwords" "PowerMizerLevelAC=0x3"

#Enable overclocking
Option "Coolbits" "1"
Option "RenderAccel" "True"
Option "TripleBuffer" "True"
Option "DamageEvents" "True"
Option "NoDRI"
EndSection

Section "Screen"
Identifier "Default Screen"
Device "Configured Video Device"
Monitor "Configured Monitor"
DefaultDepth 24
EndSection

Section "Screen"
Identifier "Screen0"
Device "Device0"
Monitor "Monitor0"
DefaultDepth 24
Option "TwinView" "0"
Option "TwinViewXineramaInfoOrder" "CRT-0"
Option "metamodes" "CRT-0: nvidia-auto-select +0+0"
SubSection "Display"
Depth 24
EndSubSection
EndSection

Section "Screen"
Identifier "Screen1"
Device "Device1"
Monitor "Monitor1"
DefaultDepth 24
Option "TwinView" "0"
Option "metamodes" "CRT-1: nvidia-auto-select +0+0"
SubSection "Display"
Depth 24
EndSubSection
EndSection

Section "Extensions"
Option "Composite" "Enable"
EndSection

Wednesday, May 27, 2009

Displaying only posts with a certain tag on your blogger homepage

Problem:

Part of why I am creating this blog is so that if/when I am looking for a new job, those who are potentially Googling me will be able to see projects that I have worked on or am working on in order to impress prospective employers (In addition to wanting to potentially help others and also so that I may reference work I did in the past if needed). On the other hand, there may be things such as religion, philosophy or politics that I wish to blog about which might detract or prevent me from getting hired (Even in spite of non-discrimination laws. When I was in sales I learned [via my sales figures] that things can subconsciously affect our decisions, which is why I always dressed nice) As such, I wanted to be able to have the front page of my blog only display articles which were tagged with "Computers" while being able to hide hide other posts when the time comes for me to get a job.

Solution:

I was miffed on how to do this for at first. I tried deleting (manually) the main pane for posts on the front page and replacing this with the URL to page which searches for posts by tag, however, this just caused problems with the main page. I was at a loss until, after a bit of research I found the following blog post: http://blog.mobocracy.net/2007/06/filtering-blogger-by-label.html

Now, in case this link becomes defunct, here is the gist of how to properly apply this fix:

"From your blog dashboard, click on "Layout" then click on "Edit HTML". Once you have done that, click the "Expand Widget Templates" checkbox. Now you're ready to edit. As always, I recommend making a backup before you get started. First, search for the line that starts with . You are going to remove everything in that function between and the closing tag with the following:


< b:if cond="'data:blog.url">
< b:if cond="'data:post.labels'">
< b:loop values="'data:post.labels'" var="'label'">
< b:if cond="'data:label.name">
< b:include data="'post'" name="'printPosts'/">
< /b:if>
< /b:loop>
< /b:if>
< b:else/>
< b:include data="'post'" name="'printPosts'/">
< /b:if>


The above change essentially says the following: if you are on the homepage, and a post has labels, and the post has the "main" label, then call printPosts. Or, if you are not on the homepage, call printPosts. Now search for the close tag of the main function (it looks like </b:includable>) and after it paste the following code:

< b:includable id="'printPosts'" var="'post'">
< b:if cond="'data:post.dateHeader'">
< h2 class="'date-header'">< data:post.dateheader/>< /h2>
< /b:if>

< b:include data="'post'" name="'post'/">

< b:if cond="'data:blog.pageType">
< b:include data="'post'" name="'comments'/">
< /b:if>
< /b:includable>

The above code is your printPosts function/method. It is identical to what you had before, it was just turned into a function to reduce code duplication. That's it. Now only posts with a "main" label will show up on the front page. For Peter Sachs, who was wanting to filter out content, just change the (== "main") markup to something like (!= "nsfw"). Enjoy."

Note: don't forget the "Expand widget templates" checkbox or else you will not be able to find the sections referenced.

It took me a bit of time to figure out where the code was supposed to go exactly and how it was to be formatted, so I am including a complete working example taken from html code for this blog below:


< ?xml version="1.0" encoding="UTF-8" ?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
< html dir="'data:blog.languageDirection'" xmlns="'http://www.w3.org/1999/xhtml'" b="'http://www.google.com/2005/gml/b'" data="'http://www.google.com/2005/gml/data'" expr="'http://www.google.com/2005/gml/expr'">
< head>
< b:include data="'blog'" name="'all-head-content'/">
< title>< data:blog.pagetitle/>< /title>
< b:skin>< ![CDATA[/*
-----------------------------------------------
Blogger Template Style
Name: Minima
Designer: Douglas Bowman
URL: www.stopdesign.com
Date: 26 Feb 2004
Updated by: Blogger Team
----------------------------------------------- */

/* Variable definitions
====================
< Variable name="bgcolor" description="Page Background Color"
type="color" default="#fff" value="#ffffff">
< Variable name="textcolor" description="Text Color"
type="color" default="#333" value="#333333">
< Variable name="linkcolor" description="Link Color"
type="color" default="#58a" value="#5588aa">
< Variable name="pagetitlecolor" description="Blog Title Color"
type="color" default="#666" value="#666666">
< Variable name="descriptioncolor" description="Blog Description Color"
type="color" default="#999" value="#999999">
< Variable name="titlecolor" description="Post Title Color"
type="color" default="#c60" value="#cc6600">
< Variable name="bordercolor" description="Border Color"
type="color" default="#ccc" value="#cccccc">
< Variable name="sidebarcolor" description="Sidebar Title Color"
type="color" default="#999" value="#999999">
< Variable name="sidebartextcolor" description="Sidebar Text Color"
type="color" default="#666" value="#666666">
< Variable name="visitedlinkcolor" description="Visited Link Color"
type="color" default="#999" value="#999999">
< Variable name="bodyfont" description="Text Font"
type="font" default="normal normal 100% Georgia, Serif" value="normal normal 100% Georgia, Serif">
< Variable name="headerfont" description="Sidebar Title Font"
type="font"
default="normal normal 78% 'Trebuchet MS',Trebuchet,Arial,Verdana,Sans-serif" value="normal normal 78% 'Trebuchet MS',Trebuchet,Arial,Verdana,Sans-serif">
< Variable name="pagetitlefont" description="Blog Title Font"
type="font"
default="normal normal 200% Georgia, Serif" value="normal normal 200% Georgia, Serif">
< Variable name="descriptionfont" description="Blog Description Font"
type="font"
default="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif" value="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif">
< Variable name="postfooterfont" description="Post Footer Font"
type="font"
default="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif" value="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif">
< Variable name="startSide" description="Side where text starts in blog language"
type="automatic" default="left" value="left">
< Variable name="endSide" description="Side where text ends in blog language"
type="automatic" default="right" value="right">
*/

/* Use this with templates/template-twocol.html */

body {
background:$bgcolor;
margin:0;
color:$textcolor;
font:x-small Georgia Serif;
font-size/* */:/**/small;
font-size: /**/small;
text-align: center;
}
a:link {
color:$linkcolor;
text-decoration:none;
}
a:visited {
color:$visitedlinkcolor;
text-decoration:none;
}
a:hover {
color:$titlecolor;
text-decoration:underline;
}
a img {
border-width:0;
}

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

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

#header-inner {
background-position: center;
margin-left: auto;
margin-right: auto;
}

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

#header h1 {
margin:5px 5px 0;
padding:15px 20px .25em;
line-height:1.2em;
text-transform:uppercase;
letter-spacing:.2em;
font: $pagetitlefont;
}

#header a {
color:$pagetitlecolor;
text-decoration:none;
}

#header a:hover {
color:$pagetitlecolor;
}

#header .description {
margin:0 5px 5px;
padding:0 20px 15px;
max-width:700px;
text-transform:uppercase;
letter-spacing:.2em;
line-height: 1.4em;
font: $descriptionfont;
color: $descriptioncolor;
}

#header img {
margin-$startSide: auto;
margin-$endSide: auto;
}


/* Outer-Wrapper
----------------------------------------------- */
#outer-wrapper {
width: 660px;
margin:0 auto;
padding:10px;
text-align:$startSide;
font: $bodyfont;
}

#main-wrapper {
width: 410px;
float: $startSide;
word-wrap: break-word; /* fix for long text breaking sidebar float in IE */
overflow: hidden; /* fix for long non-text content breaking IE sidebar float */
}

#sidebar-wrapper {
width: 220px;
float: $endSide;
word-wrap: break-word; /* fix for long text breaking sidebar float in IE */
overflow: hidden; /* fix for long non-text content breaking IE sidebar float */
}


/* Headings
----------------------------------------------- */

h2 {
margin:1.5em 0 .75em;
font:$headerfont;
line-height: 1.4em;
text-transform:uppercase;
letter-spacing:.2em;
color:$sidebarcolor;
}


/* Posts
-----------------------------------------------
*/
h2.date-header {
margin:1.5em 0 .5em;
}

.post {
margin:.5em 0 1.5em;
border-bottom:1px dotted $bordercolor;
padding-bottom:1.5em;
}
.post h3 {
margin:.25em 0 0;
padding:0 0 4px;
font-size:140%;
font-weight:normal;
line-height:1.4em;
color:$titlecolor;
}

.post h3 a, .post h3 a:visited, .post h3 strong {
display:block;
text-decoration:none;
color:$titlecolor;
font-weight:normal;
}

.post h3 strong, .post h3 a:hover {
color:$textcolor;
}

.post-body {
margin:0 0 .75em;
line-height:1.6em;
}

.post-body blockquote {
line-height:1.3em;
}

.post-footer {
margin: .75em 0;
color:$sidebarcolor;
text-transform:uppercase;
letter-spacing:.1em;
font: $postfooterfont;
line-height: 1.4em;
}

.comment-link {
margin-$startSide:.6em;
}
.post img {
padding:4px;
border:1px solid $bordercolor;
}
.post blockquote {
margin:1em 20px;
}
.post blockquote p {
margin:.75em 0;
}

/* Comments
----------------------------------------------- */
#comments h4 {
margin:1em 0;
font-weight: bold;
line-height: 1.4em;
text-transform:uppercase;
letter-spacing:.2em;
color: $sidebarcolor;
}

#comments-block {
margin:1em 0 1.5em;
line-height:1.6em;
}
#comments-block .comment-author {
margin:.5em 0;
}
#comments-block .comment-body {
margin:.25em 0 0;
}
#comments-block .comment-footer {
margin:-.25em 0 2em;
line-height: 1.4em;
text-transform:uppercase;
letter-spacing:.1em;
}
#comments-block .comment-body p {
margin:0 0 .75em;
}
.deleted-comment {
font-style:italic;
color:gray;
}

#blog-pager-newer-link {
float: $startSide;
}

#blog-pager-older-link {
float: $endSide;
}

#blog-pager {
text-align: center;
}

.feed-links {
clear: both;
line-height: 2.5em;
}

/* Sidebar Content
----------------------------------------------- */
.sidebar {
color: $sidebartextcolor;
line-height: 1.5em;
}

.sidebar ul {
list-style:none;
margin:0 0 0;
padding:0 0 0;
}
.sidebar li {
margin:0;
padding-top:0;
padding-$endSide:0;
padding-bottom:.25em;
padding-$startSide:15px;
text-indent:-15px;
line-height:1.5em;
}

.sidebar .widget, .main .widget {
border-bottom:1px dotted $bordercolor;
margin:0 0 1.5em;
padding:0 0 1.5em;
}

.main .Blog {
border-bottom-width: 0;
}


/* Profile
----------------------------------------------- */
.profile-img {
float: $startSide;
margin-top: 0;
margin-$endSide: 5px;
margin-bottom: 5px;
margin-$startSide: 0;
padding: 4px;
border: 1px solid $bordercolor;
}

.profile-data {
margin:0;
text-transform:uppercase;
letter-spacing:.1em;
font: $postfooterfont;
color: $sidebarcolor;
font-weight: bold;
line-height: 1.6em;
}

.profile-datablock {
margin:.5em 0 .5em;
}

.profile-textblock {
margin: 0.5em 0;
line-height: 1.6em;
}

.profile-link {
font: $postfooterfont;
text-transform: uppercase;
letter-spacing: .1em;
}

/* Footer
----------------------------------------------- */
#footer {
width:660px;
clear:both;
margin:0 auto;
padding-top:15px;
line-height: 1.6em;
text-transform:uppercase;
letter-spacing:.1em;
text-align: center;
}
]]>< /b:skin>
< /head>

< body>
< div id="'outer-wrapper'">< div id="'wrap2'">

< !-- skip links for text browsers -->
< span id="'skiplinks'" style="'display:none;'">
< a href="'#main'">skip to main < /a> |
< a href="'#sidebar'">skip to sidebar< /a>
< /span>

< div id="'header-wrapper'">
< b:section class="'header'" id="'header'" maxwidgets="'1'" showaddelement="'no'">
< b:widget id="'Header1'" locked="'true'" title="'Nerdy" type="'Header'">
< b:includable id="'main'">

< b:if cond="'data:useImage'">
< b:if cond="'data:imagePlacement">
< !--Show just the image, no text-->
< div id="'header-inner'">
< a href="'data:blog.homepageUrl'" style="'display:">
< img alt="'data:title'" height="'data:height'" id="'data:widget.instanceId" src="'data:sourceUrl'" width="'data:width'" style="'display:" />
< /a>
< /div>
< b:else/>
< !--
Show image as background to text. You can't really calculate the width
reliably in JS because margins are not taken into account by any of
clientWidth, offsetWidth or scrollWidth, so we don't force a minimum
width if the user is using shrink to fit.
This results in a margin-width's worth of pixels being cropped. If the
user is not using shrink to fit then we expand the header.
-->
< div style="'"background-image:" id="'header-inner'">
< div class="'titlewrapper'" style="'background:">
< h1 class="'title'" style="'background:">
< b:include name="'title'/">
< /h1>
< /div>
< b:include name="'description'/">
< /div>
< /b:if>
< b:else/>
< !--No header image -->
< div id="'header-inner'">
< div class="'titlewrapper'">
< h1 class="'title'">
< b:include name="'title'/">
< /h1>
< /div>
< b:include name="'description'/">
< /div>
< /b:if>
< /b:includable>
< b:includable id="'title'">
< b:if cond="'data:blog.url">
< data:title/>
< b:else/>
< a href="'data:blog.homepageUrl'">< data:title/>< /a>
< /b:if>
< /b:includable>
< b:includable id="'description'">
< div class="'descriptionwrapper'">
< p class="'description'">< span>< data:description/>< /span>< /p>
< /div>
< /b:includable>
< /b:widget>
< /b:section>
< /div>

< div id="'content-wrapper'">

< div id="'crosscol-wrapper'" style="'text-align:center'">
< b:section class="'crosscol'" id="'crosscol'" showaddelement="'no'/">
< /div>

< div id="'main-wrapper'">
< b:section class="'main'" id="'main'" showaddelement="'no'">
< b:widget id="'Blog1'" locked="'true'" title="'Blog" type="'Blog'">
< b:includable id="'nextprev'">
< div class="'blog-pager'" id="'blog-pager'">
< b:if cond="'data:newerPageUrl'">
< span id="'blog-pager-newer-link'">
< a class="'blog-pager-newer-link'" href="'data:newerPageUrl'" id="'data:widget.instanceId" title="'data:newerPageTitle'">< data:newerpagetitle/>< /a>
< /span>
< /b:if>

< b:if cond="'data:olderPageUrl'">
< span id="'blog-pager-older-link'">
< a class="'blog-pager-older-link'" href="'data:olderPageUrl'" id="'data:widget.instanceId" title="'data:olderPageTitle'">< data:olderpagetitle/>< /a>
< /span>
< /b:if>

< b:if cond="'data:blog.homepageUrl">
< a class="'home-link'" href="'data:blog.homepageUrl'">< data:homemsg/>< /a>
< b:else/>
< b:if cond="'data:newerPageUrl'">
< a class="'home-link'" href="'data:blog.homepageUrl'">< data:homemsg/>< /a>
< /b:if>
< /b:if>

< /div>
< div class="'clear'/">
< /b:includable>
< b:includable id="'backlinks'" var="'post'">
< a name="'links'/">< h4>< data:post.backlinkslabel/>< /h4>
< b:if cond="'data:post.numBacklinks">
< dl class="'comments-block'" id="'comments-block'">
< b:loop values="'data:post.backlinks'" var="'backlink'">
< div class="'collapsed-backlink">
< dt class="'comment-title'">
< span class="'backlink-toggle-zippy'"> < /span>
< a href="'data:backlink.url'" rel="'nofollow'">< data:backlink.title/>< /a>
< b:include data="'backlink'" name="'backlinkDeleteIcon'/">
< /dt>
< dd class="'comment-body">
< data:backlink.snippet/>
< /dd>
< dd class="'comment-footer">
< span class="'comment-author'">< data:post.authorlabel/> < data:backlink.author/>< /span>
< span class="'comment-timestamp'">< data:post.timestamplabel/> < data:backlink.timestamp/>< /span>
< /dd>
< /div>
< /b:loop>
< /dl>
< /b:if>
< p class="'comment-footer'">
< a class="'comment-link'" href="'data:post.createLinkUrl'" id="'data:widget.instanceId" target="'_blank'">< data:post.createlinklabel/>< /a>
< /p>
< /b:includable>
< b:includable id="'post'" var="'post'">
< div class="'post">
< a name="'data:post.id'/">
< b:if cond="'data:post.title'">
< h3 class="'post-title">
< b:if cond="'data:post.link'">
< a href="'data:post.link'">< data:post.title/>< /a>
< b:else/>
< b:if cond="'data:post.url'">
< a href="'data:post.url'">< data:post.title/>< /a>
< b:else/>
< data:post.title/>
< /b:if>
< /b:if>
< /h3>
< /b:if>

< div class="'post-header-line-1'/">

< div class="'post-body">
< data:post.body/>
< div style="'clear:"> < !-- clear for photos floats -->
< /div>

< b:if cond="'data:post.hasJumpLink'">
< div class="'jump-link'">
< a href="'data:post.url">< data:post.jumptext/>< /a>
< /div>
< /b:if>

< div class="'post-footer'">
< div class="'post-footer-line">
< span class="'post-author">
< b:if cond="'data:top.showAuthor'">
< data:top.authorlabel/>
< span class="'fn'">< data:post.author/>< /span>
< /b:if>
< /span>

< span class="'post-timestamp'">
< b:if cond="'data:top.showTimestamp'">
< data:top.timestamplabel/>
< b:if cond="'data:post.url'">
< a class="'timestamp-link'" href="'data:post.url'" rel="'bookmark'" title="'permanent">< abbr class="'published'" title="'data:post.timestampISO8601'">< data:post.timestamp/>< /abbr>< /a>
< /b:if>
< /b:if>
< /span>

< span class="'reaction-buttons'">
< b:if cond="'data:top.showReactions'">
< table border="'0'" cellpadding="'0'" cellspacing="'0'" width="'100%'">< tr>
< td class="'reactions-label-cell'" nowrap="'nowrap'" valign="'top'" width="'1%'">
< span class="'reactions-label'">
< data:top.reactionslabel/>< /span> < /td>
< td>< iframe allowtransparency="'true'" class="'reactions-iframe'" src="'data:post.reactionsUrl'" frameborder="'0'" name="'reactions'" scrolling="'no'/">< /td>
< /tr>< /table>
< /b:if>
< /span>

< span class="'star-ratings'">
< b:if cond="'data:top.showStars'">
< div color="'data:backgroundColor'" color="'data:textColor'" url="'data:post.absoluteUrl'" height="'42'" type="'RatingPanel'" width="'280'/">
< /b:if>
< /span>

< span class="'post-comment-link'">
< b:if cond="'data:blog.pageType">
< b:if cond="'data:post.allowComments'">
< a class="'comment-link'" href="'data:post.addCommentUrl'" onclick="'data:post.addCommentOnclick'">< b:if cond="'data:post.numComments">1 < data:top.commentlabel/>< b:else/>< data:post.numcomments/> < data:top.commentlabelplural/>< /b:if>< /a>
< /b:if>
< /b:if>
< /span>

< !-- backlinks -->
< span class="'post-backlinks">
< b:if cond="'data:blog.pageType">
< b:if cond="'data:post.showBacklinks'">
< a class="'comment-link'" href="'data:post.url">< data:top.backlinklabel/>< /a>
< /b:if>
< /b:if>
< /span>

< span class="'post-icons'">
< !-- email post links -->
< b:if cond="'data:post.emailPostUrl'">
< span class="'item-action'">
< a href="'data:post.emailPostUrl'" title="'data:top.emailPostMsg'">
< img alt="''" class="'icon-action'" height="'13'" src="'http://www.blogger.com/img/icon18_email.gif'" width="'18'/" />
< /a>
< /span>
< /b:if>

< !-- quickedit pencil -->
< b:include data="'post'" name="'postQuickEdit'/">
< /span>
< /div>

< div class="'post-footer-line">
< span class="'post-labels'">
< b:if cond="'data:post.labels'">
< data:postlabelslabel/>
< b:loop values="'data:post.labels'" var="'label'">
< a href="'data:label.url'" rel="'tag'">< data:label.name/>< /a>< b:if cond="'data:label.isLast">,< /b:if>
< /b:loop>
< /b:if>
< /span>
< /div>

< div class="'post-footer-line">
< span class="'post-location'">
< b:if cond="'data:top.showLocation'">
< b:if cond="'data:post.location'">
< data:postlocationlabel/>
< a href="'data:post.location.mapsUrl'" target="'_blank'">< data:post.location.name/>< /a>
< /b:if>
< /b:if>
< /span>
< /div>
< /div>
< /div>
< /b:includable>
< b:includable id="'status-message'">
< b:if cond="'data:navMessage'">
< div class="'status-msg-wrap'">
< div class="'status-msg-body'">
< data:navmessage/>
< /div>
< div class="'status-msg-border'">
< div class="'status-msg-bg'">
< div class="'status-msg-hidden'">< data:navmessage/>< /div>
< /div>
< /div>
< /div>
< div style="'clear:">
< /b:if>
< /b:includable>
< b:includable id="'comment-form'" var="'post'">
< div class="'comment-form'">
< a name="'comment-form'/">
< h4 id="'comment-post-message'">< data:postcommentmsg/>< /h4>
< p>< data:blogcommentmessage/>< /p>
< data:blogteamblogmessage/>
< a href="'data:post.commentFormIframeSrc'" id="'comment-editor-src'/">
< iframe allowtransparency="'true'" class="'blogger-iframe-colorize" frameborder="'0'" height="'275'" id="'comment-editor'" name="'comment-editor'" scrolling="'no'" src="''" width="'100%'/">
< data:post.friendconnectjs/>
< data:post.cmtfpiframe/>
< script type="'text/javascript'">
BLOG_CMT_createIframe('< data:post.apprpcrelaypath/>', '< data:post.communityid/>');
< /script>
< /div>
< /b:includable>
< b:includable id="'backlinkDeleteIcon'" var="'backlink'">
< span class="'"item-control">
< a href="'data:backlink.deleteUrl'" title="'data:top.deleteBacklinkMsg'">
< img src="'http://www.blogger.com/img/icon_delete13.gif'/" />
< /a>
< /span>
< /b:includable>
< b:includable id="'postQuickEdit'" var="'post'">
< b:if cond="'data:post.editUrl'">
< span class="'"item-control">
< a href="'data:post.editUrl'" title="'data:top.editPostMsg'">
< img alt="''" class="'icon-action'" height="'18'" src="'http://www.blogger.com/img/icon18_edit_allbkg.gif'" width="'18'/" />
< /a>
< /span>
< /b:if>
< /b:includable>
< b:includable id="'main'" var="'top'">
< !-- posts -->
< div class="'blog-posts">

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

< data:adstart/>
< b:loop values="'data:posts'" var="'post'">
< b:if cond="'data:blog.url">
< b:if cond="'data:post.labels'">
< b:loop values="'data:post.labels'" var="'label'">
< b:if cond="'data:label.name">
< b:include data="'post'" name="'printPosts'/">
< /b:if>
< /b:loop>
< /b:if>
< b:else/>
< b:include data="'post'" name="'printPosts'/">
< /b:if>
< /b:loop>
< data:adend/>
< /div>

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

< !-- feed links -->
< b:include name="'feedLinks'/">

< b:if cond="'data:top.showStars'">
< script src="'http://www.google.com/jsapi'" type="'text/javascript'/">
< script type="'text/javascript'">
google.load("annotations", "1", {"locale": "< data:top.languagecode/>"});
function initialize() {
google.annotations.setApplicationId(< data:top.blogspotreviews/>);
google.annotations.createAll();
google.annotations.fetch();
}
google.setOnLoadCallback(initialize);
< /script>
< /b:if>

< /b:includable>
< b:includable id="'commentDeleteIcon'" var="'comment'">
< span class="'"item-control">
< a href="'data:comment.deleteUrl'" title="'data:top.deleteCommentMsg'">
< img src="'http://www.blogger.com/img/icon_delete13.gif'/" />
< /a>
< /span>
< /b:includable>
< b:includable id="'printPosts'" var="'post'">
< b:if cond="'data:post.dateHeader'">
< h2 class="'date-header'">< data:post.dateheader/>< /h2>
< /b:if>

< b:include data="'post'" name="'post'/">

< b:if cond="'data:blog.pageType">
< b:include data="'post'" name="'comments'/">
< /b:if>
< /b:includable>
< b:includable id="'feedLinks'">
< b:if cond="'data:blog.pageType"> < !-- Blog feed links -->
< b:if cond="'data:feedLinks'">
< div class="'blog-feeds'">
< b:include data="'feedLinks'" name="'feedLinksBody'/">
< /div>
< /b:if>

< b:else/> < !--Post feed links -->
< div class="'post-feeds'">
< b:loop values="'data:posts'" var="'post'">
< b:if cond="'data:post.allowComments'">
< b:if cond="'data:post.feedLinks'">
< b:include data="'post.feedLinks'" name="'feedLinksBody'/">
< /b:if>
< /b:if>
< /b:loop>
< /div>
< /b:if>
< /b:includable>
< b:includable id="'feedLinksBody'" var="'links'">
< div class="'feed-links'">
< data:feedlinksmsg/>
< b:loop values="'data:links'" var="'f'">
< a class="'feed-link'" href="'data:f.url'" type="'data:f.mimeType'" target="'_blank'">< data:f.name/> (< data:f.feedtype/>)< /a>
< /b:loop>
< /div>
< /b:includable>
< b:includable id="'comments'" var="'post'">
< div class="'comments'" id="'comments'">
< a name="'comments'/">
< b:if cond="'data:post.allowComments'">
< h4>
< b:if cond="'data:post.numComments">
1 < data:commentlabel/>:
< b:else/>
< data:post.numcomments/> < data:commentlabelplural/>:
< /b:if>
< /h4>

< b:if cond="'data:post.commentPagingRequired'">
< span class="'paging-control-container'">
< a class="'data:post.oldLinkClass'" href="'data:post.oldestLinkUrl'">< data:post.oldestlinktext/>< /a>
 
< a class="'data:post.oldLinkClass'" href="'data:post.olderLinkUrl'">< data:post.olderlinktext/>< /a>
 
< data:post.commentrangetext/>
 
< a class="'data:post.newLinkClass'" href="'data:post.newerLinkUrl'">< data:post.newerlinktext/>< /a>
 
< a class="'data:post.newLinkClass'" href="'data:post.newestLinkUrl'">< data:post.newestlinktext/>< /a>
< /span>
< /b:if>

< dl id="'comments-block'">
< b:loop values="'data:post.comments'" var="'comment'">
< dt class="'"comment-author" id="'data:comment.anchorName'">
< b:if cond="'data:comment.favicon'">
< img src="'data:comment.favicon'" height="'16px'" style="'margin-bottom:-2px;'" width="'16px'/" />
< /b:if>
< a name="'data:comment.anchorName'/">
< b:if cond="'data:comment.authorUrl'">
< a href="'data:comment.authorUrl'" rel="'nofollow'">< data:comment.author/>< /a>
< b:else/>
< data:comment.author/>
< /b:if>
< data:commentpostedbymsg/>
< /dt>
< dd class="'comment-body'">
< b:if cond="'data:comment.isDeleted'">
< span class="'deleted-comment'">< data:comment.body/>< /span>
< b:else/>
< p>< data:comment.body/>< /p>
< /b:if>
< /dd>
< dd class="'comment-footer'">
< span class="'comment-timestamp'">
< a href="'data:comment.url'" title="'comment">
< data:comment.timestamp/>
< /a>
< b:include data="'comment'" name="'commentDeleteIcon'/">
< /span>
< /dd>
< /b:loop>
< /dl>

< b:if cond="'data:post.commentPagingRequired'">
< span class="'paging-control-container'">
< a class="'data:post.oldLinkClass'" href="'data:post.oldestLinkUrl'">
< data:post.oldestlinktext/>
< /a>
< a class="'data:post.oldLinkClass'" href="'data:post.olderLinkUrl'">
< data:post.olderlinktext/>
< /a>
 
< data:post.commentrangetext/>
 
< a class="'data:post.newLinkClass'" href="'data:post.newerLinkUrl'">
< data:post.newerlinktext/>
< /a>
< a class="'data:post.newLinkClass'" href="'data:post.newestLinkUrl'">
< data:post.newestlinktext/>
< /a>
< /span>
< /b:if>

< p class="'comment-footer'">
< b:if cond="'data:post.embedCommentForm'">
< b:if cond="'data:post.allowNewComments'">
< b:include data="'post'" name="'comment-form'/">
< b:else/>
< data:post.nonewcommentstext/>
< /b:if>
< b:else/>
< b:if cond="'data:post.allowComments'">
< a href="'data:post.addCommentUrl'" onclick="'data:post.addCommentOnclick'">< data:postcommentmsg/>< /a>
< /b:if>
< /b:if>

< /p>
< /b:if>

< div id="'backlinks-container'">
< div id="'data:widget.instanceId">
< b:if cond="'data:post.showBacklinks'">
< b:include data="'post'" name="'backlinks'/">
< /b:if>
< /div>
< /div>
< /div>
< /b:includable>
< /b:widget>
< /b:section>
< /div>

< div id="'sidebar-wrapper'">
< b:section class="'sidebar'" id="'sidebar'" preferred="'yes'">
< b:widget id="'BlogArchive1'" locked="'false'" title="'Blog" type="'BlogArchive'">
< b:includable id="'main'">
< b:if cond="'data:title'">
< h2>< data:title/>< /h2>
< /b:if>
< div class="'widget-content'">
< div id="'ArchiveList'">
< div id="'data:widget.instanceId">
< b:if cond="'data:style">
< b:include data="'data'" name="'interval'/">
< /b:if>
< b:if cond="'data:style">
< b:include data="'data'" name="'flat'/">
< /b:if>
< b:if cond="'data:style">
< b:include data="'data'" name="'menu'/">
< /b:if>
< /div>
< /div>
< b:include name="'quickedit'/">
< /div>
< /b:includable>
< b:includable id="'flat'" var="'data'">
< ul>
< b:loop values="'data:data'" var="'i'">
< li class="'archivedate'">
< a href="'data:i.url'">< data:i.name/>< /a> (< data:i.post-count/>)
< /li>
< /b:loop>
< /ul>
< /b:includable>
< b:includable id="'menu'" var="'data'">
< select id="'data:widget.instanceId">
< option value="''">< data:title/>< /option>
< b:loop values="'data:data'" var="'i'">
< option value="'data:i.url'">< data:i.name/> (< data:i.post-count/>)< /option>
< /b:loop>
< /select>
< /b:includable>
< b:includable id="'interval'" var="'intervalData'">
< b:loop values="'data:intervalData'" var="'i'">
< ul>
< li class="'"archivedate">
< b:include data="'i'" name="'toggle'/">
< a class="'post-count-link'" href="'data:i.url'">< data:i.name/>< /a>
< span class="'post-count'" dir="'ltr'">(< data:i.post-count/>)< /span>
< b:if cond="'data:i.data'">
< b:include data="'i.data'" name="'interval'/">
< /b:if>
< b:if cond="'data:i.posts'">
< b:include data="'i.posts'" name="'posts'/">
< /b:if>
< /li>
< /ul>
< /b:loop>
< /b:includable>
< b:includable id="'toggle'" var="'interval'">
< b:if cond="'data:interval.toggleId'">
< b:if cond="'data:interval.expclass">
< a class="'toggle'" href="'data:widget.actionUrl" action="toggle"" dir="close&toggle=""" toggleopen=""">
< span class="'zippy">▼ < /span>
< /a>
< b:else/>
< a class="'toggle'" href="'data:widget.actionUrl" action="toggle"" dir="open&toggle=""" toggleopen=""">
< span class="'zippy'">
< b:if cond="'data:blog.languageDirection">
◄
< b:else/>
►
< /b:if>
< /span>
< /a>
< /b:if>
< /b:if>
< /b:includable>
< b:includable id="'posts'" var="'posts'">
< ul class="'posts'">
< b:loop values="'data:posts'" var="'i'">
< li>< a href="'data:i.url'">< data:i.title/>< /a>< /li>
< /b:loop>
< /ul>
< /b:includable>
< /b:widget>
< b:widget id="'Profile1'" locked="'false'" title="'About" type="'Profile'">
< b:includable id="'main'">
< b:if cond="'data:title">
< h2>< data:title/>< /h2>
< /b:if>
< div class="'widget-content'">
< b:if cond="'data:team"> < !-- team blog profile -->
< ul>
< b:loop values="'data:authors'" var="'i'">
< li>< a href="'data:i.userUrl'">< data:i.display-name/>< /a>< /li>
< /b:loop>
< /ul>

< b:else/> < !-- normal blog profile -->

< b:if cond="'data:photo.url">
< a href="'data:userUrl'">< img class="'profile-img'" alt="'data:photo.alt'" height="'data:photo.height'" src="'data:photo.url'" width="'data:photo.width'/" />< /a>
< /b:if>

< dl class="'profile-datablock'">
< dt class="'profile-data'">< data:displayname/>< /dt>

< b:if cond="'data:showlocation">
< dd class="'profile-data'">< data:location/>< /dd>
< /b:if>

< b:if cond="'data:aboutme">< dd class="'profile-textblock'">< data:aboutme/>< /dd>< /b:if>
< /dl>
< a class="'profile-link'" href="'data:userUrl'">< data:viewprofilemsg/>< /a>
< /b:if>

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

< !-- spacer for skins that want sidebar and main to be the same height-->
< div class="'clear'"> < /div>

< /div> < !-- end content-wrapper -->

< div id="'footer-wrapper'">
< b:section class="'footer'" id="'footer'/">
< /div>

< /div>< /div> < !-- end outer-wrapper -->
< /body>
< /html>