diff --git a/SickBeard.py b/SickBeard.py
index e9d08e1e52a44f27148139a58671ed6315f95d4e..6014dd844ff9e0c5c26d53b07f2f6fc5312739b1 100755
--- a/SickBeard.py
+++ b/SickBeard.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python2.7
 # Author: Nic Wolfe <nic@wolfeden.ca>
 # URL: http://code.google.com/p/sickbeard/
 #
@@ -37,33 +37,20 @@ import shutil_custom
 
 shutil.copyfile = shutil_custom.copyfile_custom
 
-if sys.version_info < (2, 6):
-    print "Sorry, requires Python 2.6 or 2.7."
+if sys.version_info < (2, 7):
+    print "Sorry, requires Python 2.7.x"
     sys.exit(1)
 
-try:
-    import Cheetah
-    if Cheetah.Version[0] != '2':
-        raise ValueError
-except ValueError:
-    print "Sorry, requires Python module Cheetah 2.1.0 or newer."
-    sys.exit(1)
-except:
-    print "The Python module Cheetah is required"
-    sys.exit(1)
-
-# We only need this for compiling an EXE and I will just always do that on 2.6+
+# We only need this for compiling an EXE and I will just always do that on 2.7+
 if sys.hexversion >= 0x020600F0:
     from multiprocessing import freeze_support  # @UnresolvedImport
 
-
 import certifi
 for env_cert_var in ['REQUESTS_CA_BUNDLE', 'CURL_CA_BUNDLE']:
     ca_cert_loc = os.environ.get(env_cert_var)
     if (not isinstance(ca_cert_loc, basestring)) or (not os.path.isfile(ca_cert_loc)):
         os.environ[env_cert_var] = certifi.where()
 
-
 if sys.version_info >= (2, 7, 9):
     import ssl
     ssl._create_default_https_context = ssl._create_unverified_context
@@ -156,6 +143,7 @@ class SickRage(object):
                  "sickbeard/clients/qbittorrent.py",
                  "sickbeard/clients/transmission.py",
                  "sickbeard/clients/deluge.py",
+                 "sickbeard/clients/deluged.py",
                  "sickbeard/clients/rtorrent.py"
                 ]
                 
diff --git a/autoProcessTV/mediaToSickbeard.py b/autoProcessTV/mediaToSickbeard.py
index ba0d3cafecb06db8af00001aeba78969add90a7b..289563af5a6511a067bcc7d40d1c31ecf9e0efb4 100755
--- a/autoProcessTV/mediaToSickbeard.py
+++ b/autoProcessTV/mediaToSickbeard.py
@@ -78,6 +78,19 @@ def deluge():
     
     return (dirName, nzbName)
 
+def deluged() :
+
+    if len(sys.argv) < 4:
+        scriptlogger.error('No folder supplied - is this being called from Deluge?')
+        print "No folder supplied - is this being called from Deluge?"
+        time.sleep(3)
+        sys.exit()
+
+    dirName = sys.argv[3]
+    nzbName = sys.argv[2]
+
+    return (dirName, nzbName)
+
 def blackhole():
 
     if None != os.getenv('TR_TORRENT_DIR'):
@@ -150,7 +163,7 @@ def main():
         time.sleep(3)
         sys.exit()
         
-    if not torrent_method in ['utorrent', 'transmission', 'deluge', 'blackhole']:
+    if not torrent_method in ['utorrent', 'transmission', 'deluge', 'deluged', 'blackhole']:
         scriptlogger.error(u'Unknown Torrent Method. Aborting!')
         print u'Unknown Torrent Method. Aborting!'
         time.sleep(3)
diff --git a/contributing.md b/contributing.md
index f461ca53fc119a02cc5102e943e34239494dcd9d..4ca2dc5ed4e75b5f418b9f03b94ff12c3a687a42 100644
--- a/contributing.md
+++ b/contributing.md
@@ -102,6 +102,15 @@ Please follow this process; it's the best way to get your work included in the p
    # merge upstream changes
    git merge upstream/master
    ```
+   
+- Make sure that your develop branch is up to date:
+
+   ```bash
+   # Switch to the develop branch
+   git checkout develop
+   # Pull down any updates
+   git pull
+   ```
 
 - Create a new topic branch to contain your feature, change, or fix:
 
diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css
index e51f082d56ae033cfea21a59ff1aabe43f5a27f1..0eaa72bcefe36486b327026f958c7ef265737b5a 100644
--- a/gui/slick/css/style.css
+++ b/gui/slick/css/style.css
@@ -3251,3 +3251,49 @@ login.css
 		padding: 20px;
 		float: right;
 	}
+
+/* =======================================================================
+IMDB Popular
+========================================================================== */
+
+.popularShow{
+	margin-bottom:30px;
+}
+
+.popularShow h3{
+	padding:0px;
+	margin:0px;
+	display:inline-block;
+	margin-right:30px;
+}
+
+.popularShow .left{
+	float:left;
+	width:100px;
+}
+
+.popularShow .right{
+	float:left;
+	width:600px;
+}
+
+.popularShow .year{
+	font-style:italic;
+	opacity:0.7;
+}
+
+.popularShow .coverImage{
+	width:80%;
+	padding-left:20px;
+	margin-top:4px;
+}
+
+.popularShow .rating{
+	font-size:90%;
+	display:inline-block;
+	margin-left:0px;
+}
+
+.popularShow p{
+	margin-bottom:0px;
+}
\ No newline at end of file
diff --git a/gui/slick/images/network/amazon prime instant video.png b/gui/slick/images/network/amazon prime instant video.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffa4f382943802f311c6ee6f5f077289eee953a5
Binary files /dev/null and b/gui/slick/images/network/amazon prime instant video.png differ
diff --git a/gui/slick/images/network/bbc hd.png b/gui/slick/images/network/bbc hd.png
new file mode 100644
index 0000000000000000000000000000000000000000..28b28c584a984f7e9d31da96671f5b979ba91c70
Binary files /dev/null and b/gui/slick/images/network/bbc hd.png differ
diff --git a/gui/slick/images/network/kanal5.png b/gui/slick/images/network/kanal5.png
new file mode 100644
index 0000000000000000000000000000000000000000..389e6372b45738a2e52e2c85ba73746f0b0d2f96
Binary files /dev/null and b/gui/slick/images/network/kanal5.png differ
diff --git a/gui/slick/images/network/more4.png b/gui/slick/images/network/more4.png
new file mode 100644
index 0000000000000000000000000000000000000000..f27a7bcc440a0838d7797b53f9287ec2f15a686c
Binary files /dev/null and b/gui/slick/images/network/more4.png differ
diff --git a/gui/slick/images/network/nbcsn.png b/gui/slick/images/network/nbcsn.png
new file mode 100644
index 0000000000000000000000000000000000000000..39a82ed29c086855e9b1e8dcc3593d6e8e0fc096
Binary files /dev/null and b/gui/slick/images/network/nbcsn.png differ
diff --git a/gui/slick/images/network/nfl network.png b/gui/slick/images/network/nfl network.png
new file mode 100644
index 0000000000000000000000000000000000000000..b09b7baca21dc8e045c4e285c44bcaa28438bcfe
Binary files /dev/null and b/gui/slick/images/network/nfl network.png differ
diff --git a/gui/slick/images/network/nick jr..png b/gui/slick/images/network/nick jr..png
new file mode 100644
index 0000000000000000000000000000000000000000..567a5ae5d3c3c237e4981c543302c3a9278dcc3f
Binary files /dev/null and b/gui/slick/images/network/nick jr..png differ
diff --git a/gui/slick/images/network/nrk.png b/gui/slick/images/network/nrk.png
new file mode 100644
index 0000000000000000000000000000000000000000..a229a7b727d8b4dbea431f560b15fd13da437017
Binary files /dev/null and b/gui/slick/images/network/nrk.png differ
diff --git a/gui/slick/images/network/rai.png b/gui/slick/images/network/rai.png
new file mode 100644
index 0000000000000000000000000000000000000000..46faab679246d391ea840d2a588be8c3235e487f
Binary files /dev/null and b/gui/slick/images/network/rai.png differ
diff --git a/gui/slick/images/network/reelzchannel.png b/gui/slick/images/network/reelzchannel.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c0d4a28a7ec885d1ea253e37d440f50f301bed0
Binary files /dev/null and b/gui/slick/images/network/reelzchannel.png differ
diff --git a/gui/slick/images/network/svt drama.png b/gui/slick/images/network/svt drama.png
new file mode 100644
index 0000000000000000000000000000000000000000..dcb6fbb04662a3ade7b165b5b2c97b0e3315da3c
Binary files /dev/null and b/gui/slick/images/network/svt drama.png differ
diff --git a/gui/slick/images/network/svt2.png b/gui/slick/images/network/svt2.png
new file mode 100644
index 0000000000000000000000000000000000000000..b2c4d06feedff8f51332c64c92775696053f33fd
Binary files /dev/null and b/gui/slick/images/network/svt2.png differ
diff --git a/gui/slick/images/network/tv norge.png b/gui/slick/images/network/tv norge.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a17b749b64ca746f81274aba86092c75cbad370
Binary files /dev/null and b/gui/slick/images/network/tv norge.png differ
diff --git a/gui/slick/images/network/tv1.png b/gui/slick/images/network/tv1.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8da66b843c7396a7e12adb2dc034d575a274bd9
Binary files /dev/null and b/gui/slick/images/network/tv1.png differ
diff --git a/gui/slick/interfaces/default/IRC.mako b/gui/slick/interfaces/default/IRC.mako
new file mode 100644
index 0000000000000000000000000000000000000000..ea91fd94c806f810863af42b78994276dae3c286
--- /dev/null
+++ b/gui/slick/interfaces/default/IRC.mako
@@ -0,0 +1,7 @@
+<%include file="/inc_top.mako"/>
+<%
+from sickbeard import GIT_USERNAME
+username = ("SickRageUI|?", GIT_USERNAME)[bool(GIT_USERNAME)]
+%>
+<iframe id="extFrame" src="https://kiwiirc.com/client/irc.freenode.net/?nick=${username}&theme=basic#sickrage" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/IRC.tmpl b/gui/slick/interfaces/default/IRC.tmpl
deleted file mode 100644
index cd38b9b498da6b0ebddf5e92bf0bc6472d89b28b..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/IRC.tmpl
+++ /dev/null
@@ -1,15 +0,0 @@
-#import sickbeard
-#from sickbeard.common import *
-
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#if $sickbeard.GIT_USERNAME
-#set $username = $sickbeard.GIT_USERNAME
-#else
-#set $username = "SickRageUI|?"
-#end if
-
-<iframe id="extFrame" src="https://kiwiirc.com/client/irc.freenode.net/?nick=$username&theme=basic#sickrage" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe>
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/apiBuilder.tmpl b/gui/slick/interfaces/default/apiBuilder.mako
similarity index 84%
rename from gui/slick/interfaces/default/apiBuilder.tmpl
rename to gui/slick/interfaces/default/apiBuilder.mako
index fedc9053a7a5a40c8375050bef7cea5dcfa32f49..7a42cf0bee1d00515eab718e69402c49891fffa8 100644
--- a/gui/slick/interfaces/default/apiBuilder.tmpl
+++ b/gui/slick/interfaces/default/apiBuilder.mako
@@ -8,20 +8,16 @@
         <meta name="robots" content="noindex">
 
         <script type="text/javascript" charset="utf-8">
-        <!--
-            sbRoot = "$sbRoot";
-        //-->
+            sbRoot = '${sbRoot}';
         </script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery-1.11.2.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/apibuilder.js?$sbPID"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/apibuilder.js?${sbPID}"></script>
 
         <style type="text/css">
-        <!--
             #apibuilder select { padding: 2px 2px 2px 6px; display: block; float: left; margin: auto 8px 4px auto; }
             #apibuilder select option { padding: 1px 6px; line-height: 1.2em; }
             #apibuilder .disabled { color: #ccc; }
             #apibuilder .action { background-color: #efefef; }
-        -->
         </style>
 
 <script type="text/javascript">
@@ -80,9 +76,9 @@ addList("Command", "Shows", "?cmd=shows", "shows");
 addOption("Command", "Shows.Stats", "?cmd=shows.stats", "", "", "action");
 
 // addOption("indexerid", "Optional Param", "", 1);
-#for $curShow in $sortedShowList:
-addOption("indexerid", "$curShow.name", "&indexerid=$curShow.indexerid");
-#end for
+% for curShow in sortedShowList:
+addOption("indexerid", "${curShow.name}", "&indexerid=${curShow.indexerid}");
+% endfor
 
 addOption("logs", "Optional Param", "", 1);
 addOption("logs", "Debug", "&min_level=debug");
@@ -180,20 +176,20 @@ addOption("sb.searchindexers-lang", "Spanish", "&lang=es");   // 16
 addOption("sb.searchindexers-lang", "Swedish", "&lang=sv");   // 8
 addOption("sb.searchindexers-lang", "Turkish", "&lang=tr");   // 21
 
-#for $curShow in $sortedShowList:
-addList("seasons", "$curShow.name", "&indexerid=$curShow.indexerid", "seasons-$curShow.indexerid");
-#end for
+% for curShow in sortedShowList:
+addList("seasons", "${curShow.name}", "&indexerid=${curShow.indexerid}", "seasons-${curShow.indexerid}");
+% endfor
 
-#for $curShow in $sortedShowList:
-addList("show.seasonlist", "$curShow.name", "&indexerid=$curShow.indexerid", "show.seasonlist-sort");
-#end for
+% for curShow in sortedShowList:
+addList("show.seasonlist", "${curShow.name}", "&indexerid=${curShow.indexerid}", "show.seasonlist-sort");
+% endfor
 
 addOption("show.seasonlist-sort", "Optional Param", "", 1);
 addOption("show.seasonlist-sort", "Sort by Ascending", "&sort=asc");
 
-#for $curShow in $sortedShowList:
-addList("show.setquality", "$curShow.name", "&indexerid=$curShow.indexerid", "quality");
-#end for
+% for curShow in sortedShowList:
+addList("show.setquality", "${curShow.name}", "&indexerid=${curShow.indexerid}", "quality");
+% endfor
 
 //build out generic quality options
 addOptGroup("quality", "Quality Templates");
@@ -237,59 +233,59 @@ addList("quality-archive", "720p Web-DL/720p BluRay", "&archive=hdwebdl|hdbluray
 endOptGroup("quality-archive");
 
 // build out each show's season list for season cmd
-#for $curShow in $seasonSQLResults:
-addOption("seasons-$curShow", "Optional Param", "", 1);
-    #for $curShowSeason in $seasonSQLResults[$curShow]:
-addOption("seasons-$curShow", "$curShowSeason.season", "&season=$curShowSeason.season");
-    #end for
-#end for
+% for curShow in seasonSQLResults:
+addOption("seasons-${curShow}", "Optional Param", "", 1);
+    % for curShowSeason in seasonSQLResults[curShow]:
+addOption("seasons-${curShow}", "${curShowSeason['season']}", "&season=${curShowSeason['season']}");
+    % endfor
+% endfor
 
-#for $curShow in $sortedShowList:
-addList("episode", "$curShow.name", "&indexerid=$curShow.indexerid", "episode-$curShow.indexerid");
-#end for
+% for curShow in sortedShowList:
+addList("episode", "${curShow.name}", "&indexerid=${curShow.indexerid}", "episode-${curShow.indexerid}");
+% endfor
 
 // build out each show's season+episode list for episode cmd
-#for $curShow in $episodeSQLResults:
-    #for $curShowSeason in $episodeSQLResults[$curShow]:
-addList("episode-$curShow", "$curShowSeason.season x $curShowSeason.episode", "&season=$curShowSeason.season&episode=$curShowSeason.episode", "episode-$curShow-full");
-    #end for
-addOption("episode-$curShow-full", "Optional Param", "", 1);
-addOption("episode-$curShow-full", "Show Full Path", "&full_path=1");
-#end for
+% for curShow in episodeSQLResults:
+    % for curShowSeason in episodeSQLResults[curShow]:
+addList("episode-${curShow}", "${curShowSeason['season']} x ${curShowSeason['episode']}", "&season=${curShowSeason['season']}&episode=${curShowSeason['episode']}", "episode-${curShow}-full");
+    % endfor
+addOption("episode-${curShow}-full", "Optional Param", "", 1);
+addOption("episode-${curShow}-full", "Show Full Path", "&full_path=1");
+% endfor
 
 // build out tvshow list for episode.search
-#for $curShow in $sortedShowList:
-addList("episode.search", "$curShow.name", "&indexerid=$curShow.indexerid", "episode.search-$curShow.indexerid");
-#end for
+% for curShow in sortedShowList:
+addList("episode.search", "${curShow.name}", "&indexerid=${curShow.indexerid}", "episode.search-${curShow.indexerid}");
+% endfor
 
 // build out each show's season+episode list for episode.search cmd
-#for $curShow in $episodeSQLResults:
-    #for $curShowSeason in $episodeSQLResults[$curShow]:
-addOption("episode.search-$curShow", "$curShowSeason.season x $curShowSeason.episode", "&season=$curShowSeason.season&episode=$curShowSeason.episode");
-    #end for
-#end for
+% for curShow in episodeSQLResults:
+    % for curShowSeason in episodeSQLResults[curShow]:
+addOption("episode.search-${curShow}", "${curShowSeason['season']} x ${curShowSeason['episode']}", "&season=${curShowSeason['season']}&episode=${curShowSeason['episode']}");
+    % endfor
+% endfor
 
 // build out tvshow list for episode.setstatus
-#for $curShow in $sortedShowList:
-addList("episode.setstatus", "$curShow.name", "&indexerid=$curShow.indexerid", "episode.setstatus-$curShow.indexerid");
-#end for
+% for curShow in sortedShowList:
+addList("episode.setstatus", "${curShow.name}", "&indexerid=${curShow.indexerid}", "episode.setstatus-${curShow.indexerid}");
+% endfor
 
 // build out each show's season+episode list for episode.setstatus cmd
-#for $curShow in $episodeSQLResults:
-    #set $curSeason = -1
-    #for $curShowSeason in $episodeSQLResults[$curShow]:
-        #if $curShowSeason.season != $curSeason and $curShowSeason.season != 0:
+% for curShow in episodeSQLResults:
+    <% curSeason = -1 %>
+    % for curShowSeason in episodeSQLResults[curShow]:
+        % if curShowSeason['season'] != curSeason and curShowSeason['season'] != 0:
             // insert just the season as the ep number is now optional
-            addList("episode.setstatus-$curShow", "Season $curShowSeason.season", "&season=$curShowSeason.season", "episode-status-$curShow");
-        #end if
-        #set $curSeason = int($curShowSeason.season)
-addList("episode.setstatus-$curShow", "$curShowSeason.season x $curShowSeason.episode", "&season=$curShowSeason.season&episode=$curShowSeason.episode", "episode-status-$curShow");
-    #end for
-addList("episode-status-$curShow", "Wanted", "&status=wanted", "force");
-addList("episode-status-$curShow", "Skipped", "&status=skipped", "force");
-addList("episode-status-$curShow", "Archived", "&status=archived", "force");
-addList("episode-status-$curShow", "Ignored", "&status=ignored", "force");
-#end for
+            addList("episode.setstatus-${curShow}", "Season ${curShowSeason['season']}", "&season=${curShowSeason['season']}", "episode-status-${curShow}");
+        % endif
+        <% curSeason = int(curShowSeason['season']) %>
+addList("episode.setstatus-${curShow}", "${curShowSeason['season']} x ${curShowSeason['episode']}", "&season=${curShowSeason['season']}&episode=${curShowSeason['episode']}", "episode-status-${curShow}");
+    % endfor
+addList("episode-status-${curShow}", "Wanted", "&status=wanted", "force");
+addList("episode-status-${curShow}", "Skipped", "&status=skipped", "force");
+addList("episode-status-${curShow}", "Archived", "&status=archived", "force");
+addList("episode-status-${curShow}", "Ignored", "&status=ignored", "force");
+% endfor
 
 addOption("force", "Optional Param", "", 1);
 addOption("force", "Replace Downloaded EP", "&force=1");
@@ -331,9 +327,9 @@ addOption("history-limit", "Show Only Downloaded", "&type=downloaded");
 addOption("history-limit", "Show Only Snatched", "&type=snatched");
 
 addOption("exceptions", "Optional Param", "", 1);
-#for $curShow in $sortedShowList:
-addOption("exceptions", "$curShow.name", "&indexerid=$curShow.indexerid");
-#end for
+% for curShow in sortedShowList:
+addOption("exceptions", "${curShow.name}", "&indexerid=${curShow.indexerid}");
+% endfor
 
 addOption("sb.pausebacklog", "Optional Param", "", 1);
 addOption("sb.pausebacklog", "Pause", "&pause=1");
@@ -351,16 +347,16 @@ addOption("sb.deleterootdir", "C:\\Temp", "&location=C:\\Temp", "", 1);
 addOption("sb.deleterootdir", "/usr/bin", "&location=/usr/bin/");
 addOption("sb.deleterootdir", "S:\\Invalid_Location", "&location=S:\\Invalid_Location");
 
-#for $curShow in $sortedShowList:
-addList("show.pause", "$curShow.name", "&indexerid=$curShow.indexerid", "show.pause-opt");
-#end for
+% for curShow in sortedShowList:
+addList("show.pause", "${curShow.name}", "&indexerid=${curShow.indexerid}", "show.pause-opt");
+% endfor
 addOption("show.pause-opt", "Optional Param", "", 1);
 addOption("show.pause-opt", "Unpause", "&pause=0");
 addOption("show.pause-opt", "Pause", "&pause=1");
 
-#for $curShow in $sortedShowList:
-addList("show.delete", "$curShow.name", "&indexerid=$curShow.indexerid", "show.delete-opt");
-#end for
+% for curShow in sortedShowList:
+addList("show.delete", "${curShow.name}", "&indexerid=${curShow.indexerid}", "show.delete-opt");
+% endfor
 addOption("show.delete-opt", "Remove Files", "&removefiles=1");
 addOption("show.delete-opt", "Don't Remove Files", "&removefiles=0");
 
@@ -373,7 +369,7 @@ addOption("show.delete-opt", "Don't Remove Files", "&removefiles=0");
 <table align="center">
     <tr>
         <td>
-            <input type="text" size="40" id="apikey" name="apikey" value="$apikey">
+            <input type="text" size="40" id="apikey" name="apikey" value="${apikey}">
             <input type="checkbox" id="debug" class="global"><label for="debug"> Debug?</label>
             <input type="checkbox" id="profile" class="global"><label for="profile"> Profile?</label>
             <input type="checkbox" id="jsonp" class="global"><label for="jsonp"> JSONP?</label>
@@ -402,4 +398,4 @@ addOption("show.delete-opt", "Don't Remove Files", "&removefiles=0");
 
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/gui/slick/interfaces/default/comingEpisodes.mako b/gui/slick/interfaces/default/comingEpisodes.mako
new file mode 100644
index 0000000000000000000000000000000000000000..752493538b45312bc688fae1409af30ef9222bdb
--- /dev/null
+++ b/gui/slick/interfaces/default/comingEpisodes.mako
@@ -0,0 +1,533 @@
+<%!
+    import sickbeard
+    from sickbeard.helpers import anon_url
+    from sickbeard import sbdatetime
+    from sickbeard.common import qualityPresets, qualityPresetStrings
+    import datetime
+    import time
+%>
+<%
+    sort = sickbeard.COMING_EPS_SORT
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/ajaxEpSearch.js?${sbPID}"></script>
+<h1 class="header">${header}</h1>
+<style type="text/css">
+#SubMenu {display:none;}
+#contentWrapper {padding-top:30px;}
+</style>
+
+<div class="h2footer pull-right">
+    <span>Layout:
+        <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+            <option value="${sbRoot}/setComingEpsLayout/?layout=poster" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'poster']} >Poster</option>
+            <option value="${sbRoot}/setComingEpsLayout/?layout=calendar" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'calendar']} >Calendar</option>
+            <option value="${sbRoot}/setComingEpsLayout/?layout=banner" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'banner']} >Banner</option>
+            <option value="${sbRoot}/setComingEpsLayout/?layout=list" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'list']} >List</option>
+        </select>
+    </span>
+    &nbsp;
+
+    <span>Sort By:
+        <select name="sort" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+            <option value="${sbRoot}/setComingEpsSort/?sort=date" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'date']} >Date</option>
+            <option value="${sbRoot}/setComingEpsSort/?sort=network" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'network']} >Network</option>
+            <option value="${sbRoot}/setComingEpsSort/?sort=show" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'show']} >Show</option>
+        </select>
+    </span>
+    &nbsp;
+
+    <span>View Paused:
+        <select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+            <option value="${sbRoot}/toggleComingEpsDisplayPaused" ${('', 'selected="selected"')[not bool(sickbeard.COMING_EPS_DISPLAY_PAUSED)]}>Hidden</option>
+            <option value="${sbRoot}/toggleComingEpsDisplayPaused" ${('', 'selected="selected"')[bool(sickbeard.COMING_EPS_DISPLAY_PAUSED)]}>Shown</option>
+        </select>
+    </span>
+</div>
+
+<div class="key pull-right">
+% if 'calendar' != layout:
+    <b>Key:</b>
+    <span class="listing-key listing-overdue">Missed</span>
+    <span class="listing-key listing-current">Current</span>
+    <span class="listing-key listing-default">Future</span>
+    <span class="listing-key listing-toofar">Distant</span>
+% endif
+    <a class="btn btn-inline forceBacklog" href="webcal://${sbHost}:${sbHttpPort}/calendar">
+    <i class="icon-calendar icon-white"></i>Subscribe</a>
+</div>
+
+<br>
+
+% if 'list' == layout:
+<!-- start list view //-->
+
+<script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script>
+<script type="text/javascript" charset="utf-8">
+$.tablesorter.addParser({
+    id: 'loadingNames',
+    is: function(s) {
+        return false
+    },
+    format: function(s) {
+        if (0 == s.indexOf('Loading...'))
+            return s.replace('Loading...', '000')
+% if not sickbeard.SORT_ARTICLE:
+            return (s || '').replace(/^(The|A|An)\s/i, '')
+% else:
+            return (s || '')
+% endif
+    },
+    type: 'text'
+});
+$.tablesorter.addParser({
+    id: 'quality',
+    is: function(s) {
+        return false
+    },
+    format: function(s) {
+        return s.replace('hd1080p', 5).replace('hd720p', 4).replace('hd', 3).replace('sd', 2).replace('any', 1).replace('best', 0).replace('custom', 7)
+    },
+    type: 'numeric'
+});
+$.tablesorter.addParser({
+    id: 'cDate',
+    is: function(s) {
+        return false
+    },
+    format: function(s) {
+        return s
+    },
+    type: 'numeric'
+});
+
+$(document).ready(function(){
+<% sort_codes = {'date': 0, 'show': 1, 'network': 4} %>
+% if sort not in sort_codes:
+    <% sort = 'date' %>
+% endif
+
+    sortList = [[${sort_codes[sort]}, 0]];
+
+    $('#showListTable:has(tbody tr)').tablesorter({
+        widgets: ['stickyHeaders'],
+        sortList: sortList,
+        textExtraction: {
+            0: function(node) { return $(node).find('span').text().toLowerCase() },
+            5: function(node) { return $(node).find('span').text().toLowerCase() }
+        },
+        headers: {
+            0: { sorter: 'cDate' },
+            1: { sorter: 'loadingNames' },
+            2: { sorter: false },
+            3: { sorter: false },
+            4: { sorter: 'loadingNames' },
+            5: { sorter: 'quality' },
+            6: { sorter: false },
+            7: { sorter: false },
+            8: { sorter: false }
+        }
+    });
+
+    $('#sbRoot').ajaxEpSearch();
+
+    <% fuzzydate = 'airdate' %>
+    % if sickbeard.FUZZY_DATING:
+    fuzzyMoment({
+        containerClass : '.${fuzzydate}',
+        dateHasTime : true,
+        dateFormat : '${sickbeard.DATE_PRESET}',
+        timeFormat : '${sickbeard.TIME_PRESET}',
+        trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]}
+    });
+    % endif
+
+});
+</script>
+
+<% show_div = 'listing-default' %>
+
+<input type="hidden" id="sbRoot" value="${sbRoot}" />
+
+<table id="showListTable" class="sickbeardTable tablesorter seasonstyle" cellspacing="1" border="0" cellpadding="0">
+
+    <thead>
+        <tr>
+            <th>Airdate</th>
+            <th>Show</th>
+            <th nowrap="nowrap">Next Ep</th>
+            <th>Next Ep Name</th>
+            <th>Network</th>
+            <th>Quality</th>
+            <th>Indexers</th>
+            <th>Search</th>
+        </tr>
+    </thead>
+
+    <tbody style="text-shadow:none;">
+
+% for cur_result in sql_results:
+<%
+    cur_indexer = int(cur_result['indexer'])
+    run_time = cur_result['runtime']
+
+    if int(cur_result['paused']) and not sickbeard.COMING_EPS_DISPLAY_PAUSED:
+        continue
+
+    cur_ep_airdate = cur_result['localtime'].date()
+
+    if run_time:
+        cur_ep_enddate = cur_result['localtime'] + datetime.timedelta(minutes = run_time)
+        if cur_ep_enddate < today:
+            show_div = 'listing-overdue'
+        elif cur_ep_airdate >= next_week.date():
+            show_div = 'listing-toofar'
+        elif cur_ep_airdate >= today.date() and cur_ep_airdate < next_week.date():
+            if cur_ep_airdate == today.date():
+                show_div = 'listing-current'
+            else:
+                show_div = 'listing-default'
+%>
+
+        <!-- start ${cur_result['show_name']} //-->
+        <tr class="${show_div}">
+            ## forced to use a div to wrap airdate, the column sort went crazy with a span
+            <td align="center" nowrap="nowrap">
+                <div class="${fuzzydate}">${sbdatetime.sbdatetime.sbfdatetime(cur_result['localtime']).decode(sickbeard.SYS_ENCODING)}</div><span class="sort_data">${time.mktime(cur_result['localtime'].timetuple())}</span>
+            </td>
+
+            <td class="tvShow" nowrap="nowrap"><a href="${sbRoot}/home/displayShow?show=${cur_result['showid']}">${cur_result['show_name']}</a>
+% if int(cur_result['paused']):
+                <span class="pause">[paused]</span>
+% endif
+            </td>
+
+            <td nowrap="nowrap" align="center">
+                ${'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode']))}
+            </td>
+
+            <td>
+% if cur_result['description']:
+                <img alt='' src='${sbRoot}/images/info32.png' height='16' width='16' class='plotInfo' id="plot_info_${'%s_%s_%s' % (str(cur_result['showid']), str(cur_result['season']), str(cur_result['episode']))}" />
+% else:
+                <img alt="" src="${sbRoot}/images/info32.png" width="16" height="16" class="plotInfoNone"  />
+% endif
+                ${cur_result['name']}
+            </td>
+
+            <td align="center">
+                ${cur_result['network']}
+            </td>
+
+            <td align="center">
+% if int(cur_result['quality']) in qualityPresets:
+                <span class="quality ${qualityPresetStrings[int(cur_result['quality'])]}">${qualityPresetStrings[int(cur_result['quality'])]}</span>
+% else:
+                <span class="quality Custom">Custom</span>
+% endif
+            </td>
+
+            <td align="center" style="vertical-align: middle;">
+% if cur_result['imdb_id']:
+                <a href="${anon_url('http://www.imdb.com/title/', cur_result['imdb_id'])}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="${sbRoot}/images/imdb.png" />
+% endif
+                <a href="${anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid'])}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="${sickbeard.indexerApi(cur_indexer).config['show_url']}${cur_result['showid']}"><img alt="${sickbeard.indexerApi(cur_indexer).name}" height="16" width="16" src="${sbRoot}/images/${sickbeard.indexerApi(cur_indexer).config['icon']}" /></a>
+            </td>
+
+            <td align="center">
+                <a href="${sbRoot}/home/searchEpisode?show=${cur_result['showid']}&amp;season=${cur_result['season']}&amp;episode=${cur_result['episode']}" title="Manual Search" id="forceUpdate-${cur_result['showid']}x${cur_result['season']}x${cur_result['episode']}" class="forceUpdate epSearch"><img alt="[search]" height="16" width="16" src="${sbRoot}/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a>
+            </td>
+        </tr>
+        <!-- end cur_result['show_name'] //-->
+% endfor
+    </tbody>
+
+    <tfoot>
+        <tr>
+            <th rowspan="1" colspan="10" align="center">&nbsp</th>
+        </tr>
+    </tfoot>
+
+</table>
+<!-- end list view //-->
+
+
+% elif layout in ['banner', 'poster']:
+<!-- start non list view //-->
+<script type="text/javascript" charset="utf-8">
+$(document).ready(function(){
+    $('#sbRoot').ajaxEpSearch({'size': 16, 'loadingImage': 'loading16' + themeSpinner + '.gif'});
+    $('.ep_summary').hide();
+    $('.ep_summaryTrigger').click(function() {
+        $(this).next('.ep_summary').slideToggle('normal', function() {
+            $(this).prev('.ep_summaryTrigger').attr('src', function(i, src) {
+                return $(this).next('.ep_summary').is(':visible') ? src.replace('plus','minus') : src.replace('minus','plus')
+            });
+        });
+    });
+
+    <% fuzzydate = 'airdate' %>
+    % if sickbeard.FUZZY_DATING:
+    fuzzyMoment({
+        dtInline : true,
+        dtGlue : ' at ',
+        containerClass : '.${fuzzydate}',
+        dateHasTime : true,
+        dateFormat : '${sickbeard.DATE_PRESET}',
+        timeFormat : '${sickbeard.TIME_PRESET}',
+        trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]}
+    });
+    % endif
+
+});
+</script>
+
+<%
+    cur_segment = None
+    too_late_header = False
+    missed_header = False
+    today_header = False
+    show_div = 'ep_listing listing-default'
+%>
+% if 'show' == sort:
+    <br /><br />
+% endif
+
+% for cur_result in sql_results:
+<%
+    cur_indexer = int(cur_result['indexer'])
+
+    if int(cur_result['paused']) and not sickbeard.COMING_EPS_DISPLAY_PAUSED:
+        continue
+
+    run_time = cur_result['runtime']
+%>
+    % if 'network' == sort:
+        <% show_network = ('no network', cur_result['network'])[bool(cur_result['network'])] %>
+        % if cur_segment != show_network:
+            <div class="comingepheader">
+               <br><h2 class="network">${show_network}</h2>
+
+            <% cur_segment = cur_result['network'] %>
+        % endif
+        <% cur_ep_airdate = cur_result['localtime'].date() %>
+
+        % if run_time:
+            <% cur_ep_enddate = cur_result['localtime'] + datetime.timedelta(minutes = run_time) %>
+            % if cur_ep_enddate < today:
+                <% show_div = 'ep_listing listing-overdue' %>
+            % elif cur_ep_airdate >= next_week.date():
+                <% show_div = 'ep_listing listing-toofar' %>
+            % elif cur_ep_enddate >= today and cur_ep_airdate < next_week.date():
+                % if cur_ep_airdate == today.date():
+                    <% show_div = 'ep_listing listing-current' %>
+                % else:
+                    <% show_div = 'ep_listing listing-default' %>
+                % endif
+            % endif
+        % endif
+    % elif 'date' == sort:
+        <% cur_ep_airdate = cur_result['localtime'].date() %>
+
+        % if cur_segment != cur_ep_airdate:
+            %if run_time:
+                <% cur_ep_enddate = cur_result['localtime'] + datetime.timedelta(minutes = run_time) %>
+                % if cur_ep_enddate < today and cur_ep_airdate != today.date() and not missed_header:
+                        <br /><h2 class="day">Missed</h2>
+                <% missed_header = True %>
+                % elif cur_ep_airdate >= next_week.date() and not too_late_header:
+                        <br /><h2 class="day">Later</h2>
+                <% too_late_header = True %>
+                % elif cur_ep_enddate >= today and cur_ep_airdate < next_week.date():
+                    % if cur_ep_airdate == today.date():
+                        <br /><h2 class="day">${datetime.date.fromordinal(cur_ep_airdate.toordinal()).strftime('%A').decode(sickbeard.SYS_ENCODING).capitalize()}<span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
+                        <% today_header = True %>
+                    % else:
+                        <br /><h2 class="day">${datetime.date.fromordinal(cur_ep_airdate.toordinal()).strftime('%A').decode(sickbeard.SYS_ENCODING).capitalize()}</h2>
+                    % endif
+                % endif
+            % endif
+                <% cur_segment = cur_ep_airdate %>
+        % endif
+
+        % if cur_ep_airdate == today.date() and not today_header:
+            <div class="comingepheader">
+            <br /><h2 class="day">${datetime.date.fromordinal(cur_ep_airdate.toordinal()).strftime('%A').decode(sickbeard.SYS_ENCODING).capitalize()} <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
+            <% today_header = True %>
+        % endif
+        % if run_time:
+            % if cur_ep_enddate < today:
+                <% show_div = 'ep_listing listing-overdue' %>
+            % elif cur_ep_airdate >= next_week.date():
+                <% show_div = 'ep_listing listing-toofar' %>
+            % elif cur_ep_enddate >= today and cur_ep_airdate < next_week.date():
+                % if cur_ep_airdate == today.date():
+                    <% show_div = 'ep_listing listing-current' %>
+                % else:
+                    <% show_div = 'ep_listing listing-default'%>
+                % endif
+            % endif
+        % endif
+
+        % elif 'show' == sort:
+            <% cur_ep_airdate = cur_result['localtime'].date() %>
+
+            % if run_time:
+                <% cur_ep_enddate = cur_result['localtime'] + datetime.timedelta(minutes = run_time) %>
+                % if cur_ep_enddate < today:
+                    <% show_div = 'ep_listing listing-overdue listingradius' %>
+                % elif cur_ep_airdate >= next_week.date():
+                    <% show_div = 'ep_listing listing-toofar listingradius' %>
+                % elif cur_ep_enddate >= today and cur_ep_airdate < next_week.date():
+                    % if cur_ep_airdate == today.date():
+                        <% show_div = 'ep_listing listing-current listingradius' %>
+                    % else:
+                        <% show_div = 'ep_listing listing-default listingradius' %>
+                    % endif
+                % endif
+            % endif
+        % endif
+
+<div class="${show_div}" id="listing-${cur_result['showid']}">
+    <div class="tvshowDiv">
+        <table width="100%" border="0" cellpadding="0" cellspacing="0">
+        <tr>
+            <th ${('class="nobg"', 'rowspan="2"')[banner == layout]} valign="top">
+                <a href="${sbRoot}/home/displayShow?show=${cur_result['showid']}"><img alt="" class="${('posterThumb', 'bannerThumb')[layout == 'banner']}" src="${sbRoot}/showPoster/?show=${cur_result['showid']}&amp;which=${(layout, 'poster_thumb')[layout == 'poster']}" /></a>
+            </th>
+% if 'banner' == layout:
+        </tr>
+        <tr>
+% endif
+
+            <td class="next_episode">
+                <div class="clearfix">
+                    <span class="tvshowTitle">
+                        <a href="${sbRoot}/home/displayShow?show=${cur_result['showid']}">${cur_result['show_name']}
+                    % if int(cur_result['paused']):
+                        <span class="pause">[paused]</span>
+                    % endif
+                    </a></span>
+
+                    <span class="tvshowTitleIcons">
+% if cur_result['imdb_id']:
+                        <a href="${anon_url('http://www.imdb.com/title/', cur_result['imdb_id'])}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="${sbRoot}/images/imdb.png" />
+% endif
+                        <a href="${anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid'])}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="${sickbeard.indexerApi(cur_indexer).config['show_url']}"><img alt="${sickbeard.indexerApi(cur_indexer).name}" height="16" width="16" src="${sbRoot}/images/${sickbeard.indexerApi(cur_indexer).config['icon']}" /></a>
+                        <span><a href="${sbRoot}/home/searchEpisode?show=${cur_result['showid']}&amp;season=${cur_result['season']}&amp;episode=${cur_result['episode']}" title="Manual Search" id="forceUpdate-${cur_result['showid']}" class="epSearch forceUpdate"><img alt="[search]" height="16" width="16" src="${sbRoot}/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a></span>
+                    </span>
+                </div>
+
+                <span class="title">Next Episode:</span> <span>${'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode']))} - ${cur_result['name']}</span>
+
+                <div class="clearfix">
+
+                    <span class="title">Airs: </span><span class="${fuzzydate}">${sbdatetime.sbdatetime.sbfdatetime(cur_result['localtime']).decode(sickbeard.SYS_ENCODING)}</span>${('', '<span> on %s</span>' % str(cur_result['network']))[bool(cur_result['network'])]}
+                </div>
+
+                <div class="clearfix">
+                    <span class="title">Quality:</span>
+                    % if int(cur_result['quality']) in qualityPresets:
+                        <span class="quality ${qualityPresetStrings[int(cur_result['quality'])]}">${qualityPresetStrings[int(cur_result['quality'])]}</span>
+                    % else:
+                        <span class="quality Custom">Custom</span>
+                    % endif
+                </div>
+            </td>
+        </tr>
+        <tr>
+            <td style="vertical-align: top;">
+                <div>
+% if cur_result['description']:
+                        <span class="title" style="vertical-align:middle;">Plot:</span>
+                        <img class="ep_summaryTrigger" src="${sbRoot}/images/plus.png" height="16" width="16" alt="" title="Toggle Summary" /><div class="ep_summary">${cur_result['description']}</div>
+% else:
+                        <span class="title ep_summaryTriggerNone" style="vertical-align:middle;">Plot:</span>
+                        <img class="ep_summaryTriggerNone" src="${sbRoot}/images/plus.png" height="16" width="16" alt="" />
+% endif
+                </div>
+        </td>
+        </tr>
+        </table>
+    </div>
+</div>
+
+<!-- end ${cur_result['show_name']} //-->
+% endfor
+
+<!-- end non list view //-->
+% endif
+
+% if 'calendar' == layout:
+
+##<%today = datetime.date.today()%>
+<% dates = [today + datetime.timedelta(days = i) for i in range(7)] %>
+<% tbl_day = 0 %>
+<br>
+<br>
+<div class="calendarWrapper">
+<input type="hidden" id="sbRoot" value="${sbRoot}" />
+    % for day in dates:
+    <% tbl_day += 1 %>
+        <table class="sickbeardTable tablesorter calendarTable ${'cal-%s' % (('even', 'odd')[bool(tbl_day % 2)])}" cellspacing="0" border="0" cellpadding="0">
+        <thead><tr><th>${day.strftime('%A').decode(sickbeard.SYS_ENCODING).capitalize()}</th></tr></thead>
+        <tbody>
+        <% day_has_show = False %>
+        % for cur_result in sql_results:
+            % if int(cur_result['paused']) and not sickbeard.COMING_EPS_DISPLAY_PAUSED:
+                <% continue %>
+            % endif
+
+            <% cur_indexer = int(cur_result['indexer']) %>
+            <% run_time = cur_result['runtime'] %>
+            <% airday = cur_result['localtime'].date() %>
+
+            % if airday == day:
+                % try:
+                    <% day_has_show = True %>
+                    <% airtime = sbdatetime.sbdatetime.fromtimestamp(time.mktime(cur_result['localtime'].timetuple())).sbftime().decode(sickbeard.SYS_ENCODING) %>
+                    % if sickbeard.TRIM_ZERO:
+                        <% airtime = re.sub(r'0(\d:\d\d)', r'\1', airtime, 0, re.IGNORECASE | re.MULTILINE) %>
+                    % endif
+                % except OverflowError:
+                    <% airtime = "Invalid" %>
+                % endtry
+
+                <tr>
+                    <td class="calendarShow">
+                        <div class="poster">
+                            <a title="${cur_result['show_name']}" href="${sbRoot}/home/displayShow?show=${cur_result['showid']}"><img alt="" src="${sbRoot}/showPoster/?show=${cur_result['showid']}&amp;which=poster_thumb" /></a>
+                        </div>
+                        <div class="text">
+                            <span class="airtime">
+                                ${airtime} on ${cur_result["network"]}
+                            </span>
+                            <span class="episode-title" title="${cur_result['name']}">
+                                ${'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode']))} - ${cur_result['name']}
+                            </span>
+                        </div>
+                    </td> <!-- end ${cur_result['show_name']} -->
+                </tr>
+            % endif
+
+        % endfor
+        % if not day_has_show:
+            <tr><td class="calendarShow"><span class="show-status">No shows for this day</span></td></tr>
+        % endif
+        </tbody>
+        </table>
+    % endfor
+
+<!-- end calender view //-->
+</div>
+% endif
+
+<div class="clearfix"></div>
+
+<script type="text/javascript" charset="utf-8">
+<!--
+window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
+//-->
+</script>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/comingEpisodes.tmpl b/gui/slick/interfaces/default/comingEpisodes.tmpl
deleted file mode 100644
index 099c4740d5e3035b40cf7ccdc6365efd34d88eb4..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/comingEpisodes.tmpl
+++ /dev/null
@@ -1,551 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard.common import *
-#from sickbeard import sbdatetime
-#from sickbeard.helpers import anon_url
-
-#set global $title = 'Coming Episodes'
-#set global $header = 'Coming Episodes'
-
-#set global $sbPath = '..'
-
-#set global $topmenu = 'comingEpisodes'
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
-#set $sort = $sickbeard.COMING_EPS_SORT
-<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script>
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<style type="text/css">
-#SubMenu {display:none}
-#contentWrapper {padding-top:30px}
-</style>
-
-<div class="h2footer pull-right">
-    <span>Layout:
-        <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-            <option value="$sbRoot/setComingEpsLayout/?layout=poster" #if 'poster' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>Poster</option>
-            <option value="$sbRoot/setComingEpsLayout/?layout=calendar" #if 'calendar' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>Calendar</option>
-            <option value="$sbRoot/setComingEpsLayout/?layout=banner" #if 'banner' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>Banner</option>
-            <option value="$sbRoot/setComingEpsLayout/?layout=list" #if 'list' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>List</option>
-        </select>
-    </span>
-    &nbsp;
-
-    <span>Sort By:
-        <select name="sort" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-            <option value="$sbRoot/setComingEpsSort/?sort=date" #if 'date' == $sickbeard.COMING_EPS_SORT then 'selected="selected"' else ''#>Date</option>
-            <option value="$sbRoot/setComingEpsSort/?sort=network" #if 'network' == $sickbeard.COMING_EPS_SORT then 'selected="selected"' else ''#>Network</option>
-            <option value="$sbRoot/setComingEpsSort/?sort=show" #if 'show' == $sickbeard.COMING_EPS_SORT then 'selected="selected"' else ''#>Show</option>
-        </select>
-    </span>
-    &nbsp;
-
-    <span>View Paused:
-        <select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-            <option value="$sbRoot/toggleComingEpsDisplayPaused"<%= (' selected="selected"', '')[True == sickbeard.COMING_EPS_DISPLAY_PAUSED] %>>Hidden</option>
-            <option value="$sbRoot/toggleComingEpsDisplayPaused"<%= ('', ' selected="selected"')[True == sickbeard.COMING_EPS_DISPLAY_PAUSED] %>>Shown</option>
-        </select>
-    </span>
-</div>
-
-<div class="key pull-right">
-#if 'calendar' != $layout:
-    <b>Key:</b>
-    <span class="listing-key listing-overdue">Missed</span>
-    <span class="listing-key listing-current">Current</span>
-    <span class="listing-key listing-default">Future</span>
-    <span class="listing-key listing-toofar">Distant</span>
-#end if
-    <a class="btn btn-inline forceBacklog" href="webcal://$sbHost:$sbHttpPort/calendar">
-    <i class="icon-calendar icon-white"></i>Subscribe</a>
-</div>
-
-<br>
-
-#if 'list' == $layout:
-<!-- start list view //-->
-
-<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
-<script type="text/javascript" charset="utf-8">
-<!--
-\$.tablesorter.addParser({
-    id: 'loadingNames',
-    is: function(s) {
-        return false
-    },
-    format: function(s) {
-        if (0 == s.indexOf('Loading...'))
-            return s.replace('Loading...', '000')
-#if not $sickbeard.SORT_ARTICLE:
-            return (s || '').replace(/^(The|A|An)\s/i, '')
-#else:
-            return (s || '')
-#end if
-    },
-    type: 'text'
-});
-\$.tablesorter.addParser({
-    id: 'quality',
-    is: function(s) {
-        return false
-    },
-    format: function(s) {
-        return s.replace('hd1080p', 5).replace('hd720p', 4).replace('hd', 3).replace('sd', 2).replace('any', 1).replace('best', 0).replace('custom', 7)
-    },
-    type: 'numeric'
-});
-\$.tablesorter.addParser({
-    id: 'cDate',
-    is: function(s) {
-        return false
-    },
-    format: function(s) {
-        return s
-    },
-    type: 'numeric'
-});
-
-\$(document).ready(function(){
-
-#set $sort_codes = {'date': 0, 'show': 1, 'network': 4}
-#if $sort not in $sort_codes:
-    $sort = 'date'
-#end if
-
-    sortList = [[$sort_codes[$sort], 0]];
-
-    \$('#showListTable:has(tbody tr)').tablesorter({
-        widgets: ['stickyHeaders'],
-        sortList: sortList,
-        textExtraction: {
-            0: function(node) { return \$(node).find('span').text().toLowerCase() },
-            5: function(node) { return \$(node).find('span').text().toLowerCase() }
-        },
-        headers: {
-            0: { sorter: 'cDate' },
-            1: { sorter: 'loadingNames' },
-            2: { sorter: false },
-            3: { sorter: false },
-            4: { sorter: 'loadingNames' },
-            5: { sorter: 'quality' },
-            6: { sorter: false },
-            7: { sorter: false },
-            8: { sorter: false }
-        }
-    });
-
-    \$('#sbRoot').ajaxEpSearch();
-
-    #set $fuzzydate = 'airdate'
-    #if $sickbeard.FUZZY_DATING:
-    fuzzyMoment({
-        containerClass : '.${fuzzydate}',
-        dateHasTime : true,
-        dateFormat : '${sickbeard.DATE_PRESET}',
-        timeFormat : '${sickbeard.TIME_PRESET}',
-        trimZero : #if $sickbeard.TRIM_ZERO then 'true' else 'false'#
-    });
-    #end if
-
-});
-//-->
-</script>
-
-#set $show_div = 'listing-default'
-
-<input type="hidden" id="sbRoot" value="$sbRoot" />
-
-<table id="showListTable" class="sickbeardTable tablesorter seasonstyle" cellspacing="1" border="0" cellpadding="0">
-
-    <thead>
-        <tr>
-            <th>Airdate</th>
-            <th>Show</th>
-            <th nowrap="nowrap">Next Ep</th>
-            <th>Next Ep Name</th>
-            <th>Network</th>
-            <th>Quality</th>
-            <th>Indexers</th>
-            <th>Search</th>
-        </tr>
-    </thead>
-
-    <tbody style="text-shadow:none;">
-
-#for $cur_result in $sql_results:
-    #set $cur_indexer = int($cur_result['indexer'])
-    #set $runtime = $cur_result['runtime']
-
-    #if int($cur_result['paused']) and not $sickbeard.COMING_EPS_DISPLAY_PAUSED:
-        #continue
-    #end if
-
-    #set $cur_ep_airdate = $cur_result['localtime'].date()
-
-    #if $runtime:
-        #set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
-        #if $cur_ep_enddate < $today:
-            #set $show_div = 'listing-overdue'
-        #elif $cur_ep_airdate >= $next_week.date():
-            #set $show_div = 'listing-toofar'
-        #elif $cur_ep_airdate >= $today.date() and $cur_ep_airdate < $next_week.date():
-            #if $cur_ep_airdate == $today.date():
-                #set $show_div = 'listing-current'
-            #else:
-                #set $show_div = 'listing-default'
-            #end if
-        #end if
-    #end if
-
-        <!-- start $cur_result['show_name'] //-->
-        <tr class="$show_div">
-            ## forced to use a div to wrap airdate, the column sort went crazy with a span
-            <td align="center" nowrap="nowrap">
-                <div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</div><span class="sort_data">$time.mktime($cur_result['localtime'].timetuple())</span>
-            </td>
-
-            <td class="tvShow" nowrap="nowrap"><a href="$sbRoot/home/displayShow?show=${cur_result['showid']}">$cur_result['show_name']</a>
-#if int($cur_result['paused']):
-                <span class="pause">[paused]</span>
-#end if
-            </td>
-
-            <td nowrap="nowrap" align="center">
-                <%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %>
-            </td>
-
-            <td>
-#if $cur_result['description']:
-                <img alt='' src='$sbRoot/images/info32.png' height='16' width='16' class='plotInfo' id="plot_info_<%= '%s_%s_%s' % (str(cur_result['showid']), str(cur_result['season']), str(cur_result['episode'])) %>" />
-#else:
-                <img alt="" src="$sbRoot/images/info32.png" width="16" height="16" class="plotInfoNone"  />
-#end if
-                $cur_result['name']
-            </td>
-
-            <td align="center">
-                $cur_result['network']
-            </td>
-
-            <td align="center">
-#if int($cur_result['quality']) in $qualityPresets:
-                <span class="quality $qualityPresetStrings[int($cur_result['quality'])]">$qualityPresetStrings[int($cur_result['quality'])]</span>
-#else:
-                <span class="quality Custom">Custom</span>
-#end if
-            </td>
-
-            <td align="center" style="vertical-align: middle;">
-#if $cur_result['imdb_id']:
-                <a href="<%= anon_url('http://www.imdb.com/title/', cur_result['imdb_id']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" />
-#end if
-                <a href="<%= anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="$sickbeard.indexerApi($cur_indexer).config['show_url']${cur_result['showid']}"><img alt="$sickbeard.indexerApi($cur_indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($cur_indexer).config['icon']" /></a>
-            </td>
-
-            <td align="center">
-                <a href="$sbRoot/home/searchEpisode?show=${cur_result['showid']}&amp;season=$cur_result['season']&amp;episode=$cur_result['episode']" title="Manual Search" id="forceUpdate-${cur_result['showid']}x${cur_result['season']}x${cur_result['episode']}" class="forceUpdate epSearch"><img alt="[search]" height="16" width="16" src="$sbRoot/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a>
-            </td>
-        </tr>
-        <!-- end $cur_result['show_name'] //-->
-#end for
-    </tbody>
-
-    <tfoot>
-        <tr>
-            <th rowspan="1" colspan="10" align="center">&nbsp</th>
-        </tr>
-    </tfoot>
-
-</table>
-<!-- end list view //-->
-
-
-#else if $layout in ['banner', 'poster']:
-
-
-<!-- start non list view //-->
-<script type="text/javascript" charset="utf-8">
-<!--
-\$(document).ready(function(){
-    \$('#sbRoot').ajaxEpSearch({'size': 16, 'loadingImage': 'loading16' + themeSpinner + '.gif'});
-    \$('.ep_summary').hide();
-    \$('.ep_summaryTrigger').click(function() {
-        \$(this).next('.ep_summary').slideToggle('normal', function() {
-            \$(this).prev('.ep_summaryTrigger').attr('src', function(i, src) {
-                return \$(this).next('.ep_summary').is(':visible') ? src.replace('plus','minus') : src.replace('minus','plus')
-            });
-        });
-    });
-
-    #set $fuzzydate = 'airdate'
-    #if $sickbeard.FUZZY_DATING:
-    fuzzyMoment({
-        dtInline : true,
-        dtGlue : ' at ',
-        containerClass : '.${fuzzydate}',
-        dateHasTime : true,
-        dateFormat : '${sickbeard.DATE_PRESET}',
-        timeFormat : '${sickbeard.TIME_PRESET}',
-        trimZero : #if $sickbeard.TRIM_ZERO then 'true' else 'false'#
-    });
-    #end if
-
-});
-//-->
-</script>
-
-#set $cur_segment = None
-#set $too_late_header = False
-#set $missed_header = False
-#set $today_header = False
-#set $show_div = 'ep_listing listing-default'
-
-#if 'show' == $sort:
-    <br /><br />
-#end if
-
-#for $cur_result in $sql_results:
-    #set $cur_indexer = int($cur_result['indexer'])
-
-<!-- start $cur_result['show_name'] //-->
-
-    #if int($cur_result['paused']) and not $sickbeard.COMING_EPS_DISPLAY_PAUSED:
-        #continue
-    #end if
-
-    #set $runtime = $cur_result['runtime']
-
-    #if 'network' == $sort:
-        #set $show_network = $cur_result['network'] if $cur_result['network'] else 'no network'
-        #if $cur_segment != $show_network:
-            <div class="comingepheader">
-                <br><h2 class="network">$show_network</h2>
-            #set $cur_segment = $cur_result['network']
-        #end if
-        #set $cur_ep_airdate = $cur_result['localtime'].date()
-
-        #if $runtime:
-            #set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
-            #if $cur_ep_enddate < $today:
-                #set $show_div = 'ep_listing listing-overdue'
-            #elif $cur_ep_airdate >= $next_week.date():
-                #set $show_div = 'ep_listing listing-toofar'
-            #elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
-                #if $cur_ep_airdate == $today.date():
-                #set $show_div = 'ep_listing listing-current'
-                    #else:
-                        #set $show_div = 'ep_listing listing-default'
-                    #end if
-                #end if
-            #end if
-
-    #elif 'date' == $sort:
-        #set $cur_ep_airdate = $cur_result['localtime'].date()
-
-        #if $cur_segment != $cur_ep_airdate:
-            #if $runtime:
-                #set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
-                #if $cur_ep_enddate < $today and $cur_ep_airdate != $today.date() and not $missed_header:
-                        <br /><h2 class="day">Missed</h2>
-                #set $missed_header = True
-                #elif $cur_ep_airdate >= $next_week.date() and not $too_late_header:
-                        <br /><h2 class="day">Later</h2>
-                #set $too_late_header = True
-                #elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
-                    #if $cur_ep_airdate == $today.date():
-                        <br /><h2 class="day">$datetime.date.fromordinal($cur_ep_airdate.toordinal).strftime('%A').decode($sickbeard.SYS_ENCODING).capitalize() <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
-                    #set $today_header = True
-                    #else:
-                        <br /><h2 class="day">$datetime.date.fromordinal($cur_ep_airdate.toordinal).strftime('%A').decode($sickbeard.SYS_ENCODING).capitalize()</h2>
-                    #end if
-                #end if
-            #end if
-                #set $cur_segment = $cur_ep_airdate
-        #end if
-
-        #if $cur_ep_airdate == $today.date() and not $today_header:
-            <div class="comingepheader">
-            <br /><h2 class="day">$datetime.date.fromordinal($cur_ep_airdate.toordinal).strftime('%A').decode($sickbeard.SYS_ENCODING).capitalize() <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
-            #set $today_header = True
-        #end if
-        #if $runtime:
-            #if $cur_ep_enddate < $today:
-                #set $show_div = 'ep_listing listing-overdue'
-            #elif $cur_ep_airdate >= $next_week.date():
-                #set $show_div = 'ep_listing listing-toofar'
-            #elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
-                #if $cur_ep_airdate == $today.date():
-                    #set $show_div = 'ep_listing listing-current'
-                #else:
-                    #set $show_div = 'ep_listing listing-default'
-                #end if
-            #end if
-        #end if
-
-        #elif 'show' == $sort:
-            #set $cur_ep_airdate = $cur_result['localtime'].date()
-
-            #if $runtime:
-                #set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
-                #if $cur_ep_enddate < $today:
-                    #set $show_div = 'ep_listing listing-overdue listingradius'
-                #elif $cur_ep_airdate >= $next_week.date():
-                    #set $show_div = 'ep_listing listing-toofar listingradius'
-                #elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
-                    #if $cur_ep_airdate == $today.date():
-                        #set $show_div = 'ep_listing listing-current listingradius'
-                    #else:
-                        #set $show_div = 'ep_listing listing-default listingradius'
-                    #end if
-                #end if
-            #end if
-        #end if
-
-<div class="$show_div" id="listing-${cur_result['showid']}">
-    <div class="tvshowDiv">
-        <table width="100%" border="0" cellpadding="0" cellspacing="0">
-        <tr>
-            <th #if 'banner' == $layout then 'class="nobg"' else 'rowspan="2"'# valign="top">
-                <a href="$sbRoot/home/displayShow?show=${cur_result['showid']}"><img alt="" class="#if 'banner' == $layout then 'bannerThumb' else 'posterThumb'#" src="$sbRoot/showPoster/?show=${cur_result['showid']}&amp;which=#if 'poster' == $layout then 'poster_thumb' else $layout#" /></a>
-            </th>
-#if 'banner' == $layout:
-        </tr>
-        <tr>
-#end if
-
-            <td class="next_episode">
-                <div class="clearfix">
-                    <span class="tvshowTitle">
-                        <a href="$sbRoot/home/displayShow?show=${cur_result['showid']}">$cur_result['show_name']
-                    #if int($cur_result['paused']):
-                        <span class="pause">[paused]</span>
-                    #end if
-                    </a></span>
-
-                    <span class="tvshowTitleIcons">
-#if $cur_result['imdb_id']:
-                        <a href="<%= anon_url('http://www.imdb.com/title/', cur_result['imdb_id']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" />
-#end if
-                        <a href="<%= anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="$sickbeard.indexerApi($cur_indexer).config['show_url']${cur_result['showid']}"><img alt="$sickbeard.indexerApi($cur_indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($cur_indexer).config['icon']" /></a>
-                        <span><a href="$sbRoot/home/searchEpisode?show=${cur_result['showid']}&amp;season=$cur_result['season']&amp;episode=$cur_result['episode']" title="Manual Search" id="forceUpdate-${cur_result['showid']}" class="epSearch forceUpdate"><img alt="[search]" height="16" width="16" src="$sbRoot/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a></span>
-                    </span>
-                </div>
-
-                <span class="title">Next Episode:</span> <span><%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %> - $cur_result['name']</span>
-
-                <div class="clearfix">
-
-                    <span class="title">Airs: </span><span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</span><%= ('', '<span> on %s</span>' % str(cur_result['network']))[None is not cur_result['network']] %>
-                </div>
-
-                <div class="clearfix">
-                    <span class="title">Quality:</span>
-                    #if int($cur_result['quality']) in $qualityPresets:
-                        <span class="quality $qualityPresetStrings[int($cur_result['quality'])]">$qualityPresetStrings[int($cur_result['quality'])]</span>
-                    #else:
-                        <span class="quality Custom">Custom</span>
-                    #end if
-                </div>
-            </td>
-        </tr>
-        <tr>
-            <td style="vertical-align: top;">
-                <div>
-#if $cur_result['description']:
-                        <span class="title" style="vertical-align:middle;">Plot:</span>
-                        <img class="ep_summaryTrigger" src="$sbRoot/images/plus.png" height="16" width="16" alt="" title="Toggle Summary" /><div class="ep_summary">$cur_result['description']</div>
-#else:
-                        <span class="title ep_summaryTriggerNone" style="vertical-align:middle;">Plot:</span>
-                        <img class="ep_summaryTriggerNone" src="$sbRoot/images/plus.png" height="16" width="16" alt="" />
-#end if
-                </div>
-        </td>
-        </tr>
-        </table>
-    </div>
-</div>
-
-<!-- end $cur_result['show_name'] //-->
-#end for
-
-<!-- end non list view //-->
-#end if
-
-#if 'calendar' == $layout:
-
-#set $today = datetime.date.today()
-#set $dates = [$today + datetime.timedelta(days = $i) for $i in range(7)]
-#set $tbl_day = 0
-<br>
-<br>
-<div class="calendarWrapper">
-<input type="hidden" id="sbRoot" value="$sbRoot" />
-    #for $day in $dates
-    #set $tbl_day += 1
-        <table class="sickbeardTable tablesorter calendarTable <%= 'cal-%s' % (('even', 'odd')[1 == tbl_day % 2]) %>" cellspacing="0" border="0" cellpadding="0">
-        <thead><tr><th>$day.strftime('%A').decode($sickbeard.SYS_ENCODING).capitalize()</th></tr></thead>
-        <tbody>
-        #set $day_has_show = False
-        #for $cur_result in $sql_results:
-            #if int($cur_result['paused']) and not $sickbeard.COMING_EPS_DISPLAY_PAUSED:
-                #continue
-            #end if
-
-            #set $cur_indexer = int($cur_result['indexer'])
-            #set $runtime = $cur_result['runtime']
-            #set $airday = $cur_result['localtime'].date()
-
-            #if $airday == $day:
-                #try
-                    #set $day_has_show = True
-                    #set $airtime = $sbdatetime.sbdatetime.fromtimestamp($time.mktime($cur_result['localtime'].timetuple())).sbftime().decode($sickbeard.SYS_ENCODING)
-                    #if $sickbeard.TRIM_ZERO:
-                    #set $airtime = re.sub(r'0(\d:\d\d)', r'\1', $airtime, 0, re.IGNORECASE | re.MULTILINE)
-                    #end if
-                #except OverflowError
-                    #set $airtime = "Invalid"
-                #end try
-
-                <tr>
-                    <td class="calendarShow">
-                        <div class="poster">
-                            <a title="${cur_result['show_name']}" href="$sbRoot/home/displayShow?show=${cur_result['showid']}"><img alt="" src="$sbRoot/showPoster/?show=${cur_result['showid']}&amp;which=poster_thumb" /></a>
-                        </div>
-                        <div class="text">
-                            <span class="airtime">
-                                ${airtime} on $cur_result["network"]
-                            </span>
-                            <span class="episode-title" title="$cur_result['name']">
-                                <%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %> - $cur_result['name']
-                            </span>
-                        </div>
-                    </td> <!-- end $cur_result['show_name'] -->
-                </tr>
-            #end if
-
-        #end for
-        #if not $day_has_show:
-            <tr><td class="calendarShow"><span class="show-status">No shows for this day</span></td></tr>
-        #end if
-        </tbody>
-        </table>
-    #end for
-
-<!-- end calender view //-->
-</div>
-#end if
-
-<div class="clearfix"></div>
-
-<script type="text/javascript" charset="utf-8">
-<!--
-window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
-//-->
-</script>
-
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
diff --git a/gui/slick/interfaces/default/config.mako b/gui/slick/interfaces/default/config.mako
new file mode 100644
index 0000000000000000000000000000000000000000..82b327f83b5bec3c6e18b0b9d5deb4c0b047a506
--- /dev/null
+++ b/gui/slick/interfaces/default/config.mako
@@ -0,0 +1,72 @@
+<%!
+    import sickbeard
+    from sickbeard import db
+    from sickbeard.helpers import anon_url
+    import sys, os
+%>
+<%include file="/inc_top.mako"/>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+##cpu_usage = ${psutil.cpu_percent()}
+##ram = ${psutil.phymem_usage()}
+##ram_total = ${ram.total / 2**20}
+##ram_used = ${ram.used / 2**20}
+##ram_free = ${ram.free / 2**20}
+##ram_percent_used = ${ram.percent}
+##disk = ${psutil.disk_usage('/')}
+##disk_total = ${disk.total / 2**30}
+##disk_used = ${disk.used / 2**30}
+##disk_free = ${disk.free / 2**30}
+##disk_percent_used = ${disk.percent}
+<div id="config-content">
+<table class="infoTable" cellspacing="1" border="0" cellpadding="0" width="100%">
+    <tr><td class="infoTableHeader">SR Version: </td><td class="infoTableCell">
+% if sickbeard.VERSION_NOTIFY:
+        BRANCH: (${sickbeard.BRANCH}) / COMMIT: (${sickbeard.CUR_COMMIT_HASH}) <!-- &ndash; build.date //--><br />
+% else:
+        You don't have version checking turned on. Please turn on "Check for Update" in Config > General.<br />
+% endif
+    </td></tr>
+
+<%
+    sr_user = None
+    try:
+        import pwd
+        sr_user = pwd.getpwuid(os.getuid()).pw_name
+    except ImportError:
+        import getpass
+        sr_user = getpass.getuser()
+%>
+% if sr_user:
+    <tr><td class="infoTableHeader">SR User:</td><td class="infoTableCell">${sr_user}</td></tr>
+% endif
+
+% try:
+    <% import locale %>
+    <% sr_locale = locale.getdefaultlocale() %>
+    <tr><td class="infoTableHeader">SR Locale:</td><td class="infoTableCell">${sr_locale}</td></tr>
+% except:
+    ""
+% endtry
+
+    <tr><td class="infoTableHeader">SR Config file:</td><td class="infoTableCell">${sickbeard.CONFIG_FILE}</td></tr>
+    <tr><td class="infoTableHeader">SR Database file:</td><td class="infoTableCell">${db.dbFilename()}</td></tr>
+    <tr><td class="infoTableHeader">SR Cache Dir:</td><td class="infoTableCell">${sickbeard.CACHE_DIR}</td></tr>
+    <tr><td class="infoTableHeader">SR Log Dir:</td><td class="infoTableCell">${sickbeard.LOG_DIR}</td></tr>
+    <tr><td class="infoTableHeader">SR Arguments:</td><td class="infoTableCell">${sickbeard.MY_ARGS}</td></tr>
+% if sickbeard.WEB_ROOT:
+    <tr><td class="infoTableHeader">SR Web Root:</td><td class="infoTableCell">${sickbeard.WEB_ROOT}</td></tr>
+% endif
+    <tr><td class="infoTableHeader">Python Version:</td><td class="infoTableCell">${sys.version[:120]}</td></tr>
+    <tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sb"></i> Homepage</td><td class="infoTableCell"><a href="${anon_url('http://www.sickrage.tv/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">http://www.sickrage.tv/</a></td></tr>
+    <tr><td class="infoTableHeader"><i class="icon16-web"></i> Forums</td><td class="infoTableCell"><a href="${anon_url('http://sickrage.tv/forums/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">http://sickrage.tv/forums/</a></td></tr>
+    <tr><td class="infoTableHeader"><i class="icon16-github"></i> Source</td><td class="infoTableCell"><a href="${anon_url('https://github.com/SiCKRAGETV/SickRage/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SiCKRAGETV/SickRage/</a></td></tr>
+    <tr><td class="infoTableHeader"><i class="icon16-mirc"></i> Internet Relay Chat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#sickrage" rel="noreferrer"><i>#sickrage</i> on <i>irc.freenode.net</i></a></td></tr>
+</table>
+</div>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config.tmpl b/gui/slick/interfaces/default/config.tmpl
deleted file mode 100644
index fecddf2dbb4bff73509f8d49c6ad63e690c3a82f..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/config.tmpl
+++ /dev/null
@@ -1,82 +0,0 @@
-#compiler-settings
-useLegacyImportMode = False
-#end compiler-settings
-
-#import sickbeard
-#from sickbeard import db
-#from sickbeard.helpers import anon_url
-#import os.path
-
-#set global $title="Configuration"
-#set global $header="Configuration"
-
-#set global $sbPath=".."
-
-#set global $topmenu="config"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-##set cpu_usage = $psutil.cpu_percent()
-##set ram = $psutil.phymem_usage()
-##set ram_total = $ram.total / 2**20
-##set ram_used = $ram.used / 2**20
-##set ram_free = $ram.free / 2**20
-##set ram_percent_used = $ram.percent
-##set disk = $psutil.disk_usage('/')
-##set disk_total = $disk.total / 2**30
-##set disk_used = $disk.used / 2**30
-##set disk_free = $disk.free / 2**30
-##set disk_percent_used = $disk.percent
-
-<div id="config-content">
-<table class="infoTable" cellspacing="1" border="0" cellpadding="0" width="100%">
-    <tr><td class="infoTableHeader">SR Version: </td><td class="infoTableCell">
-#if $sickbeard.VERSION_NOTIFY
-        BRANCH: ($sickbeard.BRANCH) / COMMIT: ($sickbeard.CUR_COMMIT_HASH) <!-- &ndash; build.date //--><br />
-#else
-        You don't have version checking turned on. Please turn on "Check for Update" in Config > General.<br />
-#end if
-    </td></tr>
-
-#set $sr_user = None
-#try
-#import pwd
-#set $sr_user = $pwd.getpwuid(os.getuid()).pw_name
-#except ImportError
-#import getpass
-#set $sr_user = $getpass.getuser()
-#end try
-#if $sr_user:
-    <tr><td class="infoTableHeader">SR User:</td><td class="infoTableCell">$sr_user</td></tr>
-#end if
-
-#try
-#import locale
-#set $sr_locale = $locale.getdefaultlocale()
-    <tr><td class="infoTableHeader">SR Locale:</td><td class="infoTableCell">$sr_locale</td></tr>
-#except
-#pass
-#end try
-
-    <tr><td class="infoTableHeader">SR Config file:</td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr>
-    <tr><td class="infoTableHeader">SR Database file:</td><td class="infoTableCell">$db.dbFilename()</td></tr>
-    <tr><td class="infoTableHeader">SR Cache Dir:</td><td class="infoTableCell">$sickbeard.CACHE_DIR</td></tr>
-    <tr><td class="infoTableHeader">SR Log Dir:</td><td class="infoTableCell">$sickbeard.LOG_DIR</td></tr>
-    <tr><td class="infoTableHeader">SR Arguments:</td><td class="infoTableCell">$sickbeard.MY_ARGS</td></tr>
-#if $sickbeard.WEB_ROOT
-    <tr><td class="infoTableHeader">SR Web Root:</td><td class="infoTableCell">$sickbeard.WEB_ROOT</td></tr>
-#end if
-    <tr><td class="infoTableHeader">Python Version:</td><td class="infoTableCell">$sys.version[:120]</td></tr>
-    <tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sb"></i> Homepage</td><td class="infoTableCell"><a href="<%= anon_url('http://www.sickrage.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">http://www.sickrage.tv/</a></td></tr>
-    <tr><td class="infoTableHeader"><i class="icon16-web"></i> Forums</td><td class="infoTableCell"><a href="<%= anon_url('http://sickrage.tv/forums/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">http://sickrage.tv/forums/</a></td></tr>
-    <tr><td class="infoTableHeader"><i class="icon16-github"></i> Source</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SiCKRAGETV/SickRage/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SiCKRAGETV/SickRage/</a></td></tr>
-    <tr><td class="infoTableHeader"><i class="icon16-mirc"></i> Internet Relay Chat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#sickrage" rel="noreferrer"><i>#sickrage</i> on <i>irc.freenode.net</i></a></td></tr>
-</table>
-</div>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/config_anime.tmpl b/gui/slick/interfaces/default/config_anime.mako
similarity index 74%
rename from gui/slick/interfaces/default/config_anime.tmpl
rename to gui/slick/interfaces/default/config_anime.mako
index f670bb182d0058b88eb0bea5e4a05a96ae4d6a8a..05878e5d56a562c0b292213b5931d12b8a7d45ff 100644
--- a/gui/slick/interfaces/default/config_anime.tmpl
+++ b/gui/slick/interfaces/default/config_anime.mako
@@ -1,22 +1,14 @@
-#import sickbeard
-#from sickbeard.helpers import anon_url
+<%!
+    import sickbeard
+    from sickbeard.helpers import anon_url
+%>
 
-#set global $title="Config - Anime"
-#set global $header="Anime"
+<%include file="/inc_top.mako"/>
 
-#set global $sbPath="../.."
-
-#set global $topmenu="config"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/configAnime.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
+<script type="text/javascript" src="${sbRoot}/js/configAnime.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
 <div id="content960">
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+<h1 class="header">${header}</h1>
 <div id="config">
     <div id="config-content">
 
@@ -31,14 +23,14 @@
 
                 <div id="core-component-group1" class="tab-pane active component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/anidb24.png" alt="AniDB" title="AniDB" width="24" height="24" />
-                        <h3><a href="<%= anon_url('http://anidb.info') %>" onclick="window.open(this.href, '_blank'); return false;">AniDB</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/anidb24.png" alt="AniDB" title="AniDB" width="24" height="24" />
+                        <h3><a href="${anon_url('http://anidb.info')}" onclick="window.open(this.href, '_blank'); return false;">AniDB</a></h3>
                         <p>AniDB is non-profit database of anime information that is freely open to the public</p>
                     </div>
 
                     <fieldset class="component-group-list">
                         <div class="field-pair">
-                            <input type="checkbox" class="enabler" name="use_anidb" id="use_anidb" #if $sickbeard.USE_ANIDB then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" class="enabler" name="use_anidb" id="use_anidb" ${('', 'checked="checked"')[bool(sickbeard.USE_ANIDB)]} />
                             <label for="use_notifo">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">Should Sick Beard use data from AniDB?</span>
@@ -49,7 +41,7 @@
                             <div class="field-pair">
                                 <label class="nocheck">
                                     <span class="component-title">AniDB Username</span>
-                                    <input type="text" name="anidb_username" id="anidb_username" value="$sickbeard.ANIDB_USERNAME" class="form-control input-sm input350" />
+                                    <input type="text" name="anidb_username" id="anidb_username" value="${sickbeard.ANIDB_USERNAME}" class="form-control input-sm input350" />
                                 </label>
                                 <label class="nocheck">
                                     <span class="component-title">&nbsp;</span>
@@ -60,7 +52,7 @@
                             <div class="field-pair">
                                 <label class="nocheck">
                                     <span class="component-title">AniDB Password</span>
-                                    <input type="password" name="anidb_password" id="anidb_password" value="$sickbeard.ANIDB_PASSWORD" class="form-control input-sm input350" />
+                                    <input type="password" name="anidb_password" id="anidb_password" value="${sickbeard.ANIDB_PASSWORD}" class="form-control input-sm input350" />
                                 </label>
                                 <label class="nocheck">
                                     <span class="component-title">&nbsp;</span>
@@ -68,7 +60,7 @@
                                 </label>
                             </div>
                             <div class="field-pair">
-                                <input type="checkbox" name="anidb_use_mylist" id="anidb_use_mylist" #if $sickbeard.ANIDB_USE_MYLIST then "checked=\"checked\"" else ""# />
+                                <input type="checkbox" name="anidb_use_mylist" id="anidb_use_mylist" ${('', 'checked="checked"')[bool(sickbeard.ANIDB_USE_MYLIST)]}/>
                                 <label>
                                     <span class="component-title">AniDB MyList</span>
                                     <span class="component-desc">Do you want to add the PostProcessed Episodes to the MyList ?</span>
@@ -87,7 +79,7 @@
                     </div>
                     <fieldset class="component-group-list">
                         <div class="field-pair">
-                            <input type="checkbox" class="enabler" name="split_home" id="split_home" #if $sickbeard.ANIME_SPLIT_HOME then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" class="enabler" name="split_home" id="split_home" ${('', 'checked="checked"')[bool(sickbeard.ANIME_SPLIT_HOME)]}/>
                             <label for="use_notifo">
                                 <span class="component-title">Split show lists</span>
                                 <span class="component-desc">Separate anime and normal shows in groups</span>
@@ -106,4 +98,4 @@
 </div>
 
 
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_backuprestore.tmpl b/gui/slick/interfaces/default/config_backuprestore.mako
similarity index 73%
rename from gui/slick/interfaces/default/config_backuprestore.tmpl
rename to gui/slick/interfaces/default/config_backuprestore.mako
index b798ca5392207638797c86d173435ccdb2e92bd7..2ae855669bc86c6c1d885e9cdf0eae4c9f2cb87c 100644
--- a/gui/slick/interfaces/default/config_backuprestore.tmpl
+++ b/gui/slick/interfaces/default/config_backuprestore.mako
@@ -1,34 +1,29 @@
-#import os.path
-#import datetime
-#import locale
-#import sickbeard
-#from sickbeard.common import *
-#from sickbeard.sbdatetime import *
-#from sickbeard import config
-#from sickbeard import metadata
-#from sickbeard.metadata.generic import GenericMetadata
-#set global $title  = "Config - Backup/Restore"
-#set global $header = "Backup/Restore"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="config"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/configBackupRestore.js?$sbPID"></script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-#set $indexer = 0
-#if $sickbeard.INDEXER_DEFAULT
-    #set $indexer = $sickbeard.INDEXER_DEFAULT
-#end if
-
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
+<%!
+    import datetime
+    import locale
+    import sickbeard
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets
+    from sickbeard.sbdatetime import sbdatetime, date_presets, time_presets
+    from sickbeard import config
+    from sickbeard import metadata
+    from sickbeard.metadata.generic import GenericMetadata
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/configBackupRestore.js?${sbPID}"></script>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<% indexer = 0 %>
+% if sickbeard.INDEXER_DEFAULT:
+    <% indexer = sickbeard.INDEXER_DEFAULT %>
+% endif
+
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
 
 <div id="config">
     <div id="config-content">
@@ -92,11 +87,9 @@
 <div class="clearfix"></div>
 
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#backupDir').fileBrowser({ title: 'Select backup folder to save to', key: 'backupPath' });
     jQuery('#backupFile').fileBrowser({ title: 'Select backup files to restore', key: 'backupFile', includeFiles: 1 });
     jQuery('#config-components').tabs();
-//-->
 </script>
 
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.mako
similarity index 72%
rename from gui/slick/interfaces/default/config_general.tmpl
rename to gui/slick/interfaces/default/config_general.mako
index b9b7f874462d8b5a76e7bdc1680a20b82a586e7f..b9c3208130fa817010f718bfe7e32d4298010354 100644
--- a/gui/slick/interfaces/default/config_general.tmpl
+++ b/gui/slick/interfaces/default/config_general.mako
@@ -1,51 +1,45 @@
-#import os.path
-#import datetime
-#import locale
-#import sickbeard
-#from sickbeard.common import *
-#from sickbeard.sbdatetime import *
-#from sickbeard import config
-#from sickbeard import metadata
-#from sickbeard.metadata.generic import GenericMetadata
-#from sickbeard.helpers import anon_url
-
-#set global $title  = 'Config - General'
-#set global $header = 'General Configuration'
-
-#set global $sbPath = '../..'
-
-#set global $topmenu = 'config'
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-#set $indexer = 0
-#if $sickbeard.INDEXER_DEFAULT
-    #set $indexer = $sickbeard.INDEXER_DEFAULT
-#end if
-
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/lib/bootstrap-formhelpers.min-2.3.0.js?$sbPID"></script>
+<%!
+    import datetime
+    import locale
+    import sickbeard
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets
+    from sickbeard.sbdatetime import sbdatetime, date_presets, time_presets
+    from sickbeard import config
+    from sickbeard import metadata
+    from sickbeard.metadata.generic import GenericMetadata
+    from sickbeard.helpers import anon_url
+%>
+<%include file="/inc_top.mako"/>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<% indexer = 0 %>
+% if sickbeard.INDEXER_DEFAULT:
+    <% indexer = sickbeard.INDEXER_DEFAULT %>
+% endif
+
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script>
 
 <script type="text/javascript" charset="utf-8">
     <!--
-    \$(document).ready(function(){
-        if (\$("input[name='proxy_setting']").val().length == 0) {
-            \$("input[id='proxy_indexers']").prop('checked', false);
-            \$("label[for='proxy_indexers']").hide();
+    $(document).ready(function(){
+        if ($("input[name='proxy_setting']").val().length == 0) {
+            $("input[id='proxy_indexers']").prop('checked', false);
+            $("label[for='proxy_indexers']").hide();
         }
 
-        \$("input[name='proxy_setting']").on('input', function() {
-            if( \$(this).val().length === 0 ) {
-                \$("input[id='proxy_indexers']").prop('checked', false);
-                \$("label[for='proxy_indexers']").hide();
+        $("input[name='proxy_setting']").on('input', function() {
+            if( $(this).val().length === 0 ) {
+                $("input[id='proxy_indexers']").prop('checked', false);
+                $("label[for='proxy_indexers']").hide();
             } else {
-                \$("label[for='proxy_indexers']").show();
+                $("label[for='proxy_indexers']").show();
             }
         });
     });
@@ -79,7 +73,7 @@
                             <label for="indexerDefaultLang">
                                 <span class="component-title">Default Indexer Language</span>
                                 <span class="component-desc">
-                                    <select name="indexerDefaultLang" id="indexerDefaultLang" class="form-control form-control-inline input-sm bfh-languages" data-language=$sickbeard.INDEXER_DEFAULT_LANGUAGE data-available="#echo ','.join($sickbeard.indexerApi().config['valid_languages'])#"></select>
+                                    <select name="indexerDefaultLang" id="indexerDefaultLang" class="form-control form-control-inline input-sm bfh-languages" data-language=${sickbeard.INDEXER_DEFAULT_LANGUAGE} data-available="${','.join(sickbeard.indexerApi().config['valid_languages'])}"></select>
                                     <span>for adding shows and metadata providers</span>
                                 </span>
                             </label>
@@ -88,7 +82,7 @@
                             <label for="launch_browser">
                                 <span class="component-title">Launch browser</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="launch_browser" id="launch_browser" #if $sickbeard.LAUNCH_BROWSER then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="launch_browser" id="launch_browser" ${('', 'checked="checked"')[bool(sickbeard.LAUNCH_BROWSER)]}/>
                                     <p>open the SickRage home page on startup</p>
                                 </span>
                             </label>
@@ -98,11 +92,11 @@
                                 <span class="component-title">Initial page</span>
                                 <span class="component-desc">
                                     <select id="default_page" name="default_page" class="form-control input-sm">
-                                        <option value="news" #if $sickbeard.DEFAULT_PAGE == 'news' then 'selected="selected"' else ''#>News</option>
-                                        <option value="home" #if $sickbeard.DEFAULT_PAGE == 'home' then 'selected="selected"' else ''#>Home</option>
-                                        <option value="comingEpisodes" #if $sickbeard.DEFAULT_PAGE == 'comingEpisodes' then 'selected="selected"' else ''#>Coming Episodes</option>
-                                        <option value="history" #if $sickbeard.DEFAULT_PAGE == 'history' then 'selected="selected"' else ''#>History</option>
-                                        <option value="IRC" #if $sickbeard.DEFAULT_PAGE == 'IRC' then 'selected="selected"' else ''#>IRC</option>
+                                        <option value="news" ${('', 'selected="selected"')[sickbeard.DEFAULT_PAGE == 'news']}>News</option>
+                                        <option value="home" ${('', 'selected="selected"')[sickbeard.DEFAULT_PAGE == 'home']}>Home</option>
+                                        <option value="comingEpisodes" ${('', 'selected="selected"')[sickbeard.DEFAULT_PAGE == 'comingEpisodes']}>Coming Episodes</option>
+                                        <option value="history" ${('', 'selected="selected"')[sickbeard.DEFAULT_PAGE == 'history']}>History</option>
+                                        <option value="IRC" ${('', 'selected="selected"')[sickbeard.DEFAULT_PAGE == 'IRC']}>IRC</option>
                                     </select>
                                     <span>when launching SickRage interface</span>
                                 </span>
@@ -112,7 +106,7 @@
                             <label for="showupdate_hour">
                                 <span class="component-title">When to update shows</span>
                                 <span class="component-desc">
-                                    <input type="text" name="showupdate_hour" id="showupdate_hour" value="$sickbeard.SHOWUPDATE_HOUR" class="form-control input-sm input75" />
+                                    <input type="text" name="showupdate_hour" id="showupdate_hour" value="${sickbeard.SHOWUPDATE_HOUR}" class="form-control input-sm input75" />
                                     <p>with information such as next air dates, show ended, etc. Use 15 for 3pm, 4 for 4am etc. Anything over 23 or under 0 will be set to 0 (12am)</p>
                                 </span>
                             </label>
@@ -122,7 +116,7 @@
                             <label for="update_shows_on_start">
                                 <span class="component-title">Update shows on startup</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="update_shows_on_start" id="update_shows_on_start" #if $sickbeard.UPDATE_SHOWS_ON_START then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="update_shows_on_start" id="update_shows_on_start" ${('', 'checked="checked"')[bool(sickbeard.UPDATE_SHOWS_ON_START)]}/>
                                     <p>with information such as next air dates, show ended, etc. Disable for a faster startup as show info is sheduled to update in the background anyway</p>
                                 </span>
                             </label>
@@ -132,7 +126,7 @@
                             <label for="update_shows_on_snatch">
                                 <span class="component-title">Update shows on snatch</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="update_shows_on_snatch" id="update_shows_on_snatch" #if $sickbeard.UPDATE_SHOWS_ON_SNATCH then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="update_shows_on_snatch" id="update_shows_on_snatch" ${('', 'checked="checked"')[bool(sickbeard.UPDATE_SHOWS_ON_SNATCH)]}/>
                                     <p>with information such as next air dates, show ended, etc.</p>
                                 </span>
                             </label>
@@ -142,11 +136,11 @@
                             <span class="component-title">Send to trash for actions</span>
                             <span class="component-desc">
                                 <label for="trash_remove_show" class="nextline-block">
-                                    <input type="checkbox" name="trash_remove_show" id="trash_remove_show" #if $sickbeard.TRASH_REMOVE_SHOW then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="trash_remove_show" id="trash_remove_show" ${('', 'checked="checked"')[bool(sickbeard.TRASH_REMOVE_SHOW)]}/>
                                     <p>when using show "Remove" and delete files</p>
                                 </label>
                                 <label for="trash_rotate_logs" class="nextline-block">
-                                    <input type="checkbox" name="trash_rotate_logs" id="trash_rotate_logs" #if $sickbeard.TRASH_ROTATE_LOGS then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="trash_rotate_logs" id="trash_rotate_logs" ${('', 'checked="checked"')[bool(sickbeard.TRASH_ROTATE_LOGS)]}/>
                                     <p>on scheduled deletes of the oldest log files</p>
                                 </label>
                                 <div class="clear-left"><p>selected actions use trash (recycle bin) instead of the default permanent delete</p></div>
@@ -157,7 +151,7 @@
                             <label for="log_dir">
                                 <span class="component-title">Log file folder location</span>
                                 <span class="component-desc">
-                                    <input type="text" name="log_dir" id="log_dir" value="$sickbeard.ACTUAL_LOG_DIR" class="form-control input-sm input350" />
+                                    <input type="text" name="log_dir" id="log_dir" value="${sickbeard.ACTUAL_LOG_DIR}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
@@ -166,7 +160,7 @@
                             <label for="log_nr">
                                 <span class="component-title">Number of Log files saved</span>
                                 <span class="component-desc">
-                                    <input type="text" name="log_nr" id="log_nr" value="$sickbeard.LOG_NR" class="form-control input-sm input75" />
+                                    <input type="text" name="log_nr" id="log_nr" value="${sickbeard.LOG_NR}" class="form-control input-sm input75" />
                                     <p>number of log files saved when rotating logs (default: 5) (REQUIRES RESTART)</p>
                                 </span>
                             </label>
@@ -176,7 +170,7 @@
                             <label for="log_size">
                                 <span class="component-title">Size of Log files saved</span>
                                 <span class="component-desc">
-                                    <input type="text" name="log_size" id="log_size" value="$sickbeard.LOG_SIZE" class="form-control input-sm input75" />
+                                    <input type="text" name="log_size" id="log_size" value="${sickbeard.LOG_SIZE}" class="form-control input-sm input75" />
                                     <p>maximum size of a log file saved (default: 1048576 (1MB)) (REQUIRES RESTART)</p>
                                 </span>
                             </label>
@@ -187,10 +181,10 @@
                                 <span class="component-title">Use initial indexer set to</span>
                                 <span class="component-desc">
                                     <select id="indexer_default" name="indexer_default" class="form-control input-sm">
-                                        <option value="0" #if $indexer == 0 then 'selected="selected"' else ''#>All Indexers</option>
-                                        #for $indexer in $sickbeard.indexerApi().indexers
-                                        <option value="$indexer" #if $indexer == $sickbeard.INDEXER_DEFAULT then 'selected="selected"' else ''#>$sickbeard.indexerApi().indexers[$indexer]</option>
-                                        #end for
+                                        <option value="0" ${('', 'selected="selected"')[indexer == 0]}>All Indexers</option>
+                                        % for indexer in sickbeard.indexerApi().indexers:
+                                        <option value="${indexer}" ${('', 'selected="selected"')[sickbeard.INDEXER_DEFAULT == indexer]}>${sickbeard.indexerApi().indexers[indexer]}</option>
+                                        % endfor
                                     </select>
                                     <span>as the default selection when adding new shows</span>
                                 </span>
@@ -201,7 +195,7 @@
                             <label for="indexer_timeout">
                                 <span class="component-title">Timeout show indexer at</span>
                                 <span class="component-desc">
-                                    <input type="text" name="indexer_timeout" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" class="form-control input-sm input75" />
+                                    <input type="text" name="indexer_timeout" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" class="form-control input-sm input75" />
                                     <p>seconds of inactivity when finding new shows (default:10)</p>
                                 </span>
                             </label>
@@ -212,7 +206,7 @@
                                 <span class="component-title">Show root directories</span>
                                 <span class="component-desc">
                                     <p>where the files of shows are located</p>
-                                    #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
+                                    <%include file="/inc_rootDirs.mako"/>
                                 </span>
                             </label>
                         </div>
@@ -232,7 +226,7 @@
                             <label for="version_notify">
                                 <span class="component-title">Check software updates</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="version_notify" id="version_notify" #if $sickbeard.VERSION_NOTIFY then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="version_notify" id="version_notify" ${('', 'checked="checked"')[bool(sickbeard.VERSION_NOTIFY)]}/>
                                     <p>and display notifications when updates are available.
                                     Checks are run on startup and at the frequency set below*</p>
                                 </span>
@@ -243,7 +237,7 @@
                             <label for="auto_update">
                                 <span class="component-title">Automatically update</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="auto_update" id="auto_update" #if $sickbeard.AUTO_UPDATE then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="auto_update" id="auto_update" ${('', 'checked="checked"')[bool(sickbeard.AUTO_UPDATE)]}/>
                                     <p>fetch and install software updates.
                                     Updates are run on startup and in the background at the frequency set below*</p>
                                 </span>
@@ -254,7 +248,7 @@
                             <label>
                                 <span class="component-title">Check the server every*</span>
                                 <span class="component-desc">
-                                    <input type="text" name="update_frequency" id="update_frequency" value="$sickbeard.UPDATE_FREQUENCY" class="form-control input-sm input75" />
+                                    <input type="text" name="update_frequency" id="update_frequency" value="${sickbeard.UPDATE_FREQUENCY}" class="form-control input-sm input75" />
                                     <p>hours for software updates (default:12)</p>
                                 </span>
                             </label>
@@ -264,7 +258,7 @@
                             <label for="notify_on_update">
                                 <span class="component-title">Notify on software update</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="notify_on_update" id="notify_on_update" #if $sickbeard.NOTIFY_ON_UPDATE then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="notify_on_update" id="notify_on_update" ${('', 'checked="checked"')[bool(sickbeard.NOTIFY_ON_UPDATE)]}/>
                                     <p>send a message to all enabled notifiers when SickRage has been updated</p>
                                 </span>
                             </label>
@@ -292,8 +286,8 @@
                                 <span class="component-title">Display theme:</span>
                                 <span class="component-desc">
                                     <select id="theme_name" name="theme_name" class="form-control input-sm">
-                                        <option value="dark" #if $sickbeard.THEME_NAME == 'dark' then 'selected="selected"' else ''#>Dark</option>
-                                        <option value="light" #if $sickbeard.THEME_NAME == 'light' then 'selected="selected"' else ''#>Light</option>
+                                        <option value="dark" ${('', 'selected="selected"')[sickbeard.THEME_NAME == 'dark']}>Dark</option>
+                                        <option value="light" ${('', 'selected="selected"')[sickbeard.THEME_NAME == 'light']}>Light</option>
                                     </select>
                                     <span class="red-text">for appearance to take effect, save then refresh your browser</span>
                                 </span>
@@ -303,7 +297,7 @@
                             <label for="display_all_seasons">
                                 <span class="component-title">Show all seasons</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="display_all_seasons" id="display_all_seasons" #if $sickbeard.DISPLAY_ALL_SEASONS then 'checked="checked"' else ''#>
+                                    <input type="checkbox" name="display_all_seasons" id="display_all_seasons" ${('', 'checked="checked"')[bool(sickbeard.DISPLAY_ALL_SEASONS)]}>
                                     <p>on the show summary page</p>
                                 </span>
                             </label>
@@ -312,7 +306,7 @@
                             <label for="sort_article">
                                 <span class="component-title">Sort with "The", "A", "An"</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="sort_article" id="sort_article" #if $sickbeard.SORT_ARTICLE then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="sort_article" id="sort_article" ${('', 'checked="checked"')[bool(sickbeard.SORT_ARTICLE)]}/>
                                     <p>include articles ("The", "A", "An") when sorting show lists</p>
                                 </span>
                             </label>
@@ -321,7 +315,7 @@
                             <label for="filter_row">
                                 <span class="component-title">Filter Row</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="filter_row" id="filter_row" #if $sickbeard.FILTER_ROW == True then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="filter_row" id="filter_row" ${('', 'checked="checked"')[bool(sickbeard.FILTER_ROW)]}/>
                                     <p>Add a filter row to the show display on the home page</p>
                                     <p>Supports =, >, >=, <=, <, xx to yy , xx - yy</p>
                                     <p><b>Note:</b> =, >, >=, <=, < should be first, followed by a space, then the value.</p>
@@ -333,7 +327,7 @@
                             <label for="coming_eps_missed_range">
                                 <span class="component-title">Missed episodes range</span>
                                 <span class="component-desc">
-                                    <input type="number" step="1" min="7" name="coming_eps_missed_range" id="coming_eps_missed_range" value="$sickbeard.COMING_EPS_MISSED_RANGE" class="form-control input-sm input75" />
+                                    <input type="number" step="1" min="7" name="coming_eps_missed_range" id="coming_eps_missed_range" value="${sickbeard.COMING_EPS_MISSED_RANGE}" class="form-control input-sm input75" />
                                     <p>Set the range in days of the missed episodes in the Coming Episode page</p>
                                 </span>
                             </label>
@@ -342,16 +336,16 @@
                             <label for="fuzzy_dating">
                                 <span class="component-title">Display fuzzy dates</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="fuzzy_dating" id="fuzzy_dating" class="viewIf datePresets" #if $sickbeard.FUZZY_DATING == True then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="fuzzy_dating" id="fuzzy_dating" class="viewIf datePresets" ${('', 'checked="checked"')[bool(sickbeard.FUZZY_DATING)]}/>
                                     <p>move absolute dates into tooltips and display e.g. "Last Thu", "On Tue"</p>
                                 </span>
                             </label>
                         </div>
-                        <div class="field-pair show_if_fuzzy_dating#if True == $sickbeard.FUZZY_DATING then '' else ' metadataDiv'#">
+                        <div class="field-pair show_if_fuzzy_dating ${(' metadataDiv', '')[not bool(sickbeard.FUZZY_DATING)]}">
                             <label for="trim_zero">
                                 <span class="component-title">Trim zero padding</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="trim_zero" id="trim_zero" #if True == $sickbeard.TRIM_ZERO then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="trim_zero" id="trim_zero" ${('', 'checked="checked"')[bool(sickbeard.TRIM_ZERO)]}/>
                                     <p>remove the leading number "0" shown on hour of day, and date of month</p>
                                 </span>
                             </label>
@@ -361,17 +355,16 @@
                             <label for="date_presets">
                                 <span class="component-title">Date style:</span>
                                 <span class="component-desc">
-                                    <select class="form-control input-sm #if True == $sickbeard.FUZZY_DATING then '' else ' metadataDiv'#" id="date_presets#if True == $sickbeard.FUZZY_DATING then '' else '_na'#" name="date_preset#if True == $sickbeard.FUZZY_DATING then '' else '_na'#">
-                                        #for $cur_preset in $date_presets:
-                                            <option value="$cur_preset" #if $cur_preset == $sickbeard.DATE_PRESET or ("%x" == $sickbeard.DATE_PRESET and "$cur_preset" == '%a, %b %d, %Y') then 'selected="selected"' else ''#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
-                                        #end for
+                                    <select class="form-control input-sm ${(' metadataDiv', '')[bool(sickbeard.FUZZY_DATING)]}" id="date_presets${('_na', '')[bool(sickbeard.FUZZY_DATING)]}" name="date_preset${('_na', '')[bool(sickbeard.FUZZY_DATING)]}">
+                                        % for cur_preset in date_presets:
+                                            <option value="${cur_preset}" ${('', 'selected="selected"')[sickbeard.DATE_PRESET == cur_preset or ("%x" == sickbeard.DATE_PRESET and cur_preset == '%a, %b %d, %Y')]}>${datetime.datetime(datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime(cur_preset)}</option>
+                                        % endfor
                                     </select>
-
-                                    <select class="form-control input-sm #if True != $sickbeard.FUZZY_DATING then '' else ' metadataDiv'#" id="date_presets#if True != $sickbeard.FUZZY_DATING then '' else '_na'#" name="date_preset#if True != $sickbeard.FUZZY_DATING then '' else '_na'#">
-                                        <option value="%x" #if "%x" == $sickbeard.DATE_PRESET then 'selected="selected"' else ''#>Use System Default</option>
-                                        #for $cur_preset in $date_presets:
-                                            <option value="$cur_preset" #if $cur_preset == $sickbeard.DATE_PRESET then 'selected="selected"' else ''#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
-                                        #end for
+                                    <select class="form-control input-sm ${(' metadataDiv', '')[not bool(sickbeard.FUZZY_DATING)]}" id="date_presets${(' metadataDiv', '')[not bool(sickbeard.FUZZY_DATING)]}" name="date_preset${('_na', '')[not bool(sickbeard.FUZZY_DATING)]}">
+                                        <option value="%x" ${('', 'selected="selected"')[sickbeard.DATE_PRESET == '%x']}>Use System Default</option>
+                                        % for cur_preset in date_presets:
+                                            <option value="${cur_preset}" ${('', 'selected="selected"')[sickbeard.DATE_PRESET == cur_preset]}>${datetime.datetime(datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime(cur_preset)}</option>
+                                        % endfor
                                     </select>
                                 </span>
                             </label>
@@ -382,9 +375,9 @@
                                 <span class="component-title">Time style:</span>
                                 <span class="component-desc">
                                     <select id="time_presets" name="time_preset" class="form-control input-sm">
-                                         #for $cur_preset in $time_presets:
-                                            <option value="$cur_preset" #if $cur_preset == $sickbeard.TIME_PRESET_W_SECONDS then 'selected="selected"' else ''#>$sbdatetime.now().sbftime(show_seconds=True,t_preset=$cur_preset)</option>
-                                         #end for
+                                         % for cur_preset in time_presets:
+                                            <option value="${cur_preset}" ${('', 'selected="selected"')[sickbeard.TIME_PRESET_W_SECONDS == cur_preset]}>${sbdatetime.now().sbftime(show_seconds=True, t_preset=cur_preset)}</option>
+                                         % endfor
                                     </select>
                                     <span><b>note:</b> seconds are only shown on the History page</span>
                                 </span>
@@ -395,10 +388,10 @@
                             <span class="component-title">Timezone:</span>
                             <span class="component-desc">
                                 <label for="local" class="space-right">
-                                    <input type="radio" name="timezone_display" id="local" value="local" #if "local" == $sickbeard.TIMEZONE_DISPLAY then 'checked="checked"' else ''# />Local
+                                    <input type="radio" name="timezone_display" id="local" value="local" ${('', 'checked="checked"')[sickbeard.TIMEZONE_DISPLAY == "local"]} />Local
                                 </label>
                                 <label for="network">
-                                    <input type="radio" name="timezone_display" id="network" value="network" #if "network" == $sickbeard.TIMEZONE_DISPLAY then 'checked="checked"' else ''# />Network
+                                    <input type="radio" name="timezone_display" id="network" value="network" ${('', 'checked="checked"')[sickbeard.TIMEZONE_DISPLAY == "network"]} />Network
                                 </label>
                                 <div class="clear-left"><p>display dates and times in either your timezone or the shows network timezone</p></div>
                             </span>
@@ -406,13 +399,13 @@
 
                         <div class="field-pair">
                             <label for="download_url">
-                                    <span class="component-title">Download url</span>
-                                    <input type="text" name="download_url" id="download_url" value="$sickbeard.DOWNLOAD_URL" size="35" />
-                                </label>
-                                <label>
-                                    <span class="component-title">&nbsp;</span>
-                                <span class="component-desc">URL where the shows can be downloaded.</span>
-                                </label>
+                                <span class="component-title">Download url</span>
+                                <input type="text" name="download_url" id="download_url" value="${sickbeard.DOWNLOAD_URL}" size="35" />
+                            </label>
+                            <label>
+                                <span class="component-title">&nbsp;</span>
+                            <span class="component-desc">URL where the shows can be downloaded.</span>
+                            </label>
                         </div>
 
 
@@ -436,7 +429,7 @@
                             <label for="api_key">
                                 <span class="component-title">API key</span>
                                 <span class="component-desc">
-                                    <input type="text" name="api_key" id="api_key" value="$sickbeard.API_KEY" class="form-control input-sm input300" readonly="readonly" />
+                                    <input type="text" name="api_key" id="api_key" value="${sickbeard.API_KEY}" class="form-control input-sm input300" readonly="readonly" />
                                     <input class="btn btn-inline" type="button" id="generate_new_apikey" value="Generate">
                                     <div class="clear-left"><p>used to give 3rd party programs limited access to SickRage</p></div>
                                 </span>
@@ -447,7 +440,7 @@
                             <label for="web_log">
                                 <span class="component-title">HTTP logs</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="web_log" id="web_log" #if $sickbeard.WEB_LOG then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="web_log" id="web_log" ${('', 'checked="checked"')[bool(sickbeard.WEB_LOG)]}/>
                                     <p>enable logs from the internal Tornado web server</p>
                                 </span>
                             </label>
@@ -457,7 +450,7 @@
                             <label for="web_username">
                                 <span class="component-title">HTTP username</span>
                                 <span class="component-desc">
-                                    <input type="text" name="web_username" id="web_username" value="$sickbeard.WEB_USERNAME" class="form-control input-sm input300" />
+                                    <input type="text" name="web_username" id="web_username" value="${sickbeard.WEB_USERNAME}" class="form-control input-sm input300" />
                                     <p>set blank for no login</p>
                                 </span>
                             </label>
@@ -467,7 +460,7 @@
                             <label for="web_password">
                                 <span class="component-title">HTTP password</span>
                                 <span class="component-desc">
-                                    <input type="password" name="web_password" id="web_password" value="$sickbeard.WEB_PASSWORD" class="form-control input-sm input300" />
+                                    <input type="password" name="web_password" id="web_password" value="${sickbeard.WEB_PASSWORD}" class="form-control input-sm input300" />
                                     <p>blank = no authentication</span>
                             </label>
                         </div>
@@ -476,7 +469,7 @@
                             <label for="web_port">
                                 <span class="component-title">HTTP port</span>
                                 <span class="component-desc">
-                                    <input type="text" name="web_port" id="web_port" value="$sickbeard.WEB_PORT" class="form-control input-sm input100" />
+                                    <input type="text" name="web_port" id="web_port" value="${sickbeard.WEB_PORT}" class="form-control input-sm input100" />
                                     <p>web port to browse and access SickRage (default:8081)</p>
                                 </span>
                             </label>
@@ -486,7 +479,7 @@
                             <label for="web_ipv6">
                                 <span class="component-title">Listen on IPv6</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="web_ipv6" id="web_ipv6" #if $sickbeard.WEB_IPV6 then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="web_ipv6" id="web_ipv6" ${('', 'checked="checked"')[bool(sickbeard.WEB_IPV6)]}/>
                                     <p>attempt binding to any available IPv6 address</p>
                                 </span>
                             </label>
@@ -496,7 +489,7 @@
                             <label for="enable_https">
                                 <span class="component-title">Enable HTTPS</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="enable_https" class="enabler" id="enable_https" #if $sickbeard.ENABLE_HTTPS then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="enable_https" class="enabler" id="enable_https" ${('', 'checked="checked"')[bool(sickbeard.ENABLE_HTTPS)]}/>
                                     <p>enable access to the web interface using a HTTPS address</p>
                                 </span>
                             </label>
@@ -506,7 +499,7 @@
                                 <label for="https_cert">
                                     <span class="component-title">HTTPS certificate</span>
                                     <span class="component-desc">
-                                        <input type="text" name="https_cert" id="https_cert" value="$sickbeard.HTTPS_CERT" class="form-control input-sm input300" />
+                                        <input type="text" name="https_cert" id="https_cert" value="${sickbeard.HTTPS_CERT}" class="form-control input-sm input300" />
                                         <div class="clear-left"><p>file name or path to HTTPS certificate</p></div>
                                     </span>
                                 </label>
@@ -515,7 +508,7 @@
                                 <label for="https_key">
                                     <span class="component-title">HTTPS key</span>
                                     <span class="component-desc">
-                                        <input type="text" name="https_key" id="https_key" value="$sickbeard.HTTPS_KEY" class="form-control input-sm input300" />
+                                        <input type="text" name="https_key" id="https_key" value="${sickbeard.HTTPS_KEY}" class="form-control input-sm input300" />
                                         <div class="clear-left"><p>file name or path to HTTPS key</p></div>
                                     </span>
                                 </label>
@@ -526,7 +519,7 @@
                             <label for="handle_reverse_proxy">
                                 <span class="component-title">Reverse proxy headers</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="handle_reverse_proxy" id="handle_reverse_proxy" #if $sickbeard.HANDLE_REVERSE_PROXY then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="handle_reverse_proxy" id="handle_reverse_proxy" ${('', 'checked="checked"')[bool(sickbeard.HANDLE_REVERSE_PROXY)]}/>
                                     <p>accept the following reverse proxy headers (advanced)...<br />(X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto)</p>
                                 </span>
                             </label>
@@ -555,9 +548,9 @@
                                 <span class="component-title">CPU throttling:</span>
                                 <span class="component-desc">
                                     <select id="cpu_presets" name="cpu_preset" class="form-control input-sm">
-                                    #for $cur_preset in $cpu_presets:
-                                        <option value="$cur_preset" #if $cur_preset == $sickbeard.CPU_PRESET then 'selected="selected"' else ''#>$cur_preset.capitalize()</option>
-                                    #end for
+                                    % for cur_preset in cpu_presets:
+                                        <option value="${cur_preset}" ${('', 'selected="selected"')[sickbeard.CPU_PRESET == cur_preset]}>${cur_preset.capitalize()}</option>
+                                    % endfor
                                     </select>
                                     <span>Normal (default). High is lower and Low is higher CPU use</span>
                                 </span>
@@ -568,7 +561,7 @@
                             <label>
                                 <span class="component-title">Anonymous redirect</span>
                                 <span class="component-desc">
-                                    <input type="text" name="anon_redirect" value="$sickbeard.ANON_REDIRECT" class="form-control input-sm input300" />
+                                    <input type="text" name="anon_redirect" value="${sickbeard.ANON_REDIRECT}" class="form-control input-sm input300" />
                                     <div class="clear-left"><p>backlink protection via anonymizer service, must end in "?"</p></div>
                                 </span>
                             </label>
@@ -578,25 +571,27 @@
                             <label for="debug">
                                 <span class="component-title">Enable debug</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="debug" id="debug" #if $sickbeard.DEBUG then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="debug" id="debug" ${('', 'checked="checked"')[sickbeard.DEBUG == cur_preset]}/>
                                     <p>Enable debug logs<p>
                                 </span>
                             </label>
                         </div>
-                                            <div class="field-pair">
-                                                    <label for="ssl_verify">
-                                                        <span class="component-title">Verify SSL Certs</span>
-                                                            <span class="component-desc">
-                                                                <input type="checkbox" name="ssl_verify" id="ssl_verify" #if $sickbeard.SSL_VERIFY then 'checked="checked"' else ''#/>
-                                                                <p>Verify SSL Certificates (Disable this for broken SSL installs (Like QNAP)<p>
-                                                            </span>
-                                                    </label>
-                                                </div>
+
+                        <div class="field-pair">
+                            <label for="ssl_verify">
+                                <span class="component-title">Verify SSL Certs</span>
+                                    <span class="component-desc">
+                                        <input type="checkbox" name="ssl_verify" id="ssl_verify" ${('', 'checked="checked"')[bool(sickbeard.SSL_VERIFY)]}/>
+                                        <p>Verify SSL Certificates (Disable this for broken SSL installs (Like QNAP)<p>
+                                    </span>
+                            </label>
+                        </div>
+
                         <div class="field-pair">
                             <label for="no_restart">
                                 <span class="component-title">No Restart</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="no_restart" id="no_restart" #if $sickbeard.NO_RESTART then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="no_restart" id="no_restart" ${('', 'checked="checked"')[bool(sickbeard.NO_RESTART)]}/>
                                     <p>Only shutdown when restarting SR.
                                     Only select this when you have external software restarting SR automatically when it stops (like FireDaemon)</p>
                                 </span>
@@ -608,9 +603,9 @@
                             <label for="encryption_version">
                                 <span class="component-title">Encrypt passwords</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="encryption_version" id="encryption_version" #if $sickbeard.ENCRYPTION_VERSION then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="encryption_version" id="encryption_version" ${('', 'checked="checked"')[bool(sickbeard.ENCRYPTION_VERSION)]}/>
                                     <p>in the <code>config.ini</code> file.
-                                    <b>Warning:</b> Passwords must only contain <a target="_blank" href="<%= anon_url('http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters') %>">ASCII characters</a></p>
+                                    <b>Warning:</b> Passwords must only contain <a target="_blank" href="${anon_url('http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters')}">ASCII characters</a></p>
                                 </span>
                             </label>
                         </div>
@@ -619,7 +614,7 @@
                             <label for="calendar_unprotected">
                                 <span class="component-title">Unprotected calendar</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="calendar_unprotected" id="calendar_unprotected" #if $sickbeard.CALENDAR_UNPROTECTED then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="calendar_unprotected" id="calendar_unprotected" ${('', 'checked="checked"')[bool(sickbeard.CALENDAR_UNPROTECTED)]}/>
                                     <p>allow subscribing to the calendar without user and password.
                                     Some services like Google Calendar only work this way</p>
                                 </span>
@@ -631,7 +626,7 @@
                             <label>
                                 <span class="component-title">Proxy host</span>
                                 <span class="component-desc">
-                                    <input type="text" name="proxy_setting" value="$sickbeard.PROXY_SETTING" class="form-control input-sm input300" />
+                                    <input type="text" name="proxy_setting" value="${sickbeard.PROXY_SETTING}" class="form-control input-sm input300" />
                                     <div class="clear-left"><p>blank to disable or proxy to use when connecting to providers</p></div>
                             </label>
                         </div>
@@ -640,7 +635,7 @@
                             <label for="proxy_indexers">
                                 <span class="component-title">Use proxy for indexers</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="proxy_indexers" id="proxy_indexers" #if True == $sickbeard.PROXY_INDEXERS then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="proxy_indexers" id="proxy_indexers" ${('', 'checked="checked"')[bool(sickbeard.PROXY_INDEXERS)]}/>
                                     <p>use proxy host for connecting to indexers (thetvdb, tvrage)</p>
                                 </span>
                             </label>
@@ -649,20 +644,20 @@
                             <label for="ep_default_deleted_status">
                                 <span class="component-title">Default deleted episode status:</span>
                                     <span class="component-desc">
-#if not $sickbeard.SKIP_REMOVED_FILES or ($sickbeard.USE_TRAKT and $sickbeard.TRAKT_USE_ROLLING_DOWNLOAD)
+% if not sickbeard.SKIP_REMOVED_FILES or (sickbeard.USE_TRAKT and sickbeard.TRAKT_USE_ROLLING_DOWNLOAD):
                                         <select name="ep_default_deleted_status" id="ep_default_deleted_status" class="form-control input-sm">
-                                        #for $defStatus in [$ARCHIVED, $IGNORED]:
-                                            <option value="$defStatus" #if $defStatus == $sickbeard.EP_DEFAULT_DELETED_STATUS then 'selected="selected"' else ''#>$statusStrings[$defStatus]</option>
-                                        #end for
+                                        % for defStatus in [ARCHIVED, IGNORED]:
+                                            <option value="${defStatus}" ${('', 'selected="selected"')[sickbeard.EP_DEFAULT_DELETED_STATUS == defStatus]}>${statusStrings[defStatus]}</option>
+                                        % endfor
                                         </select>
-#else
+% else:
                                         <select name="ep_default_deleted_status" id="ep_default_deleted_status" class="form-control input-sm" disabled="disabled">
-                                        #for $defStatus in [$ARCHIVED, $IGNORED]:
-                                            <option value="$defStatus" #if $defStatus == $sickbeard.EP_DEFAULT_DELETED_STATUS then 'selected="selected"' else ''#>$statusStrings[$defStatus]</option>
-                                        #end for
+                                        % for defStatus in [ARCHIVED, IGNORED]:
+                                            <option value="${defStatus}" ${('', 'selected="selected"')[sickbeard.EP_DEFAULT_DELETED_STATUS == defStatus]}>${statusStrings[defStatus]}</option>
+                                        % endfor
                                         </select>
-                                        <input type="hidden" name="ep_default_deleted_status" value="$sickbeard.EP_DEFAULT_DELETED_STATUS" />
-#end if
+                                        <input type="hidden" name="ep_default_deleted_status" value="${sickbeard.EP_DEFAULT_DELETED_STATUS}" />
+% endif
                                     <span>Define the status to be set for media file that has been deleted.</span>
                                 </span>
                             </label>
@@ -685,29 +680,29 @@
                                 <span class="component-title">Branch version:</span>
                                 <span class="component-desc">
                                     <select id="branchVersion" class="form-control form-control-inline input-sm pull-left">
-                                    #set $gh_branch = $sickbeard.versionCheckScheduler.action.list_remote_branches()
-                                    #if $gh_branch:
-                                        #for $cur_branch in $gh_branch:
-                                            #if $sickbeard.GIT_USERNAME and $sickbeard.GIT_PASSWORD and $sickbeard.DEVELOPER == 1
-                                                <option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
-                                            #else if $sickbeard.GIT_USERNAME and $sickbeard.GIT_PASSWORD and $cur_branch in ['master', 'develop']
-                                                <option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
-                                            #else if $cur_branch == 'master'
-                                                <option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
-                                            #end if
-                                        #end for
-                                    #end if
+                                    <% gh_branch = sickbeard.versionCheckScheduler.action.list_remote_branches() %>
+                                    % if gh_branch:
+                                        % for cur_branch in gh_branch:
+                                            % if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and sickbeard.DEVELOPER == 1:
+                                                <option value="${cur_branch}" ${('', 'selected="selected"')[sickbeard.BRANCH == cur_branch]}>${cur_branch}</option>
+                                            % elif sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and cur_branch in ['master', 'develop']:
+                                                <option value="${cur_branch}" ${('', 'selected="selected"')[sickbeard.BRANCH == cur_branch]}>${cur_branch}</option>
+                                            % elif cur_branch == 'master':
+                                                <option value="${cur_branch}" ${('', 'selected="selected"')[sickbeard.BRANCH == cur_branch]}>${cur_branch}</option>
+                                            % endif
+                                        % endfor
+                                    % endif
                                     </select>
-                                    #if not $gh_branch
+                                    % if not gh_branch:
                                        <input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch" disabled>
-                                    #else
+                                    % else:
                                        <input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch">
-                                    #end if
-                                    #if not $gh_branch
+                                    % endif
+                                    % if not gh_branch:
                                        <div class="clear-left" style="color:#FF0000"><p>Error: No branches found.</p></div>
-                                    #else
+                                    % else:
                                        <div class="clear-left"><p>select branch to use (restart required)</p></div>
-                                    #end if
+                                    % endif
                                 </span>
                             </label>
                         </div>
@@ -716,7 +711,7 @@
                             <label for="git_username">
                                 <span class="component-title">GitHub username</span>
                                 <span class="component-desc">
-                                    <input type="text" name="git_username" id="git_username" value="$sickbeard.GIT_USERNAME" class="form-control input-sm input300" />
+                                    <input type="text" name="git_username" id="git_username" value="${sickbeard.GIT_USERNAME}" class="form-control input-sm input300" />
                                     <div class="clear-left"><p>*** (REQUIRED FOR SUBMITTING ISSUES) ***</p></div>
                                 </span>
                             </label>
@@ -726,7 +721,7 @@
                             <label for="git_password">
                                 <span class="component-title">GitHub password</span>
                                 <span class="component-desc">
-                                    <input type="password" name="git_password" id="git_password" value="$sickbeard.GIT_PASSWORD" class="form-control input-sm input300" />
+                                    <input type="password" name="git_password" id="git_password" value="${sickbeard.GIT_PASSWORD}" class="form-control input-sm input300" />
                                     <div class="clear-left"><p>*** (REQUIRED FOR SUBMITTING ISSUES) ***</p></div>
                                 </span>
                             </label>
@@ -736,7 +731,7 @@
                             <label for="git_remote">
                                 <span class="component-title">GitHub remote for branch</span>
                                 <span class="component-desc">
-                                    <input type="text" name="git_remote" id="git_remote" value="$sickbeard.GIT_REMOTE" class="form-control input-sm input300" />
+                                    <input type="text" name="git_remote" id="git_remote" value="${sickbeard.GIT_REMOTE}" class="form-control input-sm input300" />
                                     <div class="clear-left"><p>default:origin. Access repo configured remotes (save then refresh browser)</p></div>
                                 </span>
                             </label>
@@ -746,7 +741,7 @@
                             <label>
                                 <span class="component-title">Git executable path</span>
                                 <span class="component-desc">
-                                    <input type="text" name="git_path" value="$sickbeard.GIT_PATH" class="form-control input-sm input300" />
+                                    <input type="text" name="git_path" value="${sickbeard.GIT_PATH}" class="form-control input-sm input300" />
                                     <div class="clear-left"><p>only needed if OS is unable to locate git from env</p></div>
                                 </span>
                             </label>
@@ -756,7 +751,7 @@
                             <label for="git_reset">
                                 <span class="component-title">Git reset</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="git_reset" id="git_reset" #if True == $sickbeard.GIT_RESET then 'checked="checked"' else ''#/>
+                                    <input type="checkbox" name="git_reset" id="git_reset" ${('', 'checked="checked"')[bool(sickbeard.GIT_RESET)]}/>
                                     <p>removes untracked files and performs a hard reset on git branch automatically to help resolve update issues</p>
                                 </span>
                             </label>
@@ -766,7 +761,7 @@
                             <label for="git_autoissues">
                                 <span class="component-title">Git auto-issues submit</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="git_autoissues" id="git_autoissues" #if True == $sickbeard.GIT_AUTOISSUES then 'checked="checked"' else ''# disable/>
+                                    <input type="checkbox" name="git_autoissues" id="git_autoissues" ${('', 'checked="checked"')[bool(sickbeard.GIT_AUTOISSUES)]} disable/>
                                     <p>automatically submit bug/issue reports to our issue tracker when errors are logged</p>
                                 </span>
                             </label>
@@ -780,7 +775,7 @@
                 </div><!-- /component-group3 //-->
 
                 <br/>
-                <h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">$sickbeard.DATA_DIR</span></b> </h6>
+                <h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">${sickbeard.DATA_DIR}</span></b> </h6>
                 <input type="submit" class="btn pull-left config_submitter button" value="Save Changes" />
 
             </div><!-- /config-components -->
@@ -792,10 +787,8 @@
 <div></div>
 
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#log_dir').fileBrowser({ title: 'Select log file folder location' });
     jQuery('#config-components').tabs();
-//-->
 </script>
 
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_notifications.tmpl b/gui/slick/interfaces/default/config_notifications.mako
similarity index 78%
rename from gui/slick/interfaces/default/config_notifications.tmpl
rename to gui/slick/interfaces/default/config_notifications.mako
index e17c379432ab8c0cd1659975914df72e8cbaaa38..8c0542e65421e5ca198123350150a134631975d4 100644
--- a/gui/slick/interfaces/default/config_notifications.tmpl
+++ b/gui/slick/interfaces/default/config_notifications.mako
@@ -1,23 +1,19 @@
-#import sickbeard
-#from sickbeard.helpers import anon_url
-#from sickbeard.common import *
-
-#set global $title="Config - Notifications"
-#set global $header="Notifications"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="config"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/configNotifications.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+<%
+    import sickbeard
+    import re
+    from sickbeard.helpers import anon_url
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets, multiEpStrings
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/configNotifications.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="config">
     <div id="config-content">
@@ -33,8 +29,8 @@
                 <div class="component-group">
 
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/kodi.png" alt="" title="KODI" />
-                        <h3><a href="<%= anon_url('http://kodi.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">KODI</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/kodi.png" alt="" title="KODI" />
+                        <h3><a href="${anon_url('http://kodi.tv/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">KODI</a></h3>
                         <p>A free and open source cross-platform media center and home entertainment system software with a 10-foot user interface designed for the living-room TV.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -42,7 +38,7 @@
                             <label class="clearfix" for="use_kodi">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_kodi" id="use_kodi" #if $sickbeard.USE_KODI then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_kodi" id="use_kodi" ${('', 'checked="checked"')[bool(sickbeard.USE_KODI)]}/>
                                     <p>should SickRage send KODI commands ?<p>
                                 </span>
                             </label>
@@ -53,7 +49,7 @@
                                 <label for="kodi_always_on">
                                     <span class="component-title">Always on</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_always_on" id="kodi_always_on" #if $sickbeard.KODI_ALWAYS_ON then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_always_on" id="kodi_always_on" ${('', 'checked="checked"')[bool(sickbeard.KODI_ALWAYS_ON)]}/>
                                         <p>log errors when unreachable ?</p>
                                     </span>
                                 </label>
@@ -62,7 +58,7 @@
                                 <label for="kodi_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_notify_onsnatch" id="kodi_notify_onsnatch" #if $sickbeard.KODI_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_notify_onsnatch" id="kodi_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.KODI_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -71,7 +67,7 @@
                                 <label for="kodi_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_notify_ondownload" id="kodi_notify_ondownload" #if $sickbeard.KODI_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_notify_ondownload" id="kodi_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.KODI_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -80,7 +76,7 @@
                                 <label for="kodi_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_notify_onsubtitledownload" id="kodi_notify_onsubtitledownload" #if $sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_notify_onsubtitledownload" id="kodi_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -89,7 +85,7 @@
                                 <label for="kodi_update_library">
                                     <span class="component-title">Update library</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_update_library" id="kodi_update_library" #if $sickbeard.KODI_UPDATE_LIBRARY then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_update_library" id="kodi_update_library" ${('', 'checked="checked"')[bool(sickbeard.KODI_UPDATE_LIBRARY)]}/>
                                         <p>update KODI library when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -98,7 +94,7 @@
                                 <label for="kodi_update_full">
                                     <span class="component-title">Full library update</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_update_full" id="kodi_update_full" #if $sickbeard.KODI_UPDATE_FULL then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_update_full" id="kodi_update_full" ${('', 'checked="checked"')[bool(sickbeard.KODI_UPDATE_FULL)]}/>
                                         <p>perform a full library update if update per-show fails ?</p>
                                     </span>
                                 </label>
@@ -107,7 +103,7 @@
                                 <label for="kodi_update_onlyfirst">
                                     <span class="component-title">Only update first host</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="kodi_update_onlyfirst" id="kodi_update_onlyfirst" #if $sickbeard.KODI_UPDATE_ONLYFIRST then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="kodi_update_onlyfirst" id="kodi_update_onlyfirst" ${('', 'checked="checked"')[bool(sickbeard.KODI_UPDATE_ONLYFIRST)]}/>
                                         <p>only send library updates to the first active host ?</p>
                                     </span>
                                 </label>
@@ -115,7 +111,7 @@
                             <div class="field-pair">
                                 <label for="kodi_host">
                                     <span class="component-title">KODI IP:Port</span>
-                                    <input type="text" name="kodi_host" id="kodi_host" value="$sickbeard.KODI_HOST" class="form-control input-sm input350" />
+                                    <input type="text" name="kodi_host" id="kodi_host" value="${sickbeard.KODI_HOST}" class="form-control input-sm input350" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -129,7 +125,7 @@
                             <div class="field-pair">
                                 <label for="kodi_username">
                                     <span class="component-title">KODI username</span>
-                                    <input type="text" name="kodi_username" id="kodi_username" value="$sickbeard.KODI_USERNAME" class="form-control input-sm input250" />
+                                    <input type="text" name="kodi_username" id="kodi_username" value="${sickbeard.KODI_USERNAME}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -139,7 +135,7 @@
                             <div class="field-pair">
                                 <label for="kodi_password">
                                     <span class="component-title">KODI password</span>
-                                    <input type="password" name="kodi_password" id="kodi_password" value="$sickbeard.KODI_PASSWORD" class="form-control input-sm input250" />
+                                    <input type="password" name="kodi_password" id="kodi_password" value="${sickbeard.KODI_PASSWORD}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -158,8 +154,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" />
-                        <h3><a href="<%= anon_url('http://www.plexapp.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Plex Media Server</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/plex.png" alt="" title="Plex Media Server" />
+                        <h3><a href="${anon_url('http://www.plexapp.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Plex Media Server</a></h3>
                         <p>Experience your media on a visually stunning, easy to use interface on your Mac connected to your TV. Your media library has never looked this good!</p>
                         <p class="plexinfo hide">For sending notifications to Plex Home Theater (PHT) clients, use the KODI notifier with port <b>3005</b>.</p>
                     </div>
@@ -168,7 +164,7 @@
                             <label for="use_plex">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_plex" id="use_plex" #if $sickbeard.USE_PLEX then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_plex" id="use_plex" ${('', 'checked="checked"')[bool(sickbeard.USE_PLEX)]}/>
                                     <p>should SickRage send Plex commands ?</p>
                                 </span>
                             </label>
@@ -178,7 +174,7 @@
                             <div class="field-pair">
                                 <label for="plex_server_token">
                                     <span class="component-title">Plex Media Server Auth Token</span>
-                                    <input type="text" name="plex_server_token" id="plex_server_token" value="$sickbeard.PLEX_SERVER_TOKEN" class="form-control input-sm input250" />
+                                    <input type="text" name="plex_server_token" id="plex_server_token" value="${sickbeard.PLEX_SERVER_TOKEN}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -186,7 +182,7 @@
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <span class="component-desc">(<a href="<%= anon_url('https://support.plex.tv/hc/en-us/articles/204059436-Finding-your-account-token-X-Plex-Token') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><u>Finding your account token</u></a>)</span>
+                                    <span class="component-desc">(<a href="${anon_url('https://support.plex.tv/hc/en-us/articles/204059436-Finding-your-account-token-X-Plex-Token')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><u>Finding your account token</u></a>)</span>
                                 </label>
                             </div>
                             <div class="component-group" style="padding: 0; min-height: 130px">
@@ -194,7 +190,7 @@
                                     <label for="plex_username">
                                         <span class="component-title">Server Username</span>
                                         <span class="component-desc">
-                                            <input type="text" name="plex_username" id="plex_username" value="$sickbeard.PLEX_USERNAME" class="form-control input-sm input250" />
+                                            <input type="text" name="plex_username" id="plex_username" value="${sickbeard.PLEX_USERNAME}" class="form-control input-sm input250" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -203,7 +199,7 @@
                                     <label for="plex_password">
                                         <span class="component-title">Server/client password</span>
                                         <span class="component-desc">
-                                            <input type="password" name="plex_password" id="plex_password" value="#echo '*' * len($sickbeard.PLEX_PASSWORD)#" class="form-control input-sm input250" />
+                                            <input type="password" name="plex_password" id="plex_password" value="${'*' * len(sickbeard.PLEX_PASSWORD)}" class="form-control input-sm input250" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -215,7 +211,7 @@
                                     <label for="plex_update_library">
                                         <span class="component-title">Update server library</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" class="enabler" name="plex_update_library" id="plex_update_library" #if $sickbeard.PLEX_UPDATE_LIBRARY then 'checked="checked" ' else ''#/>
+                                            <input type="checkbox" class="enabler" name="plex_update_library" id="plex_update_library" ${('', 'checked="checked"')[bool(sickbeard.PLEX_UPDATE_LIBRARY)]}/>
                                             <p>update Plex Media Server library when a download finishes</p>
                                         </span>
                                     </label>
@@ -225,7 +221,7 @@
                                         <label for="plex_server_host">
                                             <span class="component-title">Plex Media Server IP:Port</span>
                                             <span class="component-desc">
-                                                <input type="text" name="plex_server_host" id="plex_server_host" value="<%= re.sub(r'\b,\b', ', ', sickbeard.PLEX_SERVER_HOST) %>" class="form-control input-sm input350" />
+                                                <input type="text" name="plex_server_host" id="plex_server_host" value="${re.sub(r'\b,\b', ', ', sickbeard.PLEX_SERVER_HOST)}" class="form-control input-sm input350" />
                                                 <div class="clear-left">
                                                     <p>one or more hosts running Plex Media Server<br />(eg. 192.168.1.1:32400, 192.168.1.2:32400)</p>
                                                 </div>
@@ -247,15 +243,15 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Client" />
-                        <h3><a href="<%= anon_url('http://www.plexapp.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Plex Media Client</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/plex.png" alt="" title="Plex Media Client" />
+                        <h3><a href="${anon_url('http://www.plexapp.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Plex Media Client</a></h3>
                     </div>
                     <fieldset class="component-group-list">
                         <div class="field-pair">
                             <label for="use_plex_client">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_plex" id="use_plex_client" #if $sickbeard.USE_PLEX_CLIENT then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_plex" id="use_plex_client" ${('', 'checked="checked"')[bool(sickbeard.USE_PLEX_CLIENT)]}/>
                                     <p>should SickRage send Plex commands ?</p>
                                 </span>
                             </label>
@@ -266,7 +262,7 @@
                                 <label for="plex_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="plex_notify_onsnatch" id="plex_notify_onsnatch" #if $sickbeard.PLEX_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="plex_notify_onsnatch" id="plex_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.PLEX_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -275,7 +271,7 @@
                                 <label for="plex_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="plex_notify_ondownload" id="plex_notify_ondownload" #if $sickbeard.PLEX_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="plex_notify_ondownload" id="plex_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.PLEX_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -284,7 +280,7 @@
                                 <label for="plex_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="plex_notify_onsubtitledownload" id="plex_notify_onsubtitledownload" #if $sickbeard.PLEX_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="plex_notify_onsubtitledownload" id="plex_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.PLEX_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -293,7 +289,7 @@
                                 <label for="plex_host">
                                     <span class="component-title">Plex Client IP:Port</span>
                                     <span class="component-desc">
-                                        <input type="text" name="plex_host" id="plex_host" value="$sickbeard.PLEX_HOST" class="form-control input-sm input350" />
+                                        <input type="text" name="plex_host" id="plex_host" value="${sickbeard.PLEX_HOST}" class="form-control input-sm input350" />
                                         <div class="clear-left">
                                             <p>one or more hosts running Plex client<br />(eg. 192.168.1.100:3000, 192.168.1.101:3000)</p>
                                         </div>
@@ -305,7 +301,7 @@
                                     <label for="plex_username">
                                         <span class="component-title">Server Username</span>
                                         <span class="component-desc">
-                                            <input type="text" name="plex_client_username" id="plex_client_username" value="$sickbeard.PLEX_CLIENT_USERNAME" class="form-control input-sm input250" />
+                                            <input type="text" name="plex_client_username" id="plex_client_username" value="${sickbeard.PLEX_CLIENT_USERNAME}" class="form-control input-sm input250" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -314,7 +310,7 @@
                                     <label for="plex_client_password">
                                         <span class="component-title">Client Password</span>
                                         <span class="component-desc">
-                                            <input type="password" name="plex_client_password" id="plex_client_password" value="#echo '*' * len($sickbeard.PLEX_CLIENT_PASSWORD)#" class="form-control input-sm input250" />
+                                            <input type="password" name="plex_client_password" id="plex_client_password" value="${'*' * len(sickbeard.PLEX_CLIENT_PASSWORD)}" class="form-control input-sm input250" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -334,8 +330,8 @@
 
                  <div class="component-group">
                      <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/emby.png" alt="" title="Emby" />
-                        <h3><a href="<%= anon_url('http://emby.media/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Emby</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/emby.png" alt="" title="Emby" />
+                        <h3><a href="${anon_url('http://emby.media/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Emby</a></h3>
                         <p>A home media server built using other popular open source technologies.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -343,7 +339,7 @@
                             <label for="use_emby">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_emby" id="use_emby" #if $sickbeard.USE_EMBY then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_emby" id="use_emby" ${('', 'checked="checked"')[bool(sickbeard.USE_EMBY)]} />
                                     <p>should SickRage send update commands to Emby?<p>
                                 </span>
                             </label>
@@ -352,7 +348,7 @@
                             <div class="field-pair">
                                 <label for="emby_host">
                                     <span class="component-title">Emby IP:Port</span>
-                                    <input type="text" name="emby_host" id="emby_host" value="$sickbeard.EMBY_HOST" class="form-control input-sm input250" />
+                                    <input type="text" name="emby_host" id="emby_host" value="${sickbeard.EMBY_HOST}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -362,7 +358,7 @@
                             <div class="field-pair">
                                 <label for="emby_apikey">
                                     <span class="component-title">Emby API Key</span>
-                                    <input type="text" name="emby_apikey" id="emby_apikey" value="$sickbeard.EMBY_APIKEY" class="form-control input-sm input250" />
+                                    <input type="text" name="emby_apikey" id="emby_apikey" value="${sickbeard.EMBY_APIKEY}" class="form-control input-sm input250" />
                                 </label>
                             </div>
                             <div class="testNotification" id="testEMBY-result">Click below to test.</div>
@@ -376,8 +372,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/nmj.png" alt="" title="Networked Media Jukebox" />
-                        <h3><a href="<%= anon_url('http://www.popcornhour.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">NMJ</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/nmj.png" alt="" title="Networked Media Jukebox" />
+                        <h3><a href="${anon_url('http://www.popcornhour.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">NMJ</a></h3>
                         <p>The Networked Media Jukebox, or NMJ, is the official media jukebox interface made available for the Popcorn Hour 200-series.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -385,7 +381,7 @@
                             <label for="use_nmj">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_nmj" id="use_nmj" #if $sickbeard.USE_NMJ then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_nmj" id="use_nmj" ${('', 'checked="checked"')[bool(sickbeard.USE_NMJ)]}/>
                                     <p>should SickRage send update commands to NMJ ?</p>
                                 </span>
                             </label>
@@ -395,7 +391,7 @@
                             <div class="field-pair">
                                 <label for="nmj_host">
                                     <span class="component-title">Popcorn IP address</span>
-                                    <input type="text" name="nmj_host" id="nmj_host" value="$sickbeard.NMJ_HOST" class="form-control input-sm input250" />
+                                    <input type="text" name="nmj_host" id="nmj_host" value="${sickbeard.NMJ_HOST}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -415,7 +411,7 @@
                             <div class="field-pair">
                                 <label for="nmj_database">
                                     <span class="component-title">NMJ database</span>
-                                    <input type="text" name="nmj_database" id="nmj_database" value="$sickbeard.NMJ_DATABASE" class="form-control input-sm input250" #if $sickbeard.NMJ_DATABASE then "readonly=\"readonly\"" else ""# />
+                                    <input type="text" name="nmj_database" id="nmj_database" value="${sickbeard.NMJ_DATABASE}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJ_DATABASE == True]}/>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -425,7 +421,7 @@
                             <div class="field-pair">
                                 <label for="nmj_mount">
                                     <span class="component-title">NMJ mount url</span>
-                                    <input type="text" name="nmj_mount" id="nmj_mount" value="$sickbeard.NMJ_MOUNT" class="form-control input-sm input250" #if $sickbeard.NMJ_MOUNT then "readonly=\"readonly\"" else ""# />
+                                    <input type="text" name="nmj_mount" id="nmj_mount" value="${sickbeard.NMJ_MOUNT}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJ_MOUNT == True]}/>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -433,7 +429,7 @@
                                 </label>
                             </div>
                             <div class="testNotification" id="testNMJ-result">Click below to test.</div>
-                            <input  class="btn" type="button" value="Test NMJ" id="testNMJ" />
+                            <input class="btn" type="button" value="Test NMJ" id="testNMJ" />
                             <input type="submit" class="config_submitter btn" value="Save Changes" />
                         </div><!-- /content_use_nmj //-->
 
@@ -442,8 +438,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/nmj.png" alt="" title="Networked Media Jukebox v2"/>
-                        <h3><a href="<%= anon_url('http://www.popcornhour.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">NMJv2</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/nmj.png" alt="" title="Networked Media Jukebox v2"/>
+                        <h3><a href="${anon_url('http://www.popcornhour.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">NMJv2</a></h3>
                         <p>The Networked Media Jukebox, or NMJv2, is the official media jukebox interface made available for the Popcorn Hour 300 & 400-series.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -451,7 +447,7 @@
                             <label for="use_nmjv2">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_nmjv2" id="use_nmjv2" #if $sickbeard.USE_NMJv2 then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_nmjv2" id="use_nmjv2" ${('', 'checked="checked"')[bool(sickbeard.USE_NMJv2)]}/>
                                     <p>should SickRage send update commands to NMJv2 ?</p>
                                 </span>
                             </label>
@@ -461,7 +457,7 @@
                             <div class="field-pair">
                                 <label for="nmjv2_host">
                                     <span class="component-title">Popcorn IP address</span>
-                                    <input type="text" name="nmjv2_host" id="nmjv2_host" value="$sickbeard.NMJv2_HOST" class="form-control input-sm input250" />
+                                    <input type="text" name="nmjv2_host" id="nmjv2_host" value="${sickbeard.NMJv2_HOST}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -472,10 +468,10 @@
                                 <span class="component-title">Database location</span>
                                 <span class="component-desc">
                                     <label for="NMJV2_DBLOC_A" class="space-right">
-                                        <input type="radio" NAME="nmjv2_dbloc" VALUE="local" id="NMJV2_DBLOC_A" #if $sickbeard.NMJv2_DBLOC=="local" then "checked=\"checked\"" else ""# />PCH Local Media
+                                        <input type="radio" NAME="nmjv2_dbloc" VALUE="local" id="NMJV2_DBLOC_A" ${('', 'checked="checked"')[sickbeard.NMJv2_DBLOC == 'local']}/>PCH Local Media
                                     </label>
                                     <label for="NMJV2_DBLOC_B">
-                                        <input type="radio" NAME="nmjv2_dbloc" VALUE="network" id="NMJV2_DBLOC_B" #if $sickbeard.NMJv2_DBLOC=="network" then "checked=\"checked\"" else ""#/>PCH Network Media
+                                        <input type="radio" NAME="nmjv2_dbloc" VALUE="network" id="NMJV2_DBLOC_B" ${('', 'checked="checked"')[sickbeard.NMJv2_DBLOC == 'network']}/>PCH Network Media
                                     </label>
                                 </span>
                             </div>
@@ -512,7 +508,7 @@
                             <div class="field-pair">
                                 <label for="nmjv2_database">
                                     <span class="component-title">NMJv2 database</span>
-                                    <input type="text" name="nmjv2_database" id="nmjv2_database" value="$sickbeard.NMJv2_DATABASE" class="form-control input-sm input250" #if $sickbeard.NMJv2_DATABASE then "readonly=\"readonly\"" else ""# />
+                                    <input type="text" name="nmjv2_database" id="nmjv2_database" value="${sickbeard.NMJv2_DATABASE}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJv2_DATABASE == True]}/>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -530,8 +526,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/synoindex.png" alt="" title="Synology" />
-                        <h3><a href="<%= anon_url('http://synology.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Synology</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/synoindex.png" alt="" title="Synology" />
+                        <h3><a href="${anon_url('http://synology.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Synology</a></h3>
                         <p>The Synology DiskStation NAS.</p>
                         <p>Synology Indexer is the daemon running on the Synology NAS to build its media database.</p>
                     </div>
@@ -541,7 +537,7 @@
                             <label for="use_synoindex">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_synoindex" id="use_synoindex" #if $sickbeard.USE_SYNOINDEX then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_synoindex" id="use_synoindex" ${('', 'checked="checked"')[bool(sickbeard.USE_SYNOINDEX)]}/>
                                     <p>should SickRage send Synology notifications ?</p>
                                 </span>
                             </label>
@@ -561,8 +557,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/synologynotifier.png" alt="" title="Synology Indexer" />
-                        <h3><a href="<%= anon_url('http://synology.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Synology Notifier</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/synologynotifier.png" alt="" title="Synology Indexer" />
+                        <h3><a href="${anon_url('http://synology.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Synology Notifier</a></h3>
                         <p>Synology Notifier is the notification system of Synology DSM</p>
                     </div>
 
@@ -571,7 +567,7 @@
                             <label for="use_synologynotifier">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_synologynotifier" id="use_synologynotifier" #if $sickbeard.USE_SYNOLOGYNOTIFIER then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_synologynotifier" id="use_synologynotifier" ${('', 'checked="checked"')[bool(sickbeard.USE_SYNOLOGYNOTIFIER)]}/>
                                     <p>should SickRage send notifications to the Synology Notifier ?</p>
                                 </span>
                             </label>
@@ -585,7 +581,7 @@
                                 <label for="synologynotifier_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="synologynotifier_notify_onsnatch" id="synologynotifier_notify_onsnatch" #if $sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="synologynotifier_notify_onsnatch" id="synologynotifier_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -594,7 +590,7 @@
                                 <label for="synologynotifier_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="synologynotifier_notify_ondownload" id="synologynotifier_notify_ondownload" #if $sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="synologynotifier_notify_ondownload" id="synologynotifier_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -603,7 +599,7 @@
                                 <label for="synologynotifier_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="synologynotifier_notify_onsubtitledownload" id="synologynotifier_notify_onsubtitledownload" #if $sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="synologynotifier_notify_onsubtitledownload" id="synologynotifier_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -616,8 +612,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/pytivo.png" alt="" title="pyTivo" />
-                        <h3><a href="<%= anon_url('http://pytivo.sourceforge.net/wiki/index.php/PyTivo') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">pyTivo</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/pytivo.png" alt="" title="pyTivo" />
+                        <h3><a href="${anon_url('http://pytivo.sourceforge.net/wiki/index.php/PyTivo')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">pyTivo</a></h3>
                         <p>pyTivo is both an HMO and GoBack server. This notifier will load the completed downloads to your Tivo.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -625,7 +621,7 @@
                             <label for="use_pytivo">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_pytivo" id="use_pytivo" #if $sickbeard.USE_PYTIVO then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_pytivo" id="use_pytivo" ${('', 'checked="checked"')[bool(sickbeard.USE_PYTIVO)]}/>
                                     <p>should SickRage send notifications to pyTivo ?</p>
                                 </span>
                             </label>
@@ -639,7 +635,7 @@
                             <div class="field-pair">
                                 <label for="pytivo_host">
                                     <span class="component-title">pyTivo IP:Port</span>
-                                    <input type="text" name="pytivo_host" id="pytivo_host" value="$sickbeard.PYTIVO_HOST" class="form-control input-sm input250" />
+                                    <input type="text" name="pytivo_host" id="pytivo_host" value="${sickbeard.PYTIVO_HOST}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -649,7 +645,7 @@
                             <div class="field-pair">
                                 <label for="pytivo_share_name">
                                     <span class="component-title">pyTivo share name</span>
-                                    <input type="text" name="pytivo_share_name" id="pytivo_share_name" value="$sickbeard.PYTIVO_SHARE_NAME" class="form-control input-sm input250" />
+                                    <input type="text" name="pytivo_share_name" id="pytivo_share_name" value="${sickbeard.PYTIVO_SHARE_NAME}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -659,7 +655,7 @@
                             <div class="field-pair">
                                 <label for="pytivo_tivo_name">
                                     <span class="component-title">Tivo name</span>
-                                    <input type="text" name="pytivo_tivo_name" id="pytivo_tivo_name" value="$sickbeard.PYTIVO_TIVO_NAME" class="form-control input-sm input250" />
+                                    <input type="text" name="pytivo_tivo_name" id="pytivo_tivo_name" value="${sickbeard.PYTIVO_TIVO_NAME}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -678,8 +674,8 @@
             <div id="tabs-2">
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/growl.png" alt="" title="Growl" />
-                        <h3><a href="<%= anon_url('http://growl.info/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Growl</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/growl.png" alt="" title="Growl" />
+                        <h3><a href="${anon_url('http://growl.info/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Growl</a></h3>
                         <p>A cross-platform unobtrusive global notification system.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -687,7 +683,7 @@
                             <label for="use_growl">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_growl" id="use_growl" #if $sickbeard.USE_GROWL then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_growl" id="use_growl" ${('', 'checked="checked"')[bool(sickbeard.USE_GROWL)]}/>
                                     <p>should SickRage send Growl notifications ?</p>
                                 </span>
                             </label>
@@ -698,7 +694,7 @@
                                 <label for="growl_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="growl_notify_onsnatch" id="growl_notify_onsnatch" #if $sickbeard.GROWL_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="growl_notify_onsnatch" id="growl_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.GROWL_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -707,7 +703,7 @@
                                 <label for="growl_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="growl_notify_ondownload" id="growl_notify_ondownload" #if $sickbeard.GROWL_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="growl_notify_ondownload" id="growl_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.GROWL_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -716,7 +712,7 @@
                                 <label for="growl_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="growl_notify_onsubtitledownload" id="growl_notify_onsubtitledownload" #if $sickbeard.GROWL_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="growl_notify_onsubtitledownload" id="growl_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.GROWL_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -724,7 +720,7 @@
                             <div class="field-pair">
                                 <label for="growl_host">
                                     <span class="component-title">Growl IP:Port</span>
-                                    <input type="text" name="growl_host" id="growl_host" value="$sickbeard.GROWL_HOST" class="form-control input-sm input250" />
+                                    <input type="text" name="growl_host" id="growl_host" value="${sickbeard.GROWL_HOST}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -734,7 +730,7 @@
                             <div class="field-pair">
                                 <label for="growl_password">
                                     <span class="component-title">Growl password</span>
-                                    <input type="password" name="growl_password" id="growl_password" value="$sickbeard.GROWL_PASSWORD" class="form-control input-sm input250" />
+                                    <input type="password" name="growl_password" id="growl_password" value="${sickbeard.GROWL_PASSWORD}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -756,8 +752,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/prowl.png" alt="Prowl" title="Prowl" />
-                        <h3><a href="<%= anon_url('http://www.prowlapp.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Prowl</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/prowl.png" alt="Prowl" title="Prowl" />
+                        <h3><a href="${anon_url('http://www.prowlapp.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Prowl</a></h3>
                         <p>A Growl client for iOS.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -765,7 +761,7 @@
                             <label for="use_prowl">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_prowl" id="use_prowl" #if $sickbeard.USE_PROWL then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_prowl" id="use_prowl" ${('', 'checked="checked"')[bool(sickbeard.USE_PROWL)]}/>
                                     <p>should SickRage send Prowl notifications ?</p>
                                 </span>
                             </label>
@@ -776,7 +772,7 @@
                                 <label for="prowl_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="prowl_notify_onsnatch" id="prowl_notify_onsnatch" #if $sickbeard.PROWL_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="prowl_notify_onsnatch" id="prowl_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.PROWL_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -785,7 +781,7 @@
                                 <label for="prowl_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="prowl_notify_ondownload" id="prowl_notify_ondownload" #if $sickbeard.PROWL_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="prowl_notify_ondownload" id="prowl_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.PROWL_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -794,7 +790,7 @@
                                 <label for="prowl_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="prowl_notify_onsubtitledownload" id="prowl_notify_onsubtitledownload" #if $sickbeard.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="prowl_notify_onsubtitledownload" id="prowl_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -802,22 +798,22 @@
                             <div class="field-pair">
                                 <label for="prowl_api">
                                     <span class="component-title">Prowl API key:</span>
-                                    <input type="text" name="prowl_api" id="prowl_api" value="$sickbeard.PROWL_API" class="form-control input-sm input250" />
+                                    <input type="text" name="prowl_api" id="prowl_api" value="${sickbeard.PROWL_API}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <span class="component-desc">get your key at: <a href="<%= anon_url('https://www.prowlapp.com/api_settings.php') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://www.prowlapp.com/api_settings.php</a></span>
+                                    <span class="component-desc">get your key at: <a href="${anon_url('https://www.prowlapp.com/api_settings.php')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://www.prowlapp.com/api_settings.php</a></span>
                                 </label>
                             </div>
                             <div class="field-pair">
                                 <label for="prowl_priority">
                                     <span class="component-title">Prowl priority:</span>
                                     <select id="prowl_priority" name="prowl_priority" class="form-control input-sm">
-                                        <option value="-2" #if $sickbeard.PROWL_PRIORITY == "-2" then 'selected="selected"' else ""#>Very Low</option>
-                                        <option value="-1" #if $sickbeard.PROWL_PRIORITY == "-1" then 'selected="selected"' else ""#>Moderate</option>
-                                        <option value="0" #if $sickbeard.PROWL_PRIORITY == "0" then 'selected="selected"' else ""#>Normal</option>
-                                        <option value="1" #if $sickbeard.PROWL_PRIORITY == "1" then 'selected="selected"' else ""#>High</option>
-                                        <option value="2" #if $sickbeard.PROWL_PRIORITY == "2" then 'selected="selected"' else ""#>Emergency</option>
+                                        <option value="-2" ${('', 'selected="selected"')[sickbeard.PROWL_PRIORITY == '-2']}>Very Low</option>
+                                        <option value="-1" ${('', 'selected="selected"')[sickbeard.PROWL_PRIORITY == '-1']}>Moderate</option>
+                                        <option value="0" ${('', 'selected="selected"')[sickbeard.PROWL_PRIORITY == '0']}>Normal</option>
+                                        <option value="1" ${('', 'selected="selected"')[sickbeard.PROWL_PRIORITY == '1']}>High</option>
+                                        <option value="2" ${('', 'selected="selected"')[sickbeard.PROWL_PRIORITY == '2']}>Emergency</option>
                                     </select>
                                 </label>
                                 <label>
@@ -836,8 +832,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/libnotify.png" alt="" title="Libnotify" />
-                        <h3><a href="<%= anon_url('http://library.gnome.org/devel/libnotify/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Libnotify</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/libnotify.png" alt="" title="Libnotify" />
+                        <h3><a href="${anon_url('http://library.gnome.org/devel/libnotify/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Libnotify</a></h3>
                         <p>The standard desktop notification API for Linux/*nix systems.  This notifier will only function if the pynotify module is installed (Ubuntu/Debian package <a href="apt:python-notify">python-notify</a>).</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -845,7 +841,7 @@
                             <label for="use_libnotify">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_libnotify" id="use_libnotify" #if $sickbeard.USE_LIBNOTIFY then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_libnotify" id="use_libnotify" ${('', 'checked="checked"')[bool(sickbeard.USE_LIBNOTIFY)]}/>
                                     <p>should SickRage send Libnotify notifications ?</p>
                                 </span>
                             </label>
@@ -856,7 +852,7 @@
                                 <label for="libnotify_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="libnotify_notify_onsnatch" id="libnotify_notify_onsnatch" #if $sickbeard.LIBNOTIFY_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="libnotify_notify_onsnatch" id="libnotify_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.LIBNOTIFY_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -865,7 +861,7 @@
                                 <label for="libnotify_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="libnotify_notify_ondownload" id="libnotify_notify_ondownload" #if $sickbeard.LIBNOTIFY_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="libnotify_notify_ondownload" id="libnotify_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.LIBNOTIFY_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -874,7 +870,7 @@
                                 <label for="libnotify_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="libnotify_notify_onsubtitledownload" id="libnotify_notify_onsubtitledownload" #if $sickbeard.LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="libnotify_notify_onsubtitledownload" id="libnotify_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -890,8 +886,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/pushover.png" alt="" title="Pushover" />
-                        <h3><a href="<%= anon_url('https://pushover.net/apps/clone/sickrage') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Pushover</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/pushover.png" alt="" title="Pushover" />
+                        <h3><a href="${anon_url('https://pushover.net/apps/clone/sickrage')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Pushover</a></h3>
                         <p>Pushover makes it easy to send real-time notifications to your Android and iOS devices.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -899,7 +895,7 @@
                             <label for="use_pushover">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_pushover" id="use_pushover" #if $sickbeard.USE_PUSHOVER then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_pushover" id="use_pushover" ${('', 'checked="checked"')[bool(sickbeard.USE_PUSHOVER)]}/>
                                     <p>should SickRage send Pushover notifications ?</p>
                                 </span>
                             </label>
@@ -910,7 +906,7 @@
                                 <label for="pushover_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushover_notify_onsnatch" id="pushover_notify_onsnatch" #if $sickbeard.PUSHOVER_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushover_notify_onsnatch" id="pushover_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.PUSHOVER_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -919,7 +915,7 @@
                                 <label for="pushover_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushover_notify_ondownload" id="pushover_notify_ondownload" #if $sickbeard.PUSHOVER_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushover_notify_ondownload" id="pushover_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.PUSHOVER_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -928,7 +924,7 @@
                                 <label for="pushover_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushover_notify_onsubtitledownload" id="pushover_notify_onsubtitledownload" #if $sickbeard.PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushover_notify_onsubtitledownload" id="pushover_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -936,7 +932,7 @@
                             <div class="field-pair">
                                 <label for="pushover_userkey">
                                     <span class="component-title">Pushover key</span>
-                                    <input type="text" name="pushover_userkey" id="pushover_userkey" value="$sickbeard.PUSHOVER_USERKEY" class="form-control input-sm input250" />
+                                    <input type="text" name="pushover_userkey" id="pushover_userkey" value="${sickbeard.PUSHOVER_USERKEY}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -946,23 +942,56 @@
                             <div class="field-pair">
                                 <label for="pushover_apikey">
                                     <span class="component-title">Pushover API key</span>
-                                    <input type="text" name="pushover_apikey" id="pushover_apikey" value="$sickbeard.PUSHOVER_APIKEY" class="form-control input-sm input250" />
+                                    <input type="text" name="pushover_apikey" id="pushover_apikey" value="${sickbeard.PUSHOVER_APIKEY}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <span class="component-desc"><a href="<%= anon_url('https://pushover.net/apps/clone/sickrage') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><b>Click here</b></a> to create a Pushover API key</span>
+                                    <span class="component-desc"><a href="${anon_url('https://pushover.net/apps/clone/sickrage')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><b>Click here</b></a> to create a Pushover API key</span>
                                 </label>
                             </div>
                             <div class="field-pair">
                                 <label for="pushover_device">
                                     <span class="component-title">Pushover devices</span>
-                                    <input type="text" name="pushover_device" id="pushover_device" value="$sickbeard.PUSHOVER_DEVICE" class="form-control input-sm input250" />
+                                    <input type="text" name="pushover_device" id="pushover_device" value="${sickbeard.PUSHOVER_DEVICE}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
                                     <span class="component-desc">comma separated list of pushover devices you want to send notifications to</span>
                                 </label>
                             </div>
+                            <div class="field-pair">
+                                <label for="pushover_sound">
+                                    <span class="component-title">Pushover notification sound</span>
+                                    <select id="pushover_sound" name="pushover_sound" class="form-control input-sm">
+                                        <option value="pushover" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'pushover']}>Pushover</option>
+                                        <option value="bike" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'bike']}>Bike</option>
+                                        <option value="bugle" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'bugle']}>Bugle</option>
+                                        <option value="cashregister" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'cashregister']}>Cash Register</option>
+                                        <option value="classical" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'classical']}>Classical</option>
+                                        <option value="cosmic" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'cosmic']}>Cosmic</option>
+                                        <option value="falling" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'falling']}>Falling</option>
+                                        <option value="gamelan" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'gamelan']}>Gamelan</option>
+                                        <option value="incoming" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'incoming']}> Incoming</option>
+                                        <option value="intermission" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'intermission']}>Intermission</option>
+                                        <option value="magic" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'magic']}>Magic</option>
+                                        <option value="mechanical" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'mechanical']}>Mechanical</option>
+                                        <option value="pianobar" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'pianobar']}>Piano Bar</option>
+                                        <option value="siren" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'siren']}>Siren</option>
+                                        <option value="spacealarm" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'spacealarm']}>Space Alarm</option>
+                                        <option value="tugboat" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'tugboat']}>Tug Boat</option>
+                                        <option value="alien" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'alien']}>Alien Alarm (long)</option>
+                                        <option value="climb" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'climb']}>Climb (long)</option>
+                                        <option value="persistent" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'persistent']}>Persistent (long)</option>
+                                        <option value="echo" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'echo']}>Pushover Echo (long)</option>
+                                        <option value="updown" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'updown']}>Up Down (long)</option>
+                                        <option value="none" ${('', 'selected="selected"')[sickbeard.PUSHOVER_SOUND == 'none']}>None (silent)</option>
+                                    </select>
+                                </label>
+                                <label>
+                                    <span class="component-title">&nbsp;</span>
+                                    <span class="component-desc">Choose notification sound to use</span>
+                                </label>
+                            </div>
                             <div class="testNotification" id="testPushover-result">Click below to test.</div>
                             <input  class="btn" type="button" value="Test Pushover" id="testPushover" />
                             <input type="submit" class="config_submitter btn" value="Save Changes" />
@@ -973,8 +1002,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/boxcar.png" alt="" title="Boxcar" />
-                        <h3><a href="<%= anon_url('http://boxcar.io/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Boxcar</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/boxcar.png" alt="" title="Boxcar" />
+                        <h3><a href="${anon_url('http://boxcar.io/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Boxcar</a></h3>
                         <p>Universal push notification for iOS. Read your messages where and when you want them! A subscription will be sent if needed.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -982,7 +1011,7 @@
                             <label for="use_boxcar">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_boxcar" id="use_boxcar" #if $sickbeard.USE_BOXCAR then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_boxcar" id="use_boxcar" ${('', 'checked="checked"')[bool(sickbeard.USE_BOXCAR)]}/>
                                     <p>should SickRage send Boxcar notifications ?</p>
                                 </span>
                             </label>
@@ -993,7 +1022,7 @@
                                 <label for="boxcar_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="boxcar_notify_onsnatch" id="boxcar_notify_onsnatch" #if $sickbeard.BOXCAR_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="boxcar_notify_onsnatch" id="boxcar_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.BOXCAR_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1002,7 +1031,7 @@
                                 <label for="boxcar_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="boxcar_notify_ondownload" id="boxcar_notify_ondownload" #if $sickbeard.BOXCAR_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="boxcar_notify_ondownload" id="boxcar_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.BOXCAR_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1011,7 +1040,7 @@
                                 <label for="boxcar_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="boxcar_notify_onsubtitledownload" id="boxcar_notify_onsubtitledownload" #if $sickbeard.BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="boxcar_notify_onsubtitledownload" id="boxcar_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1019,7 +1048,7 @@
                             <div class="field-pair">
                                 <label for="boxcar_username">
                                     <span class="component-title">Boxcar username</span>
-                                    <input type="text" name="boxcar_username" id="boxcar_username" value="$sickbeard.BOXCAR_USERNAME" class="form-control input-sm input250" />
+                                    <input type="text" name="boxcar_username" id="boxcar_username" value="${sickbeard.BOXCAR_USERNAME}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1036,8 +1065,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/boxcar2.png" alt="" title="Boxcar2"/>
-                        <h3><a href="<%= anon_url('https://new.boxcar.io/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Boxcar2</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/boxcar2.png" alt="" title="Boxcar2"/>
+                        <h3><a href="${anon_url('https://new.boxcar.io/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Boxcar2</a></h3>
                         <p>Read your messages where and when you want them!</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1045,7 +1074,7 @@
                             <label for="use_boxcar2">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_boxcar2" id="use_boxcar2" #if $sickbeard.USE_BOXCAR2 then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_boxcar2" id="use_boxcar2" ${('', 'checked="checked"')[bool(sickbeard.USE_BOXCAR2)]}/>
                                     <p>should SickRage send Boxcar2 notifications ?</p>
                                 </span>
                             </label>
@@ -1056,7 +1085,7 @@
                                 <label for="boxcar2_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="boxcar2_notify_onsnatch" id="boxcar2_notify_onsnatch" #if $sickbeard.BOXCAR2_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="boxcar2_notify_onsnatch" id="boxcar2_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.BOXCAR2_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1065,7 +1094,7 @@
                                 <label for="boxcar2_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="boxcar2_notify_ondownload" id="boxcar2_notify_ondownload" #if $sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="boxcar2_notify_ondownload" id="boxcar2_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1074,7 +1103,7 @@
                                 <label for="boxcar2_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="boxcar2_notify_onsubtitledownload" id="boxcar2_notify_onsubtitledownload" #if $sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="boxcar2_notify_onsubtitledownload" id="boxcar2_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1082,7 +1111,7 @@
                             <div class="field-pair">
                                 <label for="boxcar2_accesstoken">
                                     <span class="component-title">Boxcar2 access token</span>
-                                    <input type="text" name="boxcar2_accesstoken" id="boxcar2_accesstoken" value="$sickbeard.BOXCAR2_ACCESSTOKEN" class="form-control input-sm input250" />
+                                    <input type="text" name="boxcar2_accesstoken" id="boxcar2_accesstoken" value="${sickbeard.BOXCAR2_ACCESSTOKEN}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1099,8 +1128,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/nma.png" alt="" title="NMA"/>
-                        <h3><a href="<%= anon_url('http://nma.usk.bz') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Notify My Android</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/nma.png" alt="" title="NMA"/>
+                        <h3><a href="${anon_url('http://nma.usk.bz')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Notify My Android</a></h3>
                         <p>Notify My Android is a Prowl-like Android App and API that offers an easy way to send notifications from your application directly to your Android device.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1108,7 +1137,7 @@
                             <label for="use_nma">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_nma" id="use_nma" #if $sickbeard.USE_NMA then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_nma" id="use_nma" ${('', 'checked="checked"')[bool(sickbeard.USE_NMA)]}/>
                                     <p>should SickRage send NMA notifications ?</p>
                                 </span>
                             </label>
@@ -1119,7 +1148,7 @@
                                 <label for="nma_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="nma_notify_onsnatch" id="nma_notify_onsnatch" #if $sickbeard.NMA_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="nma_notify_onsnatch" id="nma_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.NMA_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1128,7 +1157,7 @@
                                 <label for="nma_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="nma_notify_ondownload" id="nma_notify_ondownload" #if $sickbeard.NMA_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="nma_notify_ondownload" id="nma_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.NMA_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1137,7 +1166,7 @@
                                 <label for="nma_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="nma_notify_onsubtitledownload" id="nma_notify_onsubtitledownload" #if $sickbeard.NMA_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="nma_notify_onsubtitledownload" id="nma_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.NMA_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1145,7 +1174,7 @@
                             <div class="field-pair">
                                 <label for="nma_api">
                                        <span class="component-title">NMA API key:</span>
-                                    <input type="text" name="nma_api" id="nma_api" value="$sickbeard.NMA_API" class="form-control input-sm input350" />
+                                    <input type="text" name="nma_api" id="nma_api" value="${sickbeard.NMA_API}" class="form-control input-sm input350" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1156,11 +1185,11 @@
                                 <label for="nma_priority">
                                     <span class="component-title">NMA priority:</span>
                                        <select id="nma_priority" name="nma_priority" class="form-control input-sm">
-                                        <option value="-2" #if $sickbeard.NMA_PRIORITY == "-2" then 'selected="selected"' else ""#>Very Low</option>
-                                        <option value="-1" #if $sickbeard.NMA_PRIORITY == "-1" then 'selected="selected"' else ""#>Moderate</option>
-                                        <option value="0" #if $sickbeard.NMA_PRIORITY == "0" then 'selected="selected"' else ""#>Normal</option>
-                                        <option value="1" #if $sickbeard.NMA_PRIORITY == "1" then 'selected="selected"' else ""#>High</option>
-                                        <option value="2" #if $sickbeard.NMA_PRIORITY == "2" then 'selected="selected"' else ""#>Emergency</option>
+                                        <option value="-2" ${('', 'selected="selected"')[sickbeard.NMA_PRIORITY == '-2']}>Very Low</option>
+                                        <option value="-1" ${('', 'selected="selected"')[sickbeard.NMA_PRIORITY == '-1']}>Moderate</option>
+                                        <option value="0" ${('', 'selected="selected"')[sickbeard.NMA_PRIORITY == '0']}>Normal</option>
+                                        <option value="1" ${('', 'selected="selected"')[sickbeard.NMA_PRIORITY == '1']}>High</option>
+                                        <option value="2" ${('', 'selected="selected"')[sickbeard.NMA_PRIORITY == '2']}>Emergency</option>
                                     </select>
                                 </label>
                                 <label>
@@ -1178,8 +1207,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/pushalot.png" alt="" title="Pushalot" />
-                        <h3><a href="<%= anon_url('https://pushalot.com') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Pushalot</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/pushalot.png" alt="" title="Pushalot" />
+                        <h3><a href="${anon_url('https://pushalot.com')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Pushalot</a></h3>
                         <p>Pushalot is a platform for receiving custom push notifications to connected devices running Windows Phone or Windows 8.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1187,7 +1216,7 @@
                             <label for="use_pushalot">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_pushalot" id="use_pushalot" #if $sickbeard.USE_PUSHALOT then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_pushalot" id="use_pushalot" ${('', 'checked="checked"')[bool(sickbeard.USE_PUSHALOT)]}/>
                                     <p>should SickRage send Pushalot notifications ?
                                 </span>
                             </label>
@@ -1198,7 +1227,7 @@
                                 <label for="pushalot_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushalot_notify_onsnatch" id="pushalot_notify_onsnatch" #if $sickbeard.PUSHALOT_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushalot_notify_onsnatch" id="pushalot_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.PUSHALOT_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1207,7 +1236,7 @@
                                 <label for="pushalot_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushalot_notify_ondownload" id="pushalot_notify_ondownload" #if $sickbeard.PUSHALOT_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushalot_notify_ondownload" id="pushalot_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.PUSHALOT_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1216,7 +1245,7 @@
                                 <label for="pushalot_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushalot_notify_onsubtitledownload" id="pushalot_notify_onsubtitledownload" #if $sickbeard.PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushalot_notify_onsubtitledownload" id="pushalot_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1224,7 +1253,7 @@
                             <div class="field-pair">
                                 <label for="pushalot_authorizationtoken">
                                     <span class="component-title">Pushalot authorization token</span>
-                                    <input type="text" name="pushalot_authorizationtoken" id="pushalot_authorizationtoken" value="$sickbeard.PUSHALOT_AUTHORIZATIONTOKEN" class="form-control input-sm input350" />
+                                    <input type="text" name="pushalot_authorizationtoken" id="pushalot_authorizationtoken" value="${sickbeard.PUSHALOT_AUTHORIZATIONTOKEN}" class="form-control input-sm input350" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1241,8 +1270,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/pushbullet.png" alt="" title="Pushbullet" />
-                        <h3><a href="<%= anon_url('https://www.pushbullet.com') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Pushbullet</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/pushbullet.png" alt="" title="Pushbullet" />
+                        <h3><a href="${anon_url('https://www.pushbullet.com')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Pushbullet</a></h3>
                         <p>Pushbullet is a platform for receiving custom push notifications to connected devices running Android and desktop Chrome browsers.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1250,7 +1279,7 @@
                             <label for="use_pushbullet">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_pushbullet" id="use_pushbullet" #if $sickbeard.USE_PUSHBULLET then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_pushbullet" id="use_pushbullet" ${('', 'checked="checked"')[bool(sickbeard.USE_PUSHBULLET)]}/>
                                     <p>should SickRage send Pushbullet notifications ?</p>
                                 </span>
                             </label>
@@ -1261,7 +1290,7 @@
                                 <label for="pushbullet_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushbullet_notify_onsnatch" id="pushbullet_notify_onsnatch" #if $sickbeard.PUSHBULLET_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushbullet_notify_onsnatch" id="pushbullet_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.PUSHBULLET_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1270,7 +1299,7 @@
                                 <label for="pushbullet_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushbullet_notify_ondownload" id="pushbullet_notify_ondownload" #if $sickbeard.PUSHBULLET_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushbullet_notify_ondownload" id="pushbullet_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.PUSHBULLET_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1279,7 +1308,7 @@
                                 <label for="pushbullet_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="pushbullet_notify_onsubtitledownload" id="pushbullet_notify_onsubtitledownload" #if $sickbeard.PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="pushbullet_notify_onsubtitledownload" id="pushbullet_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1287,7 +1316,7 @@
                             <div class="field-pair">
                                 <label for="pushbullet_api">
                                     <span class="component-title">Pushbullet API key</span>
-                                    <input type="text" name="pushbullet_api" id="pushbullet_api" value="$sickbeard.PUSHBULLET_API" class="form-control input-sm input350" />
+                                    <input type="text" name="pushbullet_api" id="pushbullet_api" value="${sickbeard.PUSHBULLET_API}" class="form-control input-sm input350" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1298,7 +1327,7 @@
                                 <label for="pushbullet_device_list">
                                     <span class="component-title">Pushbullet devices</span>
                                     <select name="pushbullet_device_list" id="pushbullet_device_list" class="form-control input-sm"></select>
-                                    <input type="hidden" id="pushbullet_device" value="$sickbeard.PUSHBULLET_DEVICE">
+                                    <input type="hidden" id="pushbullet_device" value="${sickbeard.PUSHBULLET_DEVICE}">
                                     <input type="button" class="btn btn-inline" value="Update device list" id="getPushbulletDevices" />
                                 </label>
                                 <label>
@@ -1315,8 +1344,8 @@
                 </div><!-- /pushbullet component-group //-->
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/freemobile.png" alt="" title="Free Mobile" />
-                        <h3><a href="<%= anon_url('http://mobile.free.fr/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Free Mobile</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/freemobile.png" alt="" title="Free Mobile" />
+                        <h3><a href="${anon_url('http://mobile.free.fr/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Free Mobile</a></h3>
                         <p>Free Mobile is a famous French cellular network provider.<br> It provides to their customer a free SMS API.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1324,7 +1353,7 @@
                             <label for="use_freemobile">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_freemobile" id="use_freemobile" #if $sickbeard.USE_FREEMOBILE then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_freemobile" id="use_freemobile" ${('', 'checked="checked"')[bool(sickbeard.USE_FREEMOBILE)]}/>
                                     <p>should SickRage send SMS notifications ?</p>
                                 </span>
                             </label>
@@ -1335,7 +1364,7 @@
                                 <label for="freemobile_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="freemobile_notify_onsnatch" id="freemobile_notify_onsnatch" #if $sickbeard.FREEMOBILE_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="freemobile_notify_onsnatch" id="freemobile_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.FREEMOBILE_NOTIFY_ONSNATCH)]}/>
                                         <p>send a SMS when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1344,7 +1373,7 @@
                                 <label for="freemobile_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="freemobile_notify_ondownload" id="freemobile_notify_ondownload" #if $sickbeard.FREEMOBILE_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="freemobile_notify_ondownload" id="freemobile_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.FREEMOBILE_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a SMS when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1353,7 +1382,7 @@
                                 <label for="freemobile_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="freemobile_notify_onsubtitledownload" id="freemobile_notify_onsubtitledownload" #if $sickbeard.FREEMOBILE_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="freemobile_notify_onsubtitledownload" id="freemobile_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.FREEMOBILE_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a SMS when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1361,7 +1390,7 @@
                             <div class="field-pair">
                                 <label for="freemobile_id">
                                     <span class="component-title">Free Mobile customer ID</span>
-                                    <input type="text" name="freemobile_id" id="freemobile_id" value="$sickbeard.FREEMOBILE_ID" class="form-control input-sm input250" />
+                                    <input type="text" name="freemobile_id" id="freemobile_id" value="${sickbeard.FREEMOBILE_ID}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1371,7 +1400,7 @@
                             <div class="field-pair">
                                 <label for="freemobile_password">
                                     <span class="component-title">Free Mobile API Key</span>
-                                    <input type="text" name="freemobile_apikey" id="freemobile_apikey" value="$sickbeard.FREEMOBILE_APIKEY" class="form-control input-sm input250" />
+                                    <input type="text" name="freemobile_apikey" id="freemobile_apikey" value="${sickbeard.FREEMOBILE_APIKEY}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1391,8 +1420,8 @@
             <div id="tabs-3">
                 <div class="component-group">
                        <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/twitter.png" alt="" title="Twitter"/>
-                        <h3><a href="<%= anon_url('http://www.twitter.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Twitter</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/twitter.png" alt="" title="Twitter"/>
+                        <h3><a href="${anon_url('http://www.twitter.com/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Twitter</a></h3>
                         <p>A social networking and microblogging service, enabling its users to send and read other users' messages called tweets.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1400,7 +1429,7 @@
                             <label for="use_twitter">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_twitter" id="use_twitter" #if $sickbeard.USE_TWITTER then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_twitter" id="use_twitter" ${('', 'checked="checked"')[bool(sickbeard.USE_TWITTER)]}/>
                                     <p>should SickRage post tweets on Twitter ?</p>
                                 </span>
                             </label>
@@ -1415,7 +1444,7 @@
                                 <label for="twitter_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="twitter_notify_onsnatch" id="twitter_notify_onsnatch" #if $sickbeard.TWITTER_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="twitter_notify_onsnatch" id="twitter_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.TWITTER_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1424,7 +1453,7 @@
                                 <label for="twitter_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="twitter_notify_ondownload" id="twitter_notify_ondownload" #if $sickbeard.TWITTER_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="twitter_notify_ondownload" id="twitter_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.TWITTER_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1433,11 +1462,29 @@
                                 <label for="twitter_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="twitter_notify_onsubtitledownload" id="twitter_notify_onsubtitledownload" #if $sickbeard.TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="twitter_notify_onsubtitledownload" id="twitter_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
                             </div>
+                            <div class="field-pair">
+                                <label for="twitter_usedm">
+                                    <span class="component-title">Send direct message</span>
+                                    <span class="component-desc">
+                                        <input type="checkbox" name="twitter_usedm" id="twitter_usedm" ${('', 'checked="checked"')[bool(sickbeard.TWITTER_USEDM)]}/>
+                                        <p>send a notification via Direct Message, not via status update</p>
+                                    </span>
+                                </label>
+                            </div>
+                            <div class="field-pair">
+                                <label for="twitter_dmto">
+                                    <span class="component-title">Send DM to</span>
+                                    <input type="text" name="twitter_dmto" id="twitter_dmto" value="${sickbeard.TWITTER_DMTO}" class="form-control input-sm input250" />
+                                </label>
+                                <p>
+                                    <span class="component-desc">Twitter account to send Direct Messages to (must follow you)</span>
+                                </p>
+                            </div>
                             <div class="field-pair">
                                 <label>
                                     <span class="component-title">Step One</span>
@@ -1475,8 +1522,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/trakt.png" alt="" title="Trakt"/>
-                        <h3><a href="<%= anon_url('http://trakt.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Trakt</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/trakt.png" alt="" title="Trakt"/>
+                        <h3><a href="${anon_url('http://trakt.tv/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Trakt</a></h3>
                         <p>trakt helps keep a record of what TV shows and movies you are watching. Based on your favorites, trakt recommends additional shows and movies you'll enjoy!</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1484,7 +1531,7 @@
                             <label for="use_trakt">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_trakt" id="use_trakt" #if $sickbeard.USE_TRAKT then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_trakt" id="use_trakt" ${('', 'checked="checked"')[bool(sickbeard.USE_TRAKT)]}/>
                                     <p>should SickRage send Trakt.tv notifications ?</p>
                                 </span>
                             </label>
@@ -1494,14 +1541,14 @@
                             <div class="field-pair">
                                 <label for="trakt_username">
                                     <span class="component-title">Trakt username</span>
-                                    <input type="text" name="trakt_username" id="trakt_username" value="$sickbeard.TRAKT_USERNAME" class="form-control input-sm input250" />
+                                    <input type="text" name="trakt_username" id="trakt_username" value="${sickbeard.TRAKT_USERNAME}" class="form-control input-sm input250" />
                                 </label>
                                 <p>
                                     <span class="component-desc">username of your Trakt account.</span>
                                 </p>
                             </div>
-                            <input type="hidden" id="trakt_pin_url" value="$sickbeard.TRAKT_PIN_URL">
-                            <input type="button"#if $sickbeard.TRAKT_ACCESS_TOKEN then "class=\"btn hide\"" else "class=\"btn\""# value="Get Trakt PIN" id="TraktGetPin" />
+                            <input type="hidden" id="trakt_pin_url" value="${sickbeard.TRAKT_PIN_URL}">
+                            <input type="button" class="btn ${('', 'hide')[bool(sickbeard.TRAKT_ACCESS_TOKEN)]}" value="Get Trakt PIN" id="TraktGetPin" />
                             <div class="field-pair">
                                 <label for="trakt_pin">
                                     <span class="component-title">Trakt PIN</span>
@@ -1515,7 +1562,7 @@
                             <div class="field-pair">
                                 <label for="trakt_timeout">
                                     <span class="component-title">API Timeout</span>
-                                    <input type="text" name="trakt_timeout" id="trakt_timeout" value="$sickbeard.TRAKT_TIMEOUT" class="form-control input-sm input75" />
+                                    <input type="text" name="trakt_timeout" id="trakt_timeout" value="${sickbeard.TRAKT_TIMEOUT}" class="form-control input-sm input75" />
                                 </label>
                                 <p>
                                     <span class="component-desc">
@@ -1528,9 +1575,9 @@
                                     <span class="component-title">Default indexer</span>
                                     <span class="component-desc">
                                         <select id="trakt_default_indexer" name="trakt_default_indexer" class="form-control input-sm">
-                                            #for $indexer in $sickbeard.indexerApi().indexers
-                                            <option value="$indexer" #if $indexer == $sickbeard.TRAKT_DEFAULT_INDEXER then "selected=\"selected\"" else ""#>$sickbeard.indexerApi().indexers[$indexer]</option>
-                                            #end for
+                                            % for indexer in sickbeard.indexerApi().indexers:
+                                            <option value="${indexer}" ${('', 'selected="selected"')[sickbeard.TRAKT_DEFAULT_INDEXER == indexer]}>${sickbeard.indexerApi().indexers[indexer]}</option>
+                                            % endfor
                                         </select>
                                     </span>
                                 </label>
@@ -1539,7 +1586,7 @@
                                 <label for="trakt_sync">
                                     <span class="component-title">Sync libraries</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" class="enabler" name="trakt_sync" id="trakt_sync" #if $sickbeard.TRAKT_SYNC then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" class="enabler" name="trakt_sync" id="trakt_sync" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_SYNC)]}/>
                                         <p>sync your SickRage show library with your trakt show library.</p>
                                     </span>
                                 </label>
@@ -1549,7 +1596,7 @@
                                     <label for="trakt_sync_remove">
                                         <span class="component-title">Remove Episodes From Collection</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="trakt_sync_remove" id="trakt_sync_remove" #if $sickbeard.TRAKT_SYNC_REMOVE then "checked=\"checked\"" else ""# />
+                                            <input type="checkbox" name="trakt_sync_remove" id="trakt_sync_remove" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_SYNC_REMOVE)]}/>
                                             <p>Remove an Episode from your Trakt Collection if it is not in your SickRage Library.</p>
                                         </span>
                                     </label>
@@ -1559,7 +1606,7 @@
                                 <label for="trakt_sync_watchlist">
                                     <span class="component-title">Sync watchlist</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" class="enabler" name="trakt_sync_watchlist" id="trakt_sync_watchlist" #if $sickbeard.TRAKT_SYNC_WATCHLIST then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" class="enabler" name="trakt_sync_watchlist" id="trakt_sync_watchlist" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_SYNC_WATCHLIST)]}/>
                                         <p>sync your SickRage show watchlist with your trakt show watchlist (either Show and Episode).</p>
                                         <p>Episode will be added on watch list when wanted or snatched and will be removed when downloaded </p>
                                     </span>
@@ -1570,9 +1617,9 @@
                                     <label for="trakt_method_add">
                                         <span class="component-title">Watchlist add method</span>
                                            <select id="trakt_method_add" name="trakt_method_add" class="form-control input-sm">
-                                            <option value="0" #if $sickbeard.TRAKT_METHOD_ADD == 0 then "selected=\"selected\"" else ""#>Skip All</option>
-                                            <option value="1" #if $sickbeard.TRAKT_METHOD_ADD == 1 then "selected=\"selected\"" else ""#>Download Pilot Only</option>
-                                            <option value="2" #if $sickbeard.TRAKT_METHOD_ADD == 2 then "selected=\"selected\"" else ""#>Get whole show</option>
+                                            <option value="0" ${('', 'selected="selected"')[sickbeard.TRAKT_METHOD_ADD == 0]}>Skip All</option>
+                                            <option value="1" ${('', 'selected="selected"')[sickbeard.TRAKT_METHOD_ADD == 1]}>Download Pilot Only</option>
+                                            <option value="2" ${('', 'selected="selected"')[sickbeard.TRAKT_METHOD_ADD == 2]}>Get whole show</option>
                                         </select>
                                     </label>
                                     <label>
@@ -1584,7 +1631,7 @@
                                     <label for="trakt_remove_watchlist">
                                         <span class="component-title">Remove episode</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="trakt_remove_watchlist" id="trakt_remove_watchlist" #if $sickbeard.TRAKT_REMOVE_WATCHLIST then "checked=\"checked\"" else ""# />
+                                            <input type="checkbox" name="trakt_remove_watchlist" id="trakt_remove_watchlist" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_REMOVE_WATCHLIST)]}/>
                                             <p>remove an episode from your watchlist after it is downloaded.</p>
                                         </span>
                                     </label>
@@ -1593,7 +1640,7 @@
                                     <label for="trakt_remove_serieslist">
                                         <span class="component-title">Remove series</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="trakt_remove_serieslist" id="trakt_remove_serieslist" #if $sickbeard.TRAKT_REMOVE_SERIESLIST then "checked=\"checked\"" else ""# />
+                                            <input type="checkbox" name="trakt_remove_serieslist" id="trakt_remove_serieslist" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_REMOVE_SERIESLIST)]}/>
                                             <p>remove the whole series from your watchlist after any download.</p>
                                         </span>
                                     </label>
@@ -1602,7 +1649,7 @@
                                     <label for="trakt_remove_show_from_sickrage">
                                         <span class="component-title">Remove watched show:</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="trakt_remove_show_from_sickrage" id="trakt_remove_show_from_sickrage" #if $sickbeard.TRAKT_REMOVE_SHOW_FROM_SICKRAGE then "checked=\"checked\"" else ""# />
+                                            <input type="checkbox" name="trakt_remove_show_from_sickrage" id="trakt_remove_show_from_sickrage" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_REMOVE_SHOW_FROM_SICKRAGE)]}/>
                                             <p>remove the show from sickrage if it's ended and completely watched</p>
                                         </span>
                                     </label>
@@ -1610,24 +1657,24 @@
                                 <div class="field-pair">
                                     <label for="trakt_start_paused">
                                         <span class="component-title">Start paused</span>
-#if not $sickbeard.TRAKT_USE_ROLLING_DOWNLOAD
+% if not sickbeard.TRAKT_USE_ROLLING_DOWNLOAD:
                                         <span class="component-desc">
-                                            <input type="checkbox" name="trakt_start_paused" id="trakt_start_paused" #if $sickbeard.TRAKT_START_PAUSED then "checked=\"checked\"" else ""# />
+                                            <input type="checkbox" name="trakt_start_paused" id="trakt_start_paused" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_START_PAUSED)]}/>
                                             <p>show's grabbed from your trakt watchlist start paused.</p>
                                         </span>
-#else
+% else:
                                         <span class="component-desc">
-                                            <input type="checkbox" name="trakt_start_paused" id="trakt_start_paused" #if $sickbeard.TRAKT_START_PAUSED then "checked=\"checked\"" else ""# disabled="disable"/>
+                                            <input type="checkbox" name="trakt_start_paused" id="trakt_start_paused" #${('', 'checked="checked"')[bool(sickbeard.TRAKT_START_PAUSED)]} disabled="disable"/>
                                             <p>show's grabbed from your trakt watchlist start paused.</p>
                                         </span>
-#end if
+% endif
                                     </label>
                                 </div>
                             </div>
                             <div class="field-pair">
                                 <label for="trakt_blacklist_name">
                                     <span class="component-title">Trakt blackList name</span>
-                                    <input type="text" name="trakt_blacklist_name" id="trakt_blacklist_name" value="$sickbeard.TRAKT_BLACKLIST_NAME" class="form-control input-sm input150" />
+                                    <input type="text" name="trakt_blacklist_name" id="trakt_blacklist_name" value="${sickbeard.TRAKT_BLACKLIST_NAME}" class="form-control input-sm input150" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1638,7 +1685,7 @@
                                 <label for="trakt_use_rolling_download">
                                     <span class="component-title">Use rolling download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" class="enabler" name="trakt_use_rolling_download" id="trakt_use_rolling_download" #if $sickbeard.TRAKT_USE_ROLLING_DOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" class="enabler" name="trakt_use_rolling_download" id="trakt_use_rolling_download" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_USE_ROLLING_DOWNLOAD)]}/>
                                         <p>Collect defined number of episodes after last watched one</p>
                                     </span>
                                 </label>
@@ -1648,7 +1695,7 @@
                                     <label for="trakt_rolling_num_ep">
                                         <span class="component-title">Number of episodes</span>
                                         <span class="component-desc">
-                                        <input type="number" name="trakt_rolling_num_ep" id="trakt_rolling_num_ep" value="$sickbeard.TRAKT_ROLLING_NUM_EP" class="form-control input-sm input75"/>
+                                        <input type="number" name="trakt_rolling_num_ep" id="trakt_rolling_num_ep" value="${sickbeard.TRAKT_ROLLING_NUM_EP}" class="form-control input-sm input75"/>
                                     </label>
                                     <label>
                                         <span class="component-title">&nbsp;</span>
@@ -1658,7 +1705,7 @@
                                 <div class="field-pair">
                                     <label for="trakt_rolling_frequency">
                                         <span class="component-title">Rolling frequency check</span>
-                                        <input type="text" name="trakt_rolling_frequency" id="trakt_rolling_frequency" value="$sickbeard.TRAKT_ROLLING_FREQUENCY" class="form-control input-sm input250" />
+                                        <input type="text" name="trakt_rolling_frequency" id="trakt_rolling_frequency" value="${sickbeard.TRAKT_ROLLING_FREQUENCY}" class="form-control input-sm input250" />
                                     </label>
                                     <p>
                                         <span class="component-desc">Hours between check. (Cannot be lower than 4 hours)</span>
@@ -1668,7 +1715,7 @@
                                     <label for="trakt_rolling_add_paused">
                                         <span class="component-title">Add new show as paused</span>
                                         <span class="component-desc">
-                                        <input type="checkbox" name="trakt_rolling_add_paused" id="trakt_rolling_add_paused" #if $sickbeard.TRAKT_ROLLING_ADD_PAUSED then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="trakt_rolling_add_paused" id="trakt_rolling_add_paused" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_ROLLING_ADD_PAUSED)]}/>
                                     </label>
                                     <label>
                                         <span class="component-title">&nbsp;</span>
@@ -1685,8 +1732,8 @@
 
                 <div class="component-group">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/email.png" alt="" title="Email" />
-                        <h3><a href="<%= anon_url('http://en.wikipedia.org/wiki/Comparison_of_webmail_providers') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Email</a></h3>
+                        <img class="notifier-icon" src="${sbRoot}/images/notifiers/email.png" alt="" title="Email" />
+                        <h3><a href="${anon_url('http://en.wikipedia.org/wiki/Comparison_of_webmail_providers')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Email</a></h3>
                         <p>Allows configuration of email notifications on a per show basis.</p>
                     </div>
                     <fieldset class="component-group-list">
@@ -1694,7 +1741,7 @@
                             <label for="use_email">
                                 <span class="component-title">Enable</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="use_email" id="use_email" #if $sickbeard.USE_EMAIL then "checked=\"checked\"" else ""# />
+                                    <input type="checkbox" class="enabler" name="use_email" id="use_email" ${('', 'checked="checked"')[bool(sickbeard.USE_EMAIL)]}/>
                                     <p>should SickRage send email notifications ?</p>
                                 </span>
                             </label>
@@ -1705,7 +1752,7 @@
                                 <label for="email_notify_onsnatch">
                                     <span class="component-title">Notify on snatch</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="email_notify_onsnatch" id="email_notify_onsnatch" #if $sickbeard.EMAIL_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="email_notify_onsnatch" id="email_notify_onsnatch" ${('', 'checked="checked"')[bool(sickbeard.EMAIL_NOTIFY_ONSNATCH)]}/>
                                         <p>send a notification when a download starts ?</p>
                                     </span>
                                 </label>
@@ -1714,7 +1761,7 @@
                                 <label for="email_notify_ondownload">
                                     <span class="component-title">Notify on download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="email_notify_ondownload" id="email_notify_ondownload" #if $sickbeard.EMAIL_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="email_notify_ondownload" id="email_notify_ondownload" ${('', 'checked="checked"')[bool(sickbeard.EMAIL_NOTIFY_ONDOWNLOAD)]}/>
                                         <p>send a notification when a download finishes ?</p>
                                     </span>
                                 </label>
@@ -1723,7 +1770,7 @@
                                 <label for="email_notify_onsubtitledownload">
                                     <span class="component-title">Notify on subtitle download</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="email_notify_onsubtitledownload" id="email_notify_onsubtitledownload" #if $sickbeard.EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="email_notify_onsubtitledownload" id="email_notify_onsubtitledownload" ${('', 'checked="checked"')[bool(sickbeard.EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD)]}/>
                                         <p>send a notification when subtitles are downloaded ?</p>
                                     </span>
                                 </label>
@@ -1731,7 +1778,7 @@
                             <div class="field-pair">
                                 <label for="email_host">
                                     <span class="component-title">SMTP host</span>
-                                    <input type="text" name="email_host" id="email_host" value="$sickbeard.EMAIL_HOST" class="form-control input-sm input250" />
+                                    <input type="text" name="email_host" id="email_host" value="${sickbeard.EMAIL_HOST}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1741,7 +1788,7 @@
                             <div class="field-pair">
                                 <label for="email_port">
                                     <span class="component-title">SMTP port</span>
-                                    <input type="text" name="email_port" id="email_port" value="$sickbeard.EMAIL_PORT" class="form-control input-sm input75" />
+                                    <input type="text" name="email_port" id="email_port" value="${sickbeard.EMAIL_PORT}" class="form-control input-sm input75" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1751,7 +1798,7 @@
                             <div class="field-pair">
                                 <label for="email_from">
                                     <span class="component-title">SMTP from</span>
-                                    <input type="text" name="email_from" id="email_from" value="$sickbeard.EMAIL_FROM" class="form-control input-sm input250" />
+                                    <input type="text" name="email_from" id="email_from" value="${sickbeard.EMAIL_FROM}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1762,7 +1809,7 @@
                                 <label for="email_tls">
                                     <span class="component-title">Use TLS</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="email_tls" id="email_tls" #if $sickbeard.EMAIL_TLS then "checked=\"checked\"" else ""# />
+                                        <input type="checkbox" name="email_tls" id="email_tls" ${('', 'checked="checked"')[bool(sickbeard.EMAIL_TLS)]}/>
                                         <p>check to use TLS encryption.</p>
                                     </span>
                                 </label>
@@ -1770,7 +1817,7 @@
                             <div class="field-pair">
                                 <label for="email_user">
                                     <span class="component-title">SMTP user</span>
-                                    <input type="text" name="email_user" id="email_user" value="$sickbeard.EMAIL_USER" class="form-control input-sm input250" />
+                                    <input type="text" name="email_user" id="email_user" value="${sickbeard.EMAIL_USER}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1780,7 +1827,7 @@
                             <div class="field-pair">
                                 <label for="email_password">
                                     <span class="component-title">SMTP password</span>
-                                    <input type="password" name="email_password" id="email_password" value="$sickbeard.EMAIL_PASSWORD" class="form-control input-sm input250" />
+                                    <input type="password" name="email_password" id="email_password" value="${sickbeard.EMAIL_PASSWORD}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1790,7 +1837,7 @@
                             <div class="field-pair">
                                 <label for="email_list">
                                     <span class="component-title">Global email list</span>
-                                    <input type="text" name="email_list" id="email_list" value="$sickbeard.EMAIL_LIST" class="form-control input-sm input350" />
+                                    <input type="text" name="email_list" id="email_list" value="${sickbeard.EMAIL_LIST}" class="form-control input-sm input350" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1835,8 +1882,6 @@
 
 <div class="clearfix"></div>
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#config-components').tabs();
-//-->
 </script>
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_postProcessing.tmpl b/gui/slick/interfaces/default/config_postProcessing.mako
similarity index 82%
rename from gui/slick/interfaces/default/config_postProcessing.tmpl
rename to gui/slick/interfaces/default/config_postProcessing.mako
index 878861a81cea9e06d0c454a85d4d3a144468d843..05bc981f88b337110d36c1c31dd098a54e1f9b24 100644
--- a/gui/slick/interfaces/default/config_postProcessing.tmpl
+++ b/gui/slick/interfaces/default/config_postProcessing.mako
@@ -1,27 +1,22 @@
-#import os.path
-#import sickbeard
-#from sickbeard.common import *
-#from sickbeard import config
-#from sickbeard import metadata
-#from sickbeard.metadata.generic import GenericMetadata
-#from sickbeard import naming
-
-#set global $title  = "Config - Post Processing"
-#set global $header = "Post Processing"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="config"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/configPostProcessing.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
+<%
+    import os.path
+    import sickbeard
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets, multiEpStrings
+    from sickbeard import config
+    from sickbeard import metadata
+    from sickbeard.metadata.generic import GenericMetadata
+    from sickbeard import naming
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/configPostProcessing.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
 <div id="content960">
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 <div id="config">
 <div id="config-content">
 
@@ -45,7 +40,7 @@
                         <div class="field-pair">
                             <label class="nocheck" for="tv_download_dir">
                                 <span class="component-title">TV Download Dir</span>
-                                <input type="text" name="tv_download_dir" id="tv_download_dir" value="$sickbeard.TV_DOWNLOAD_DIR" class="form-control input-sm input350" />
+                                <input type="text" name="tv_download_dir" id="tv_download_dir" value="${sickbeard.TV_DOWNLOAD_DIR}" class="form-control input-sm input350" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -70,15 +65,10 @@
                                 <span class="component-title">Process Episode Method:</span>
                                 <span class="component-desc">
                                     <select name="process_method" id="process_method" class="form-control input-sm">
-                                        #set $process_method_text = {'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"}
-                                        #for $curAction in ('copy', 'move', 'hardlink', 'symlink'):
-                                          #if $sickbeard.PROCESS_METHOD == $curAction:
-                                            #set $process_method = "selected=\"selected\""
-                                          #else
-                                            #set $process_method = ""
-                                          #end if
-                                        <option value="$curAction" $process_method>$process_method_text[$curAction]</option>
-                                        #end for
+                                        <% process_method_text = {'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"} %>
+                                        % for curAction in ('copy', 'move', 'hardlink', 'symlink'):
+                                        <option value="${curAction}" ${('', 'selected="selected"')[sickbeard.PROCESS_METHOD == curAction]}>${process_method_text[curAction]}</option>
+                                        % endfor
                                     </select>
                                 </span>
                             </label>
@@ -88,14 +78,14 @@
                             </label>
                         </div>
                         <div class="field-pair">
-                            <input type="checkbox" name="del_rar_contents" id="del_rar_contents" #if $sickbeard.DELRARCONTENTS == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="del_rar_contents" id="del_rar_contents" ${('', 'checked="checked"')[bool(sickbeard.DELRARCONTENTS)]}/>
                             <label for="del_rar_contents">
                                 <span class="component-title">Delete RAR contents</span>
                                 <span class="component-desc">Delete content of RAR files, even if Process Method not set to move?</span>
                             </label>
                         </div>
                         <div class="field-pair">
-                            <input type="checkbox" name="skip_removed_files" id="skip_removed_files" #if $sickbeard.SKIP_REMOVED_FILES == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="skip_removed_files" id="skip_removed_files" ${('', 'checked="checked"')[bool(sickbeard.SKIP_REMOVED_FILES)]}/>
                             <label for="skip_removed_files">
                                 <span class="component-title">Skip Remove Detection</span>
                                 <span class="component-desc">Skip detection of removed files, so they don't get set to ignored/archived?</span>
@@ -108,7 +98,7 @@
                         <div class="field-pair">
                             <label class="nocheck">
                                 <span class="component-title">Extra Scripts</span>
-                                <input type="text" name="extra_scripts" value="<%='|'.join(sickbeard.EXTRA_SCRIPTS)%>" class="form-control input-sm input350" />
+                                <input type="text" name="extra_scripts" value="${'|'.join(sickbeard.EXTRA_SCRIPTS)}" class="form-control input-sm input350" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -118,7 +108,7 @@
                                 <span class="component-title">&nbsp;</span>
                                 <span class="component-desc">
                                     <ul>
-                                        <li>See <a href="https://github.com/SiCKRAGETV/SickRage/wiki/Post%20Processing"><font color='red'><b>Wiki</b></font></a> for a script arguments description.</li>
+                                        <li>See <a href="https://github.com/SiCKRAGETV/sickrage-issues/wiki/Post-Processing"><font color='red'><b>Wiki</b></font></a> for a script arguments description.</li>
                                         <li>Additional scripts separated by <b>|</b>.</li>
                                         <li>Scripts are called after SickRage's own post-processing.</li>
                                         <li>For any scripted languages, include the interpreter executable before the script. See the following example:</li>
@@ -132,7 +122,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" name="move_associated_files" id="move_associated_files" #if $sickbeard.MOVE_ASSOCIATED_FILES == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="move_associated_files" id="move_associated_files" ${('', 'checked="checked"')[bool(sickbeard.MOVE_ASSOCIATED_FILES)]}/>
                             <label for="move_associated_files">
                                 <span class="component-title">Move Associated Files</span>
                                 <span class="component-desc">Move srr/srt/sfv/etc files with the episode when processed?</span>
@@ -141,7 +131,7 @@
                         <div class="field-pair">
                             <label class="nocheck">
                                 <span class="component-title">Sync File Extensions</span>
-                                <input type="text" name="sync_files" id="sync_files" value="$sickbeard.SYNC_FILES" class="form-control input-sm input350" />
+                                <input type="text" name="sync_files" id="sync_files" value="${sickbeard.SYNC_FILES}" class="form-control input-sm input350" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -149,7 +139,7 @@
                             </label>
                         </div>
                         <div class="field-pair">
-                            <input type="checkbox" name="postpone_if_sync_files" id="postpone_if_sync_files" #if $sickbeard.POSTPONE_IF_SYNC_FILES == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="postpone_if_sync_files" id="postpone_if_sync_files" ${('', 'checked="checked"')[bool(sickbeard.POSTPONE_IF_SYNC_FILES)]}/>
                             <label for="postpone_if_sync_files">
                                 <span class="component-title">Postpone post processing</span>
                                 <span class="component-desc">if sync files are present in the TV download dir</span>
@@ -157,7 +147,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" name="nfo_rename" id="nfo_rename" #if $sickbeard.NFO_RENAME == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="nfo_rename" id="nfo_rename" ${('', 'checked="checked"')[bool(sickbeard.NFO_RENAME)]}/>
                             <label for="nfo_rename">
                                 <span class="component-title">Rename .nfo file</span>
                                 <span class="component-desc">Rename the original .nfo file to .nfo-orig to avoid conflicts?</span>
@@ -165,7 +155,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" name="rename_episodes" id="rename_episodes" #if $sickbeard.RENAME_EPISODES == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="rename_episodes" id="rename_episodes" ${('', 'checked="checked"')[bool(sickbeard.RENAME_EPISODES)]}/>
                             <label for="rename_episodes">
                                 <span class="component-title">Rename Episodes</span>
                                 <span class="component-desc">Rename episode using the Episode Naming settings?</span>
@@ -173,7 +163,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" name="airdate_episodes" id="airdate_episodes" #if $sickbeard.AIRDATE_EPISODES == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="airdate_episodes" id="airdate_episodes" ${('', 'checked="checked"')[bool(sickbeard.AIRDATE_EPISODES)]}/>
                             <label for="airdate_episodes">
                                 <span class="component-title">Change File Date</span>
                                 <span class="component-desc">Set last modified filedate to the date that the episode aired?</span>
@@ -185,7 +175,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" name="process_automatically" id="process_automatically" #if $sickbeard.PROCESS_AUTOMATICALLY == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="process_automatically" id="process_automatically" ${('', 'checked="checked"')[bool(sickbeard.PROCESS_AUTOMATICALLY)]}/>
                             <label for="process_automatically">
                                 <span class="component-title">Scan and Process</span>
                                 <span class="component-desc">Scan and post-process any files in your <i>TV Download Dir</i>?</span>
@@ -201,7 +191,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" name="no_delete" id="no_delete" #if $sickbeard.NO_DELETE == True then "checked=\"checked\"" else ""# />
+                            <input type="checkbox" name="no_delete" id="no_delete" ${('', 'checked="checked"')[bool(sickbeard.NO_DELETE)]}/>
                             <label for="no_delete">
                                 <span class="component-title">Don't delete empty folders</span>
                                 <span class="component-desc">Leave empty folders when Post Processing?</span>
@@ -215,7 +205,7 @@
                         <div class="field-pair">
                             <label class="nocheck">
                                 <span class="component-title">Auto Post-Processing Frequency</span>
-                                <input type="text" name="autopostprocesser_frequency" id="autopostprocesser_frequency" value="$sickbeard.AUTOPOSTPROCESSER_FREQUENCY" class="form-control input-sm input75" />
+                                <input type="text" name="autopostprocesser_frequency" id="autopostprocesser_frequency" value="${sickbeard.AUTOPOSTPROCESSER_FREQUENCY}" class="form-control input-sm input75" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -224,7 +214,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input id="unpack" type="checkbox" name="unpack" #if $sickbeard.UNPACK == True then "checked=\"checked\"" else ""# />
+                            <input id="unpack" type="checkbox" name="unpack" ${('', 'checked="checked"')[bool(sickbeard.UNPACK)]} />
                             <label for="unpack">
                                 <span class="component-title">Unpack</span>
                                 <span class="component-desc">Unpack any TV releases in your <i>TV Download Dir</i>?</span>
@@ -236,7 +226,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input id="use_failed_downloads" type="checkbox" class="enabler" name="use_failed_downloads" #if $sickbeard.USE_FAILED_DOWNLOADS == True then "checked=\"checked\"" else ""# />
+                            <input id="use_failed_downloads" type="checkbox" class="enabler" name="use_failed_downloads" ${('', 'checked="checked"')[bool(sickbeard.USE_FAILED_DOWNLOADS)]}/>
                             <label for="use_failed_downloads">
                                 <span class="component-title">Use Failed Downloads</span>
                                 <span class="component-desc">Use Failed Download Handling?</span>
@@ -248,7 +238,7 @@
 
                         <div id="content_use_failed_downloads">
                             <div class="field-pair">
-                                <input id="delete_failed" type="checkbox" name="delete_failed" #if $sickbeard.DELETE_FAILED == True then "checked=\"checked\"" else ""# />
+                                <input id="delete_failed" type="checkbox" name="delete_failed" ${('', 'checked="checked"')[bool(sickbeard.DELETE_FAILED)]}/>
                                 <label for="delete_failed">
                                     <span class="component-title">Delete Failed</span>
                                     <span class="component-desc">Delete files left over from a failed download?</span>
@@ -280,15 +270,15 @@
                                 <span class="component-title">Name Pattern:</span>
                                 <span class="component-desc">
                                     <select id="name_presets" class="form-control input-sm">
-                                        #set is_custom = True
-                                        #for $cur_preset in $naming.name_presets:
-                                            #set $tmp = $naming.test_name($cur_preset, anime_type=3)
-                                            #if $cur_preset == $sickbeard.NAMING_PATTERN:
-                                                #set is_custom = False
-                                            #end if
-                                            <option id="$cur_preset" #if $cur_preset == $sickbeard.NAMING_PATTERN then "selected=\"selected\"" else ""#>$os.path.join($tmp['dir'], $tmp['name'])</option>
-                                        #end for
-                                        <option id="$sickbeard.NAMING_PATTERN" #if $is_custom then "selected=\"selected\"" else ""#>Custom...</option>
+                                        <% is_custom = True %>
+                                        % for cur_preset in naming.name_presets:
+                                            <% tmp = naming.test_name(cur_preset, anime_type=3) %>
+                                            % if cur_preset == sickbeard.NAMING_PATTERN:
+                                                <% is_custom = False %>
+                                            % endif
+                                            <option id="${cur_preset}" ${('', 'selected="selected"')[sickbeard.NAMING_PATTERN == cur_preset]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                        % endfor
+                                        <option id="${sickbeard.NAMING_PATTERN}" ${('', 'selected="selected"')[bool(is_custom)]}>Custom...</option>
                                     </select>
                                 </span>
                             </label>
@@ -301,8 +291,8 @@
                                         &nbsp;
                                     </span>
                                     <span class="component-desc">
-                                        <input type="text" name="naming_pattern" id="naming_pattern" value="$sickbeard.NAMING_PATTERN" class="form-control input-sm input350" />
-                                        <img src="$sbRoot/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_key" title="Toggle Naming Legend" class="legend" class="legend" />
+                                        <input type="text" name="naming_pattern" id="naming_pattern" value="${sickbeard.NAMING_PATTERN}" class="form-control input-sm input350" />
+                                        <img src="${sbRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_key" title="Toggle Naming Legend" class="legend" class="legend" />
                                     </span>
                                 </label>
                                 <label class="nocheck">
@@ -437,9 +427,9 @@
                                 <span class="component-title">Multi-Episode Style:</span>
                                 <span class="component-desc">
                                     <select id="naming_multi_ep" name="naming_multi_ep" class="form-control input-sm">
-                                    #for $cur_multi_ep in sorted($multiEpStrings.items(), key=lambda x: x[1]):
-                                        <option value="$cur_multi_ep[0]" #if $cur_multi_ep[0] == $sickbeard.NAMING_MULTI_EP then "selected=\"selected\" class=\"selected\"" else ""#>$cur_multi_ep[1]</option>
-                                    #end for
+                                    % for cur_multi_ep in sorted(multiEpStrings.iteritems(), key=lambda x: x[1]):
+                                        <option value="${cur_multi_ep[0]}" ${('', 'selected="selected"')[cur_multi_ep[0] == sickbeard.NAMING_MULTI_EP]}>${cur_multi_ep[1]}</option>
+                                    % endfor
                                     </select>
                                 </span>
                             </label>
@@ -462,7 +452,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" id="naming_strip_year"  name="naming_strip_year" #if $sickbeard.NAMING_STRIP_YEAR then "checked=\"checked\"" else ""#/>
+                            <input type="checkbox" id="naming_strip_year"  name="naming_strip_year" ${('', 'checked="checked"')[bool(sickbeard.NAMING_STRIP_YEAR)]}/>
                             <label for="naming_strip_year">
                                 <span class="component-title">Strip Show Year</span>
                                 <span class="component-desc">Remove the TV show's year when renaming the file?</span>
@@ -474,7 +464,7 @@
                         </div>
 
                         <div class="field-pair">
-                            <input type="checkbox" class="enabler" id="naming_custom_abd" name="naming_custom_abd" #if $sickbeard.NAMING_CUSTOM_ABD then "checked=\"checked\"" else ""#/>
+                            <input type="checkbox" class="enabler" id="naming_custom_abd" name="naming_custom_abd" ${('', 'checked="checked"')[bool(sickbeard.NAMING_CUSTOM_ABD)]}/>
                             <label for="naming_custom_abd">
                                 <span class="component-title">Custom Air-By-Date</span>
                                 <span class="component-desc">Name Air-By-Date shows differently than regular shows?</span>
@@ -487,15 +477,15 @@
                                     <span class="component-title">Name Pattern:</span>
                                     <span class="component-desc">
                                         <select id="name_abd_presets" class="form-control input-sm">
-                                            #set is_abd_custom = True
-                                            #for $cur_preset in $naming.name_abd_presets:
-                                                #set $tmp = $naming.test_name($cur_preset)
-                                                #if $cur_preset == $sickbeard.NAMING_ABD_PATTERN:
-                                                    #set is_abd_custom = False
-                                                #end if
-                                                <option id="$cur_preset" #if $cur_preset == $sickbeard.NAMING_ABD_PATTERN then "selected=\"selected\"" else ""#>$os.path.join($tmp['dir'], $tmp['name'])</option>
-                                            #end for
-                                            <option id="$sickbeard.NAMING_ABD_PATTERN" #if $is_abd_custom then "selected=\"selected\"" else ""#>Custom...</option>
+                                            <% is_abd_custom = True %>
+                                            % for cur_preset in naming.name_abd_presets:
+                                                <% tmp = naming.test_name(cur_preset) %>
+                                                % if cur_preset == sickbeard.NAMING_ABD_PATTERN:
+                                                    <% is_abd_custom = False %>
+                                                % endif
+                                                <option id="${cur_preset}" ${('', 'selected="selected"')[sickbeard.NAMING_ABD_PATTERN == cur_preset]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                            % endfor
+                                            <option id="${sickbeard.NAMING_ABD_PATTERN}" ${('', 'selected="selected"')[bool(is_abd_custom)]}>Custom...</option>
                                         </select>
                                     </span>
                                 </label>
@@ -508,8 +498,8 @@
                                             &nbsp;
                                         </span>
                                         <span class="component-desc">
-                                            <input type="text" name="naming_abd_pattern" id="naming_abd_pattern" value="$sickbeard.NAMING_ABD_PATTERN" class="form-control input-sm input350" />
-                                            <img src="$sbRoot/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_abd_key" title="Toggle ABD Naming Legend" class="legend" />
+                                            <input type="text" name="naming_abd_pattern" id="naming_abd_pattern" value="${sickbeard.NAMING_ABD_PATTERN}" class="form-control input-sm input350" />
+                                            <img src="${sbRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_abd_key" title="Toggle ABD Naming Legend" class="legend" />
                                         </span>
                                     </label>
                                 </div>
@@ -651,7 +641,7 @@
                         </div><!-- /naming_abd_different -->
 
                         <div class="field-pair">
-                            <input type="checkbox" class="enabler" id="naming_custom_sports" name="naming_custom_sports" #if $sickbeard.NAMING_CUSTOM_SPORTS then "checked=\"checked\"" else ""#/>
+                            <input type="checkbox" class="enabler" id="naming_custom_sports" name="naming_custom_sports" ${('', 'checked="checked"')[bool(sickbeard.NAMING_CUSTOM_SPORTS)]}/>
                             <label for="naming_custom_sports">
                                 <span class="component-title">Custom Sports</span>
                                 <span class="component-desc">Name Sports shows differently than regular shows?</span>
@@ -664,15 +654,15 @@
                                     <span class="component-title">Name Pattern:</span>
                                     <span class="component-desc">
                                         <select id="name_sports_presets" class="form-control input-sm">
-                                            #set is_sports_custom = True
-                                            #for $cur_preset in $naming.name_sports_presets:
-                                                #set $tmp = $naming.test_name($cur_preset)
-                                                #if $cur_preset == $sickbeard.NAMING_SPORTS_PATTERN:
-                                                    #set is_sports_custom = False
-                                                #end if
-                                                <option id="$cur_preset" #if $cur_preset == $sickbeard.NAMING_SPORTS_PATTERN then "selected=\"selected\"" else ""#>$os.path.join($tmp['dir'], $tmp['name'])</option>
-                                            #end for
-                                            <option id="$sickbeard.NAMING_SPORTS_PATTERN" #if $is_sports_custom then "selected=\"selected\"" else ""#>Custom...</option>
+                                            <% is_sports_custom = True %>
+                                            % for cur_preset in naming.name_sports_presets:
+                                                <% tmp = naming.test_name(cur_preset) %>
+                                                % if cur_preset == sickbeard.NAMING_SPORTS_PATTERN:
+                                                    <% is_sports_custom = False %>
+                                                % endif
+                                                <option id="${cur_preset}" ${('', 'selected="selected"')[NAMING_SPORTS_PATTERN == cur_preset]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                            % endfor
+                                            <option id="${sickbeard.NAMING_SPORTS_PATTERN}" ${('', 'selected="selected"')[bool(is_sports_custom)]}>Custom...</option>
                                         </select>
                                     </span>
                                 </label>
@@ -685,8 +675,8 @@
                                             &nbsp;
                                         </span>
                                         <span class="component-desc">
-                                            <input type="text" name="naming_sports_pattern" id="naming_sports_pattern" value="$sickbeard.NAMING_SPORTS_PATTERN" class="form-control input-sm input350" />
-                                            <img src="$sbRoot/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_sports_key" title="Toggle Sports Naming Legend" class="legend" />
+                                            <input type="text" name="naming_sports_pattern" id="naming_sports_pattern" value="${sickbeard.NAMING_SPORTS_PATTERN}" class="form-control input-sm input350" />
+                                            <img src="${sbRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_sports_key" title="Toggle Sports Naming Legend" class="legend" />
                                         </span>
                                     </label>
                                 </div>
@@ -829,7 +819,7 @@
 
                         <!-- naming_anime_custom -->
                         <div class="field-pair">
-                            <input type="checkbox" class="enabler" id="naming_custom_anime" name="naming_custom_anime" #if $sickbeard.NAMING_CUSTOM_ANIME then "checked=\"checked\"" else ""#/>
+                            <input type="checkbox" class="enabler" id="naming_custom_anime" name="naming_custom_anime" ${('', 'checked="checked"')[bool(sickbeard.NAMING_CUSTOM_ANIME)]}/>
                             <label for="naming_custom_anime">
                                 <span class="component-title">Custom Anime</span>
                                 <span class="component-desc">Name Anime shows differently than regular shows?</span>
@@ -842,15 +832,15 @@
                                     <span class="component-title">Name Pattern:</span>
                                     <span class="component-desc">
                                         <select id="name_anime_presets" class="form-control input-sm">
-                                            #set is_anime_custom = True
-                                            #for $cur_preset in $naming.name_anime_presets:
-                                                #set $tmp = $naming.test_name($cur_preset)
-                                                #if $cur_preset == $sickbeard.NAMING_ANIME_PATTERN:
-                                                    #set is_anime_custom = False
-                                                #end if
-                                                <option id="$cur_preset" #if $cur_preset == $sickbeard.NAMING_ANIME_PATTERN then "selected=\"selected\"" else ""#>$os.path.join($tmp['dir'], $tmp['name'])</option>
-                                            #end for
-                                            <option id="$sickbeard.NAMING_ANIME_PATTERN" #if $is_anime_custom then "selected=\"selected\"" else ""#>Custom...</option>
+                                            <% is_anime_custom = True %>
+                                            % for cur_preset in naming.name_anime_presets:
+                                                <% tmp = naming.test_name(cur_preset) %>
+                                                % if cur_preset == sickbeard.NAMING_ANIME_PATTERN:
+                                                    <% is_anime_custom = False %>
+                                                % endif
+                                                <option id="${cur_preset}" ${('', 'selected="selected"')[cur_preset == sickbeard.NAMING_ANIME_PATTERN]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                            % endfor
+                                            <option id="${sickbeard.NAMING_ANIME_PATTERN}" ${('', 'selected="selected"')[bool(is_anime_custom)]}>Custom...</option>
                                         </select>
                                     </span>
                                 </label>
@@ -863,8 +853,8 @@
                                             &nbsp;
                                         </span>
                                         <span class="component-desc">
-                                            <input type="text" name="naming_anime_pattern" id="naming_anime_pattern" value="$sickbeard.NAMING_ANIME_PATTERN" class="form-control input-sm input350" />
-                                            <img src="$sbRoot/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_anime_key" title="Toggle Anime Naming Legend" class="legend" />
+                                            <input type="text" name="naming_anime_pattern" id="naming_anime_pattern" value="${sickbeard.NAMING_ANIME_PATTERN}" class="form-control input-sm input350" />
+                                            <img src="${sbRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_anime_key" title="Toggle Anime Naming Legend" class="legend" />
                                         </span>
                                     </label>
                                 </div>
@@ -995,9 +985,9 @@
                                     <span class="component-title">Multi-Episode Style:</span>
                                     <span class="component-desc">
                                         <select id="naming_anime_multi_ep" name="naming_anime_multi_ep" class="form-control input-sm">
-                                        #for $cur_multi_ep in sorted($multiEpStrings.items(), key=lambda x: x[1]):
-                                            <option value="$cur_multi_ep[0]" #if $cur_multi_ep[0] == $sickbeard.NAMING_ANIME_MULTI_EP then "selected=\"selected\" class=\"selected\"" else ""#>$cur_multi_ep[1]</option>
-                                        #end for
+                                        % for cur_multi_ep in sorted(multiEpStrings.iteritems(), key=lambda x: x[1]):
+                                            <option value="${cur_multi_ep[0]}" ${('', 'selected="selected" class="selected"')[cur_multi_ep[0] == sickbeard.NAMING_ANIME_MULTI_EP]}>${cur_multi_ep[1]}</option>
+                                        % endfor
                                         </select>
                                     </span>
                                 </label>
@@ -1020,7 +1010,7 @@
                             </div>
 
                             <div class="field-pair">
-                                <input type="radio" name="naming_anime" id="naming_anime" value="1" #if $sickbeard.NAMING_ANIME == 1 then "checked=\"checked\"" else ""#/>
+                                <input type="radio" name="naming_anime" id="naming_anime" value="1" ${('', 'checked="checked"')[sickbeard.NAMING_ANIME == 1]}/>
                                 <label for="naming_anime">
                                     <span class="component-title">Add Absolute Number</span>
                                     <span class="component-desc">Add the absolute number to the season/episode format?</span>
@@ -1032,7 +1022,7 @@
                             </div>
 
                             <div class="field-pair">
-                                <input type="radio" name="naming_anime" id="naming_anime_only" value="2" #if $sickbeard.NAMING_ANIME == 2 then "checked=\"checked\"" else ""#/>
+                                <input type="radio" name="naming_anime" id="naming_anime_only" value="2" ${('', 'checked="checked"')[sickbeard.NAMING_ANIME == 2]}/>
                                 <label for="naming_anime_only">
                                     <span class="component-title">Only Absolute Number</span>
                                     <span class="component-desc">Replace season/episode format with absolute number</span>
@@ -1044,7 +1034,7 @@
                             </div>
 
                             <div class="field-pair">
-                                <input type="radio" name="naming_anime" id="naming_anime_none" value="3" #if $sickbeard.NAMING_ANIME == 3 then "checked=\"checked\"" else ""#/>
+                                <input type="radio" name="naming_anime" id="naming_anime_none" value="3" ${('', 'checked="checked"')[sickbeard.NAMING_ANIME == 3]}/>
                                 <label for="naming_anime_none">
                                     <span class="component-title">No Absolute Number</span>
                                     <span class="component-desc">Dont include the absolute number</span>
@@ -1075,54 +1065,54 @@
                             <label>
                                 <span class="component-title">Metadata Type:</span>
                                 <span class="component-desc">
-                                    #set $m_dict = $metadata.get_metadata_generator_dict()
+                                    <% m_dict = metadata.get_metadata_generator_dict() %>
                                     <select id="metadataType" class="form-control input-sm">
-                                    #for ($cur_name, $cur_generator) in sorted($m_dict.items()):
-                                        <option value="$cur_generator.get_id()">$cur_name</option>
-                                    #end for
+                                    % for (cur_name, cur_generator) in sorted(m_dict.iteritems()):
+                                        <option value="${cur_generator.get_id()}">${cur_name}</option>
+                                    % endfor
                                     </select>
                                 </span>
                             </label>
                             <span>Toggle the metadata options that you wish to be created. <b>Multiple targets may be used.</b></span>
                         </div>
 
-                        #for ($cur_name, $cur_generator) in $m_dict.items():
-                        #set $cur_metadata_inst = $sickbeard.metadata_provider_dict[$cur_generator.name]
-                        #set $cur_id = $cur_generator.get_id()
-                        <div class="metadataDiv" id="$cur_id">
+                        % for (cur_name, cur_generator) in m_dict.iteritems():
+                        <% cur_metadata_inst = sickbeard.metadata_provider_dict[cur_generator.name] %>
+                        <% cur_id = cur_generator.get_id() %>
+                        <div class="metadataDiv" id="${cur_id}">
                             <div class="metadata_options_wrapper">
                                 <h4>Create:</h4>
                                 <div class="metadata_options">
-                                    <label for="${cur_id}_show_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_show_metadata" #if $cur_metadata_inst.show_metadata then "checked=\"checked\"" else ""#/>&nbsp;Show Metadata</label>
-                                    <label for="${cur_id}_episode_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_metadata" #if $cur_metadata_inst.episode_metadata then "checked=\"checked\"" else ""#/>&nbsp;Episode Metadata</label>
-                                    <label for="${cur_id}_fanart"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_fanart" #if $cur_metadata_inst.fanart then "checked=\"checked\"" else ""#/>&nbsp;Show Fanart</label>
-                                    <label for="${cur_id}_poster"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_poster" #if $cur_metadata_inst.poster then "checked=\"checked\"" else ""#/>&nbsp;Show Poster</label>
-                                    <label for="${cur_id}_banner"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_banner" #if $cur_metadata_inst.banner then "checked=\"checked\"" else ""#/>&nbsp;Show Banner</label>
-                                    <label for="${cur_id}_episode_thumbnails"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_episode_thumbnails" #if $cur_metadata_inst.episode_thumbnails then "checked=\"checked\"" else ""#/>&nbsp;Episode Thumbnails</label>
-                                    <label for="${cur_id}_season_posters"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_posters" #if $cur_metadata_inst.season_posters then "checked=\"checked\"" else ""#/>&nbsp;Season Posters</label>
-                                    <label for="${cur_id}_season_banners"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_banners" #if $cur_metadata_inst.season_banners then "checked=\"checked\"" else ""#/>&nbsp;Season Banners</label>
-                                    <label for="${cur_id}_season_all_poster"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_all_poster" #if $cur_metadata_inst.season_all_poster then "checked=\"checked\"" else ""#/>&nbsp;Season All Poster</label>
-                                    <label for="${cur_id}_season_all_banner"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_all_banner" #if $cur_metadata_inst.season_all_banner then "checked=\"checked\"" else ""#/>&nbsp;Season All Banner</label>
+                                    <label for="${cur_id}_show_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_show_metadata" ${('', 'checked="checked"')[bool(cur_metadata_inst.show_metadata)]}/>&nbsp;Show Metadata</label>
+                                    <label for="${cur_id}_episode_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_metadata" ${('', 'checked="checked"')[bool(cur_metadata_inst.episode_metadata)]}/>&nbsp;Episode Metadata</label>
+                                    <label for="${cur_id}_fanart"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_fanart" ${('', 'checked="checked"')[bool(cur_metadata_inst.fanart)]}/>&nbsp;Show Fanart</label>
+                                    <label for="${cur_id}_poster"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_poster" ${('', 'checked="checked"')[bool(cur_metadata_inst.poster)]}/>&nbsp;Show Poster</label>
+                                    <label for="${cur_id}_banner"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_banner" ${('', 'checked="checked"')[bool(cur_metadata_inst.banner)]}/>&nbsp;Show Banner</label>
+                                    <label for="${cur_id}_episode_thumbnails"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_episode_thumbnails" ${('', 'checked="checked"')[bool(cur_metadata_inst.episode_thumbnails)]}/>&nbsp;Episode Thumbnails</label>
+                                    <label for="${cur_id}_season_posters"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_posters" ${('', 'checked="checked"')[bool(cur_metadata_inst.season_posters)]}/>&nbsp;Season Posters</label>
+                                    <label for="${cur_id}_season_banners"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_banners" ${('', 'checked="checked"')[bool(cur_metadata_inst.season_banners)]}/>&nbsp;Season Banners</label>
+                                    <label for="${cur_id}_season_all_poster"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_all_poster" ${('', 'checked="checked"')[bool(cur_metadata_inst.season_all_poster)]}/>&nbsp;Season All Poster</label>
+                                    <label for="${cur_id}_season_all_banner"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_all_banner" ${('', 'checked="checked"')[bool(cur_metadata_inst.season_all_banner)]}/>&nbsp;Season All Banner</label>
                                 </div>
                             </div>
                             <div class="metadata_example_wrapper">
                                 <h4>Results:</h4>
                                 <div class="metadata_example">
-                                    <label for="${cur_id}_show_metadata"><span id="${cur_id}_eg_show_metadata">$cur_metadata_inst.eg_show_metadata</span></label>
-                                    <label for="${cur_id}_episode_metadata"><span id="${cur_id}_eg_episode_metadata">$cur_metadata_inst.eg_episode_metadata</span></label>
-                                    <label for="${cur_id}_fanart"><span id="${cur_id}_eg_fanart">$cur_metadata_inst.eg_fanart</span></label>
-                                    <label for="${cur_id}_poster"><span id="${cur_id}_eg_poster">$cur_metadata_inst.eg_poster</span></label>
-                                    <label for="${cur_id}_banner"><span id="${cur_id}_eg_banner">$cur_metadata_inst.eg_banner</span></label>
-                                    <label for="${cur_id}_episode_thumbnails"><span id="${cur_id}_eg_episode_thumbnails">$cur_metadata_inst.eg_episode_thumbnails</span></label>
-                                    <label for="${cur_id}_season_posters"><span id="${cur_id}_eg_season_posters">$cur_metadata_inst.eg_season_posters</span></label>
-                                    <label for="${cur_id}_season_banners"><span id="${cur_id}_eg_season_banners">$cur_metadata_inst.eg_season_banners</span></label>
-                                    <label for="${cur_id}_season_all_poster"><span id="${cur_id}_eg_season_all_poster">$cur_metadata_inst.eg_season_all_poster</span></label>
-                                    <label for="${cur_id}_season_all_banner"><span id="${cur_id}_eg_season_all_banner">$cur_metadata_inst.eg_season_all_banner</span></label>
+                                    <label for="${cur_id}_show_metadata"><span id="${cur_id}_eg_show_metadata">${cur_metadata_inst.eg_show_metadata}</span></label>
+                                    <label for="${cur_id}_episode_metadata"><span id="${cur_id}_eg_episode_metadata">${cur_metadata_inst.eg_episode_metadata}</span></label>
+                                    <label for="${cur_id}_fanart"><span id="${cur_id}_eg_fanart">${cur_metadata_inst.eg_fanart}</span></label>
+                                    <label for="${cur_id}_poster"><span id="${cur_id}_eg_poster">${cur_metadata_inst.eg_poster}</span></label>
+                                    <label for="${cur_id}_banner"><span id="${cur_id}_eg_banner">${cur_metadata_inst.eg_banner}</span></label>
+                                    <label for="${cur_id}_episode_thumbnails"><span id="${cur_id}_eg_episode_thumbnails">${cur_metadata_inst.eg_episode_thumbnails}</span></label>
+                                    <label for="${cur_id}_season_posters"><span id="${cur_id}_eg_season_posters">${cur_metadata_inst.eg_season_posters}</span></label>
+                                    <label for="${cur_id}_season_banners"><span id="${cur_id}_eg_season_banners">${cur_metadata_inst.eg_season_banners}</span></label>
+                                    <label for="${cur_id}_season_all_poster"><span id="${cur_id}_eg_season_all_poster">${cur_metadata_inst.eg_season_all_poster}</span></label>
+                                    <label for="${cur_id}_season_all_banner"><span id="${cur_id}_eg_season_all_banner">${cur_metadata_inst.eg_season_all_banner}</span></label>
                                 </div>
                             </div>
-                            <input type="hidden" name="${cur_id}_data" id="${cur_id}_data" value="$cur_metadata_inst.get_config()" />
+                            <input type="hidden" name="${cur_id}_data" id="${cur_id}_data" value="${cur_metadata_inst.get_config()}" />
                         </div>
-                        #end for
+                        % endfor
 
                         <div class="clearfix"></div><br/>
 
@@ -1131,7 +1121,7 @@
                 </div><!-- /component-group3 //-->
 
                 <br/>
-                <h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">$sickbeard.DATA_DIR</span></b> </h6>
+                <h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">${sickbeard.DATA_DIR}</span></b> </h6>
                 <input type="submit" class="btn pull-left config_submitter button" value="Save Changes" />
 
         </form>
@@ -1141,9 +1131,8 @@
 <div class="clearfix"></div>
 
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#config-components').tabs();
     jQuery('#tv_download_dir').fileBrowser({ title: 'Select TV Download Directory' });
-//-->
 </script>
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_providers.tmpl b/gui/slick/interfaces/default/config_providers.mako
similarity index 70%
rename from gui/slick/interfaces/default/config_providers.tmpl
rename to gui/slick/interfaces/default/config_providers.mako
index b2cc1f464b91d857ae7e8e1387fc2762f9081b57..4b75373959c23d346ec6ecd4c62d49693342f112 100644
--- a/gui/slick/interfaces/default/config_providers.tmpl
+++ b/gui/slick/interfaces/default/config_providers.mako
@@ -1,48 +1,38 @@
-#import sickbeard
-#from sickbeard.providers.generic import GenericProvider
-#from sickbeard.providers import thepiratebay
-#from sickbeard.helpers import anon_url
-
-#set global $title="Config - Providers"
-#set global $header="Search Providers"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="config"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-<script type="text/javascript" src="$sbRoot/js/configProviders.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
-#if $sickbeard.USE_NZBS
+<%!
+    import sickbeard
+    from sickbeard.providers.generic import GenericProvider
+    from sickbeard.providers import thepiratebay
+    from sickbeard.helpers import anon_url
+
+%>
+<%include file="/inc_top.mako"/>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+<script type="text/javascript" src="${sbRoot}/js/configProviders.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
+% if sickbeard.USE_NZBS:
 <script type="text/javascript" charset="utf-8">
-<!--
-\$(document).ready(function(){
-var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
-#for $curNewznabProvider in $sickbeard.newznabProviderList:
-\$(this).addProvider('$curNewznabProvider.getID()', '$curNewznabProvider.name', '$curNewznabProvider.url', '$curNewznabProvider.key', '$curNewznabProvider.catIDs', $int($curNewznabProvider.default), show_nzb_providers);
-#end for
+$(document).ready(function(){
+    var show_nzb_providers = ${("false", "true")[bool(sickbeard.USE_NZBS)]};
+    % for curNewznabProvider in sickbeard.newznabProviderList:
+    $(this).addProvider('${curNewznabProvider.getID()}', '${curNewznabProvider.name}', '${curNewznabProvider.url}', '${curNewznabProvider.key}', '${curNewznabProvider.catIDs}', ${int(curNewznabProvider.default)}, show_nzb_providers);
+    % endfor
 });
-//-->
 </script>
-#end if
+% endif
 
-#if $sickbeard.USE_TORRENTS
+% if sickbeard.USE_TORRENTS:
 <script type="text/javascript" charset="utf-8">
-<!--
-\$(document).ready(function(){
-#for $curTorrentRssProvider in $sickbeard.torrentRssProviderList:
-    \$(this).addTorrentRssProvider('$curTorrentRssProvider.getID()', '$curTorrentRssProvider.name', '$curTorrentRssProvider.url', '$curTorrentRssProvider.cookies', '$curTorrentRssProvider.titleTAG');
-#end for
+$(document).ready(function(){
+% for curTorrentRssProvider in sickbeard.torrentRssProviderList:
+    $(this).addTorrentRssProvider('${curTorrentRssProvider.getID()}', '${curTorrentRssProvider.name}', '${curTorrentRssProvider.url}', '${curTorrentRssProvider.cookies}', '${curTorrentRssProvider.titleTAG}');
+% endfor
 });
-//-->
 </script>
-#end if
+% endif
 
 <div id="config">
     <div id="config-content">
@@ -53,12 +43,12 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                 <ul>
                     <li><a href="#core-component-group1">Provider Priorities</a></li>
                     <li><a href="#core-component-group2">Provider Options</a></li>
-                  #if $sickbeard.USE_NZBS
+                  % if sickbeard.USE_NZBS:
                     <li><a href="#core-component-group3">Configure Custom Newznab Providers</a></li>
-                  #end if
-                  #if $sickbeard.USE_TORRENTS
+                  % endif
+                  % if sickbeard.USE_TORRENTS:
                     <li><a href="#core-component-group4">Configure Custom Torrent Providers</a></li>
-                  #end if
+                  % endif
                 </ul>
 
                 <div id="core-component-group1" class="component-group" style='min-height: 550px;'>
@@ -68,39 +58,39 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                         <p>Check off and drag the providers into the order you want them to be used.</p>
                         <p>At least one provider is required but two are recommended.</p>
 
-                        #if not $sickbeard.USE_NZBS or not $sickbeard.USE_TORRENTS:
-                        <blockquote style="margin: 20px 0;">NZB/Torrent providers can be toggled in <b><a href="$sbRoot/config/search">Search Settings</a></b></blockquote>
-                        #else:
+                        % if not sickbeard.USE_NZBS or not sickbeard.USE_TORRENTS:
+                        <blockquote style="margin: 20px 0;">NZB/Torrent providers can be toggled in <b><a href="${sbRoot}/config/search">Search Settings</a></b></blockquote>
+                        % else:
                         <br/>
-                        #end if
+                        % endif
 
                         <div>
                             <p class="note">* Provider does not support backlog searches at this time.</p>
-                            <p class="note">** Provider supports <b>limited</b> backlog searches, all episodes/qualities may not be available.</p>
                             <p class="note">! Provider is <b>NOT WORKING</b>.</p>
                         </div>
                     </div>
 
                     <fieldset class="component-group-list">
                         <ul id="provider_order_list">
-                        #for $curProvider in $sickbeard.providers.sortedProviderList():
-                            #if $curProvider.providerType == $GenericProvider.NZB and not $sickbeard.USE_NZBS:
-                                #continue
-                            #elif $curProvider.providerType == $GenericProvider.TORRENT and not $sickbeard.USE_TORRENTS:
-                                #continue
-                            #end if
-                            #set $curName = $curProvider.getID()
-                          <li class="ui-state-default" id="$curName">
-                            <input type="checkbox" id="enable_$curName" class="provider_enabler" #if $curProvider.isEnabled() then "checked=\"checked\"" else ""#/>
-                            <a href="<%= anon_url(curProvider.url) %>" class="imgLink" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/providers/$curProvider.imageName()" alt="$curProvider.name" title="$curProvider.name" width="16" height="16" style="vertical-align:middle;"/></a>
-                            <span style="vertical-align:middle;">$curProvider.name</span>
-                            #if not $curProvider.supportsBacklog then "*" else ""#
-                            #if $curProvider.name == "EZRSS" then "**" else ""#
-                            <span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="vertical-align:middle;"></span>
-                          </li>
-                        #end for
+                        % for curProvider in sickbeard.providers.sortedProviderList():
+                            <%
+                                if curProvider.providerType == GenericProvider.NZB and not sickbeard.USE_NZBS:
+                                    continue
+                                elif curProvider.providerType == GenericProvider.TORRENT and not sickbeard.USE_TORRENTS:
+                                    continue
+
+                                curName = curProvider.getID()
+                            %>
+                            <li class="ui-state-default" id="${curName}">
+                                <input type="checkbox" id="enable_${curName}" class="provider_enabler" ${('', 'checked="checked"')[curProvider.isEnabled() == True]}/>
+                                <a href="${anon_url(curProvider.url)}" class="imgLink" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><img src="${sbRoot}/images/providers/${curProvider.imageName()}" alt="${curProvider.name}" title="${curProvider.name}" width="16" height="16" style="vertical-align:middle;"/></a>
+                                <span style="vertical-align:middle;">${curProvider.name}</span>
+                                ${('*', '')[bool(curProvider.supportsBacklog)]}
+                                <span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="vertical-align:middle;"></span>
+                            </li>
+                        % endfor
                         </ul>
-                        <input type="hidden" name="provider_order" id="provider_order" value="<%=" ".join([x.getID()+':'+str(int(x.isEnabled())) for x in sickbeard.providers.sortedProviderList()])%>"/>
+                        <input type="hidden" name="provider_order" id="provider_order" value="${" ".join([x.getID()+':'+str(int(x.isEnabled())) for x in sickbeard.providers.sortedProviderList()])}"/>
                         <br/><input type="submit" class="btn config_submitter" value="Save Changes" /><br/>
                     </fieldset>
                 </div><!-- /component-group1 //-->
@@ -118,39 +108,38 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                             <label for="editAProvider" id="provider-list">
                                 <span class="component-title">Configure provider:</span>
                                 <span class="component-desc">
-                                    #set $provider_config_list = []
-                                    #for $curProvider in $sickbeard.providers.sortedProviderList():
-                                        #if $curProvider.providerType == $GenericProvider.NZB and (not $sickbeard.USE_NZBS or not $curProvider.isEnabled()):
-                                         #continue
-                                        #elif $curProvider.providerType == $GenericProvider.TORRENT and ( not $sickbeard.USE_TORRENTS or not $curProvider.isEnabled()):
-                                         #continue
-                                        #end if
-                                        $provider_config_list.append($curProvider)
-                                    #end for
-
-                                    #if $provider_config_list:
-                                    <select id="editAProvider" class="form-control input-sm">
-                                        #for $cur_provider in $provider_config_list:
-                                            <option value="$cur_provider.getID()">$cur_provider.name</option>
-                                        #end for
-                                    </select>
-                                    #else:
-                                    No providers available to configure.
-                                    #end if
+                                    <%
+                                        provider_config_list = []
+                                        for curProvider in sickbeard.providers.sortedProviderList():
+                                            if curProvider.providerType == GenericProvider.NZB and (not sickbeard.USE_NZBS or not curProvider.isEnabled()):
+                                                continue
+                                            elif curProvider.providerType == GenericProvider.TORRENT and ( not sickbeard.USE_TORRENTS or not curProvider.isEnabled()):
+                                                continue
+                                            provider_config_list.append(curProvider)
+                                    %>
+                                    % if provider_config_list:
+                                        <select id="editAProvider" class="form-control input-sm">
+                                            % for cur_provider in provider_config_list:
+                                                <option value="${cur_provider.getID()}">${cur_provider.name}</option>
+                                            % endfor
+                                        </select>
+                                    % else:
+                                        No providers available to configure.
+                                    % endif
                                 </span>
                             </label>
                         </div>
 
 
                     <!-- start div for editing providers //-->
-                    #for $curNewznabProvider in [$curProvider for $curProvider in $sickbeard.newznabProviderList]:
+                    % for curNewznabProvider in [curProvider for curProvider in sickbeard.newznabProviderList]:
                     <div class="providerDiv" id="${curNewznabProvider.getID()}Div">
-                        #if $curNewznabProvider.default and $curNewznabProvider.needs_auth
+                        % if curNewznabProvider.default and curNewznabProvider.needs_auth:
                         <div class="field-pair">
                             <label for="${curNewznabProvider.getID()}_url">
                                 <span class="component-title">URL:</span>
                                 <span class="component-desc">
-                                    <input type="text" id="${curNewznabProvider.getID()}_url" value="$curNewznabProvider.url" class="form-control input-sm input350" disabled/>
+                                    <input type="text" id="${curNewznabProvider.getID()}_url" value="${curNewznabProvider.url}" class="form-control input-sm input350" disabled/>
                                 </span>
                             </label>
                         </div>
@@ -158,49 +147,49 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                             <label for="${curNewznabProvider.getID()}_hash">
                                 <span class="component-title">API key:</span>
                                 <span class="component-desc">
-                                    <input type="text" id="${curNewznabProvider.getID()}_hash" value="$curNewznabProvider.key" newznab_name="${curNewznabProvider.getID()}_hash" class="newznab_key form-control input-sm input350" />
+                                    <input type="text" id="${curNewznabProvider.getID()}_hash" value="${curNewznabProvider.key}" newznab_name="${curNewznabProvider.getID()}_hash" class="newznab_key form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNewznabProvider, 'enable_daily'):
+                        % if hasattr(curNewznabProvider, 'enable_daily'):
                         <div class="field-pair">
                             <label for="${curNewznabProvider.getID()}_enable_daily">
                                 <span class="component-title">Enable daily searches</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curNewznabProvider.getID()}_enable_daily" id="${curNewznabProvider.getID()}_enable_daily" #if $curNewznabProvider.enable_daily then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curNewznabProvider.getID()}_enable_daily" id="${curNewznabProvider.getID()}_enable_daily" ${('', 'checked="checked"')[bool(curNewznabProvider.enable_daily)]}/>
                                     <p>enable provider to perform daily searches.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNewznabProvider, 'enable_backlog'):
+                        % if hasattr(curNewznabProvider, 'enable_backlog'):
                         <div class="field-pair">
                             <label for="${curNewznabProvider.getID()}_enable_backlog">
                                 <span class="component-title">Enable backlog searches</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curNewznabProvider.getID()}_enable_backlog" id="${curNewznabProvider.getID()}_enable_backlog" #if $curNewznabProvider.enable_backlog then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curNewznabProvider.getID()}_enable_backlog" id="${curNewznabProvider.getID()}_enable_backlog" ${('', 'checked="checked"')[bool(curNewznabProvider.enable_backlog)]}/>
                                     <p>enable provider to perform backlog searches.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNewznabProvider, 'search_fallback'):
+                        % if hasattr(curNewznabProvider, 'search_fallback'):
                         <div class="field-pair">
                             <label for="${curNewznabProvider.getID()}_search_fallback">
                                 <span class="component-title">Season search fallback</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curNewznabProvider.getID()}_search_fallback" id="${curNewznabProvider.getID()}_search_fallback" #if $curNewznabProvider.search_fallback then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curNewznabProvider.getID()}_search_fallback" id="${curNewznabProvider.getID()}_search_fallback" ${('', 'checked="checked"')[bool(curNewznabProvider.search_fallback)]}/>
                                     <p>when searching for a complete season depending on search mode you may return no results, this helps by restarting the search using the opposite search mode.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNewznabProvider, 'search_mode'):
+                        % if hasattr(curNewznabProvider, 'search_mode'):
                         <div class="field-pair">
                             <label>
                                 <span class="component-title">Season search mode</span>
@@ -211,83 +200,83 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                             <label>
                                 <span class="component-title"></span>
                                 <span class="component-desc">
-                                    <input type="radio" name="${curNewznabProvider.getID()}_search_mode" id="${curNewznabProvider.getID()}_search_mode_sponly" value="sponly" #if $curNewznabProvider.search_mode=="sponly" then "checked=\"checked\"" else ""# />season packs only.
+                                    <input type="radio" name="${curNewznabProvider.getID()}_search_mode" id="${curNewznabProvider.getID()}_search_mode_sponly" value="sponly" ${('', 'checked="checked"')[curNewznabProvider.search_mode=="sponly"]}/>season packs only.
                                 </span>
                             </label>
                             <label>
                                 <span class="component-title"></span>
                                 <span class="component-desc">
-                                    <input type="radio" name="${curNewznabProvider.getID()}_search_mode" id="${curNewznabProvider.getID()}_search_mode_eponly" value="eponly" #if $curNewznabProvider.search_mode=="eponly" then "checked=\"checked\"" else ""# />episodes only.
+                                    <input type="radio" name="${curNewznabProvider.getID()}_search_mode" id="${curNewznabProvider.getID()}_search_mode_eponly" value="eponly" ${('', 'checked="checked"')[curNewznabProvider.search_mode=="eponly"]}/>episodes only.
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
                     </div>
-                    #end for
+                    % endfor
 
-                    #for $curNzbProvider in [$curProvider for $curProvider in $sickbeard.providers.sortedProviderList() if $curProvider.providerType == $GenericProvider.NZB and $curProvider not in $sickbeard.newznabProviderList]:
+                    % for curNzbProvider in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if curProvider.providerType == GenericProvider.NZB and curProvider not in sickbeard.newznabProviderList]:
                     <div class="providerDiv" id="${curNzbProvider.getID()}Div">
-                        #if $hasattr($curNzbProvider, 'username'):
+                        % if hasattr(curNzbProvider, 'username'):
                         <div class="field-pair">
                             <label for="${curNzbProvider.getID()}_username">
                                 <span class="component-title">Username:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curNzbProvider.getID()}_username" value="$curNzbProvider.username" class="form-control input-sm input350" />
+                                    <input type="text" name="${curNzbProvider.getID()}_username" value="${curNzbProvider.username}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNzbProvider, 'api_key'):
+                        % if hasattr(curNzbProvider, 'api_key'):
                         <div class="field-pair">
                             <label for="${curNzbProvider.getID()}_api_key">
                                 <span class="component-title">API key:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curNzbProvider.getID()}_api_key" value="$curNzbProvider.api_key" class="form-control input-sm input350" />
+                                    <input type="text" name="${curNzbProvider.getID()}_api_key" value="${curNzbProvider.api_key}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
 
-                        #if $hasattr($curNzbProvider, 'enable_daily'):
+                        % if hasattr(curNzbProvider, 'enable_daily'):
                         <div class="field-pair">
                             <label for="${curNzbProvider.getID()}_enable_daily">
                                 <span class="component-title">Enable daily searches</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curNzbProvider.getID()}_enable_daily" id="${curNzbProvider.getID()}_enable_daily" #if $curNzbProvider.enable_daily then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curNzbProvider.getID()}_enable_daily" id="${curNzbProvider.getID()}_enable_daily" ${('', 'checked="checked"')[bool(curNzbProvider.enable_daily)]}/>
                                     <p>enable provider to perform daily searches.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNzbProvider, 'enable_backlog'):
+                        % if hasattr(curNzbProvider, 'enable_backlog'):
                         <div class="field-pair">
                             <label for="${curNzbProvider.getID()}_enable_backlog">
                                 <span class="component-title">Enable backlog searches</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curNzbProvider.getID()}_enable_backlog" id="${curNzbProvider.getID()}_enable_backlog" #if $curNzbProvider.enable_backlog then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curNzbProvider.getID()}_enable_backlog" id="${curNzbProvider.getID()}_enable_backlog" ${('', 'checked="checked"')[bool(curNzbProvider.enable_backlog)]}/>
                                     <p>enable provider to perform backlog searches.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNzbProvider, 'search_fallback'):
+                        % if hasattr(curNzbProvider, 'search_fallback'):
                         <div class="field-pair">
                             <label for="${curNzbProvider.getID()}_search_fallback">
                                 <span class="component-title">Season search fallback</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curNzbProvider.getID()}_search_fallback" id="${curNzbProvider.getID()}_search_fallback" #if $curNzbProvider.search_fallback then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curNzbProvider.getID()}_search_fallback" id="${curNzbProvider.getID()}_search_fallback" ${('', 'checked="checked"')[bool(curNzbProvider.search_fallback)]}/>
                                     <p>when searching for a complete season depending on search mode you may return no results, this helps by restarting the search using the opposite search mode.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curNzbProvider, 'search_mode'):
+                        % if hasattr(curNzbProvider, 'search_mode'):
                         <div class="field-pair">
                             <label>
                                 <span class="component-title">Season search mode</span>
@@ -298,95 +287,95 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                             <label>
                                 <span class="component-title"></span>
                                 <span class="component-desc">
-                                    <input type="radio" name="${curNzbProvider.getID()}_search_mode" id="${curNzbProvider.getID()}_search_mode_sponly" value="sponly" #if $curNzbProvider.search_mode=="sponly" then "checked=\"checked\"" else ""# />season packs only.
+                                    <input type="radio" name="${curNzbProvider.getID()}_search_mode" id="${curNzbProvider.getID()}_search_mode_sponly" value="sponly" ${('', 'checked="checked"')[curNzbProvider.search_mode=="sponly"]}/>season packs only.
                                 </span>
                             </label>
                             <label>
                                 <span class="component-title"></span>
                                 <span class="component-desc">
-                                    <input type="radio" name="${curNzbProvider.getID()}_search_mode" id="${curNzbProvider.getID()}_search_mode_eponly" value="eponly" #if $curNzbProvider.search_mode=="eponly" then "checked=\"checked\"" else ""# />episodes only.
+                                    <input type="radio" name="${curNzbProvider.getID()}_search_mode" id="${curNzbProvider.getID()}_search_mode_eponly" value="eponly" ${('', 'checked="checked"')[curNzbProvider.search_mode=="eponly"]}/>episodes only.
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
                     </div>
-                    #end for
+                    % endfor
 
-                    #for $curTorrentProvider in [$curProvider for $curProvider in $sickbeard.providers.sortedProviderList() if $curProvider.providerType == $GenericProvider.TORRENT]:
+                    % for curTorrentProvider in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if curProvider.providerType == GenericProvider.TORRENT]:
                     <div class="providerDiv" id="${curTorrentProvider.getID()}Div">
-                        #if $hasattr($curTorrentProvider, 'api_key'):
+                        % if hasattr(curTorrentProvider, 'api_key'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_api_key">
                                 <span class="component-title">Api key:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_api_key" id="${curTorrentProvider.getID()}_api_key" value="$curTorrentProvider.api_key" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_api_key" id="${curTorrentProvider.getID()}_api_key" value="${curTorrentProvider.api_key}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'digest'):
+                        % if hasattr(curTorrentProvider, 'digest'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_digest">
                                 <span class="component-title">Digest:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_digest" id="${curTorrentProvider.getID()}_digest" value="$curTorrentProvider.digest" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_digest" id="${curTorrentProvider.getID()}_digest" value="${curTorrentProvider.digest}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'hash'):
+                        % if hasattr(curTorrentProvider, 'hash'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_hash">
                                 <span class="component-title">Hash:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_hash" id="${curTorrentProvider.getID()}_hash" value="$curTorrentProvider.hash" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_hash" id="${curTorrentProvider.getID()}_hash" value="${curTorrentProvider.hash}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'username'):
+                        % if hasattr(curTorrentProvider, 'username'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_username">
                                 <span class="component-title">Username:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_username" id="${curTorrentProvider.getID()}_username" value="$curTorrentProvider.username" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_username" id="${curTorrentProvider.getID()}_username" value="${curTorrentProvider.username}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'password'):
+                        % if hasattr(curTorrentProvider, 'password'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_password">
                                 <span class="component-title">Password:</span>
                                 <span class="component-desc">
-                                    <input type="password" name="${curTorrentProvider.getID()}_password" id="${curTorrentProvider.getID()}_password" value="$curTorrentProvider.password" class="form-control input-sm input350" />
+                                    <input type="password" name="${curTorrentProvider.getID()}_password" id="${curTorrentProvider.getID()}_password" value="${curTorrentProvider.password}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'passkey'):
+                        % if hasattr(curTorrentProvider, 'passkey'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_passkey">
                                 <span class="component-title">Passkey:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_passkey" id="${curTorrentProvider.getID()}_passkey" value="$curTorrentProvider.passkey" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_passkey" id="${curTorrentProvider.getID()}_passkey" value="${curTorrentProvider.passkey}" class="form-control input-sm input350" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'ratio'):
+                        % if hasattr(curTorrentProvider, 'ratio'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_ratio">
                                 <span class="component-title" id="${curTorrentProvider.getID()}_ratio_desc">Seed ratio:</span>
                                 <span class="component-desc">
-                                    <input type="number" step="0.1" name="${curTorrentProvider.getID()}_ratio" id="${curTorrentProvider.getID()}_ratio" value="$curTorrentProvider.ratio" class="form-control input-sm input75" />
+                                    <input type="number" step="0.1" name="${curTorrentProvider.getID()}_ratio" id="${curTorrentProvider.getID()}_ratio" value="${curTorrentProvider.ratio}" class="form-control input-sm input75" />
                                 </span>
                             </label>
                             <label>
@@ -396,145 +385,145 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'minseed'):
+                        % if hasattr(curTorrentProvider, 'minseed'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_minseed">
                                 <span class="component-title" id="${curTorrentProvider.getID()}_minseed_desc">Minimum seeders:</span>
                                 <span class="component-desc">
-                                    <input type="number" name="${curTorrentProvider.getID()}_minseed" id="${curTorrentProvider.getID()}_minseed" value="$curTorrentProvider.minseed" class="form-control input-sm input75" />
+                                    <input type="number" name="${curTorrentProvider.getID()}_minseed" id="${curTorrentProvider.getID()}_minseed" value="${curTorrentProvider.minseed}" class="form-control input-sm input75" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'minleech'):
+                        % if hasattr(curTorrentProvider, 'minleech'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_minleech">
                                 <span class="component-title" id="${curTorrentProvider.getID()}_minleech_desc">Minimum leechers:</span>
                                 <span class="component-desc">
-                                    <input type="number" name="${curTorrentProvider.getID()}_minleech" id="${curTorrentProvider.getID()}_minleech" value="$curTorrentProvider.minleech" class="form-control input-sm input75" />
+                                    <input type="number" name="${curTorrentProvider.getID()}_minleech" id="${curTorrentProvider.getID()}_minleech" value="${curTorrentProvider.minleech}" class="form-control input-sm input75" />
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'confirmed'):
+                        % if hasattr(curTorrentProvider, 'confirmed'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_confirmed">
                                 <span class="component-title">Confirmed download</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_confirmed" id="${curTorrentProvider.getID()}_confirmed" #if $curTorrentProvider.confirmed then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_confirmed" id="${curTorrentProvider.getID()}_confirmed" ${('', 'checked="checked"')[bool(curTorrentProvider.confirmed)]}/>
                                     <p>only download torrents from trusted or verified uploaders ?</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'ranked'):
+                        % if hasattr(curTorrentProvider, 'ranked'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_ranked">
                                 <span class="component-title">Ranked torrents</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_ranked" id="${curTorrentProvider.getID()}_ranked" #if $curTorrentProvider.ranked then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_ranked" id="${curTorrentProvider.getID()}_ranked" ${('', 'checked="checked"')[bool(curTorrentProvider.ranked)]} />
                                     <p>only download ranked torrents (internal releases)</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'sorting'):
+                        % if hasattr(curTorrentProvider, 'sorting'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_sorting">
                                 <span class="component-title">Sorting results by</span>
                                 <span class="component-desc">
                                     <select name="${curTorrentProvider.getID()}_sorting" id="${curTorrentProvider.getID()}_sorting" class="form-control input-sm">
-#for $curAction in ('last', 'seeders', 'leechers'):
-    <option value="$curAction" #if $curAction == $curTorrentProvider.sorting then ' selected="selected"' else ''#>$curAction</option>
-#end for
+                                    % for curAction in ('last', 'seeders', 'leechers'):
+                                    <option value="${curAction}" ${('', 'selected="selected"')[curAction == curTorrentProvider.sorting]}>${curAction}</option>
+                                    % endfor
                                     </select>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'proxy'):
+                        % if hasattr(curTorrentProvider, 'proxy'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_proxy">
                                 <span class="component-title">Access provider via proxy</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" name="${curTorrentProvider.getID()}_proxy" id="${curTorrentProvider.getID()}_proxy" #if $curTorrentProvider.proxy.enabled then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" class="enabler" name="${curTorrentProvider.getID()}_proxy" id="${curTorrentProvider.getID()}_proxy" ${('', 'checked="checked"')[bool(curTorrentProvider.proxy.enabled)]}/>
                                     <p>to bypass country blocking mechanisms</p>
                                 </span>
                             </label>
                         </div>
 
-                        #if $hasattr($curTorrentProvider.proxy, 'url'):
+                        % if hasattr(curTorrentProvider.proxy, 'url'):
                         <div class="field-pair content_${curTorrentProvider.getID()}_proxy" id="content_${curTorrentProvider.getID()}_proxy">
                             <label for="${curTorrentProvider.getID()}_proxy_url">
                                 <span class="component-title">Proxy URL:</span>
                                 <span class="component-desc">
                                   <select name="${curTorrentProvider.getID()}_proxy_url" id="${curTorrentProvider.getID()}_proxy_url" class="form-control input-sm">
-                                    #for $i in $curTorrentProvider.proxy.urls.keys():
-                                    <option value="$curTorrentProvider.proxy.urls[$i]" #if $curTorrentProvider.proxy.urls[$i] == $curTorrentProvider.proxy.url then "selected=\"selected\"" else ""#>$i</option>
-                                    #end for
+                                    % for i in curTorrentProvider.proxy.urls.keys():
+                                    <option value="${curTorrentProvider.proxy.urls[i]}" ${('', 'selected="selected"')[curTorrentProvider.proxy.urls[i] == curTorrentProvider.proxy.url]}>${i}</option>
+                                    % endfor
                                     </select>
                                 </span>
                             </label>
                         </div>
-                        #end if
-                        #end if
+                        % endif
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'freeleech'):
+                        % if hasattr(curTorrentProvider, 'freeleech'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_freeleech">
                                 <span class="component-title">Freeleech</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_freeleech" id="${curTorrentProvider.getID()}_freeleech" #if $curTorrentProvider.freeleech then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_freeleech" id="${curTorrentProvider.getID()}_freeleech" ${('', 'checked="checked"')[bool(curTorrentProvider.freeleech)]}/>
                                     <p>only download <b>[FreeLeech]</b> torrents.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'enable_daily'):
+                        % if hasattr(curTorrentProvider, 'enable_daily'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_enable_daily">
                                 <span class="component-title">Enable daily searches</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_enable_daily" id="${curTorrentProvider.getID()}_enable_daily" #if $curTorrentProvider.enable_daily then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_enable_daily" id="${curTorrentProvider.getID()}_enable_daily" ${('', 'checked="checked"')[bool(curTorrentProvider.enable_daily)]}/>
                                     <p>enable provider to perform daily searches.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'enable_backlog'):
+                        % if hasattr(curTorrentProvider, 'enable_backlog'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_enable_backlog">
                                 <span class="component-title">Enable backlog searches</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_enable_backlog" id="${curTorrentProvider.getID()}_enable_backlog" #if $curTorrentProvider.enable_backlog then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_enable_backlog" id="${curTorrentProvider.getID()}_enable_backlog" ${('', 'checked="checked"')[bool(curTorrentProvider.enable_backlog)]}/>
                                     <p>enable provider to perform backlog searches.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'search_fallback'):
+                        % if hasattr(curTorrentProvider, 'search_fallback'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_search_fallback">
                                 <span class="component-title">Season search fallback</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_search_fallback" id="${curTorrentProvider.getID()}_search_fallback" #if $curTorrentProvider.search_fallback then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_search_fallback" id="${curTorrentProvider.getID()}_search_fallback" ${('', 'checked="checked"')[bool(curTorrentProvider.search_fallback)]}/>
                                     <p>when searching for a complete season depending on search mode you may return no results, this helps by restarting the search using the opposite search mode.</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'search_mode'):
+                        % if hasattr(curTorrentProvider, 'search_mode'):
                         <div class="field-pair">
                             <label>
                                 <span class="component-title">Season search mode</span>
@@ -545,47 +534,47 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                             <label>
                                 <span class="component-title"></span>
                                 <span class="component-desc">
-                                    <input type="radio" name="${curTorrentProvider.getID()}_search_mode" id="${curTorrentProvider.getID()}_search_mode_sponly" value="sponly" #if $curTorrentProvider.search_mode=="sponly" then "checked=\"checked\"" else ""# />season packs only.
+                                    <input type="radio" name="${curTorrentProvider.getID()}_search_mode" id="${curTorrentProvider.getID()}_search_mode_sponly" value="sponly" ${('', 'checked="checked"')[curTorrentProvider.search_mode=="sponly"]}/>season packs only.
                                 </span>
                             </label>
                             <label>
                                 <span class="component-title"></span>
                                 <span class="component-desc">
-                                    <input type="radio" name="${curTorrentProvider.getID()}_search_mode" id="${curTorrentProvider.getID()}_search_mode_eponly" value="eponly" #if $curTorrentProvider.search_mode=="eponly" then "checked=\"checked\"" else ""# />episodes only.
+                                    <input type="radio" name="${curTorrentProvider.getID()}_search_mode" id="${curTorrentProvider.getID()}_search_mode_eponly" value="eponly" ${('', 'checked="checked"')[curTorrentProvider.search_mode=="eponly"]}/>episodes only.
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'cat') and $curTorrentProvider.getID() == 'tntvillage':
+                        % if hasattr(curTorrentProvider, 'cat') and curTorrentProvider.getID() == 'tntvillage':
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_cat">
                                 <span class="component-title">Category:</span>
                                 <span class="component-desc">
                                     <select name="${curTorrentProvider.getID()}_cat" id="${curTorrentProvider.getID()}_cat" class="form-control input-sm">
-                                        #for $i in $curTorrentProvider.category_dict.keys():
-                                        <option value="$curTorrentProvider.category_dict[$i]" #if $curTorrentProvider.category_dict[$i] == $curTorrentProvider.cat then "selected=\"selected\"" else ""#>$i</option>
-                                        #end for
+                                        % for i in curTorrentProvider.category_dict.keys():
+                                        <option value="${curTorrentProvider.category_dict[i]}" ${('', 'selected="selected"')[curTorrentProvider.category_dict[i] == curTorrentProvider.cat]}>${i}</option>
+                                        % endfor
                                     </select>
                                 </span>
                            </label>
                         </div>
-                        #end if
+                        % endif
 
-                        #if $hasattr($curTorrentProvider, 'subtitle') and $curTorrentProvider.getID() == 'tntvillage':
+                        % if hasattr(curTorrentProvider, 'subtitle') and curTorrentProvider.getID() == 'tntvillage':
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_subtitle">
                                 <span class="component-title">Subtitled</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="${curTorrentProvider.getID()}_subtitle" id="${curTorrentProvider.getID()}_subtitle" #if $curTorrentProvider.subtitle then "checked=\"checked\"" else ""#/>
+                                    <input type="checkbox" name="${curTorrentProvider.getID()}_subtitle" id="${curTorrentProvider.getID()}_subtitle" ${('', 'checked="checked"')[bool(curTorrentProvider.subtitle)]}/>
                                     <p>select torrent with Italian subtitle</p>
                                 </span>
                             </label>
                         </div>
-                        #end if
+                        % endif
 
                     </div>
-                    #end for
+                    % endfor
 
 
                     <!-- end div for editing providers -->
@@ -595,7 +584,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                     </fieldset>
                 </div><!-- /component-group2 //-->
 
-                #if $sickbeard.USE_NZBS
+                % if sickbeard.USE_NZBS:
                 <div id="core-component-group3" class="component-group">
 
                     <div class="component-group-desc">
@@ -668,9 +657,9 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
 
                     </fieldset>
                 </div><!-- /component-group3 //-->
-                #end if
+                % endif
 
-                #if $sickbeard.USE_TORRENTS:
+                % if sickbeard.USE_TORRENTS:
 
                 <div id="core-component-group4" class="component-group">
 
@@ -734,7 +723,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                     </div>
                 </fieldset>
             </div><!-- /component-group4 //-->
-            #end if
+            % endif
 
             <br/><input type="submit" class="btn config_submitter_refresh" value="Save Changes" /><br/>
 
@@ -745,8 +734,6 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
 </div>
 
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#config-components').tabs();
-//-->
 </script>
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_search.tmpl b/gui/slick/interfaces/default/config_search.mako
similarity index 73%
rename from gui/slick/interfaces/default/config_search.tmpl
rename to gui/slick/interfaces/default/config_search.mako
index 3e0d97712478a2b6b1c3cae7f98c33fe74c4e4f8..11592fcacf84bb78f9fe91d0ded0e62dd279babe 100644
--- a/gui/slick/interfaces/default/config_search.tmpl
+++ b/gui/slick/interfaces/default/config_search.mako
@@ -1,25 +1,17 @@
-#import sickbeard
-#from sickbeard import clients
-#set global $title = 'Config - Episode Search'
-#set global $header = 'Search Settings'
+<%
+    import sickbeard
+    from sickbeard import clients
+%>
+<%include file="/inc_top.mako"/>
 
-#set global $sbPath = '../..'
+<script type="text/javascript" src="${sbRoot}/js/configSearch.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script>
 
-#set global $topmenu = 'config'
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
-
-<script type="text/javascript" src="$sbRoot/js/configSearch.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-#set $html_selected = ' selected="selected"'
-#set $html_checked = 'checked="checked" '
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="config">
     <div id="config-content">
@@ -38,7 +30,7 @@
 
                     <div class="component-group-desc">
                         <h3>Episode Search</h3>
-                        <p>How to manage searching with <a href="$sbRoot/config/providers">providers</a>.</p>
+                        <p>How to manage searching with <a href="${sbRoot}/config/providers">providers</a>.</p>
                     </div>
 
                     <fieldset class="component-group-list">
@@ -46,7 +38,7 @@
                             <label for="randomize_providers">
                                 <span class="component-title">Randomize Providers</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="randomize_providers" id="randomize_providers" class="enabler" <%= html_checked if sickbeard.RANDOMIZE_PROVIDERS == True else '' %>/>
+                                    <input type="checkbox" name="randomize_providers" id="randomize_providers" class="enabler" ${('', 'checked="checked"')[bool(sickbeard.RANDOMIZE_PROVIDERS)]}/>
                                     <p>randomize the provider search order instead of going in order of placement</p>
                                 </span>
                             </label>
@@ -55,7 +47,7 @@
                             <label for="download_propers">
                                 <span class="component-title">Download propers</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="download_propers" id="download_propers" class="enabler" <%= html_checked if sickbeard.DOWNLOAD_PROPERS == True else '' %>/>
+                                    <input type="checkbox" name="download_propers" id="download_propers" class="enabler" ${('', 'checked="checked"')[bool(sickbeard.DOWNLOAD_PROPERS)]}/>
                                     <p>replace original download with "Proper" or "Repack" if nuked</p>
                                 </span>
                             </label>
@@ -66,11 +58,10 @@
                                     <span class="component-title">Check propers every:</span>
                                     <span class="component-desc">
                                         <select id="check_propers_interval" name="check_propers_interval" class="form-control input-sm">
-#set $check_propers_interval_text = {'daily': "24 hours", '4h': "4 hours", '90m': "90 mins", '45m': "45 mins", '15m': "15 mins"}
-#for $curInterval in ('daily', '4h', '90m', '45m', '15m'):
-    #set $selected = $html_selected if $sickbeard.CHECK_PROPERS_INTERVAL == $curInterval else ''
-                                            <option value="$curInterval"$selected>$check_propers_interval_text[$curInterval]</option>
-#end for
+<% check_propers_interval_text = {'daily': "24 hours", '4h': "4 hours", '90m': "90 mins", '45m': "45 mins", '15m': "15 mins"} %>
+% for curInterval in ('daily', '4h', '90m', '45m', '15m'):
+                                            <option value="${curInterval}" ${('', 'selected="selected"')[sickbeard.CHECK_PROPERS_INTERVAL == curInterval]}>${check_propers_interval_text[curInterval]}</option>
+% endfor
                                         </select>
                                     </span>
                                 </label>
@@ -81,8 +72,8 @@
                             <label>
                                 <span class="component-title">Backlog search frequency</span>
                                 <span class="component-desc">
-                                    <input type="text" name="backlog_frequency" value="$sickbeard.BACKLOG_FREQUENCY" class="form-control input-sm input75" />
-                                    <p>time in minutes between searches (min. $sickbeard.MIN_BACKLOG_FREQUENCY)</p>
+                                    <input type="text" name="backlog_frequency" value="${sickbeard.BACKLOG_FREQUENCY}" class="form-control input-sm input75" />
+                                    <p>time in minutes between searches (min. ${sickbeard.MIN_BACKLOG_FREQUENCY})</p>
                                 </span>
                             </label>
                         </div>
@@ -91,8 +82,8 @@
                             <label>
                                 <span class="component-title">Daily search frequency</span>
                                 <span class="component-desc">
-                                    <input type="text" name="dailysearch_frequency" value="$sickbeard.DAILYSEARCH_FREQUENCY" class="form-control input-sm input75" />
-                                    <p>time in minutes between searches (min. $sickbeard.MIN_DAILYSEARCH_FREQUENCY)</p>
+                                    <input type="text" name="dailysearch_frequency" value="${sickbeard.DAILYSEARCH_FREQUENCY}" class="form-control input-sm input75" />
+                                    <p>time in minutes between searches (min. ${sickbeard.MIN_DAILYSEARCH_FREQUENCY})</p>
                                     </span>
                             </label>
                         </div>
@@ -101,7 +92,7 @@
                             <label>
                                 <span class="component-title">Usenet retention</span>
                                 <span class="component-desc">
-                                    <input type="text" name="usenet_retention" value="$sickbeard.USENET_RETENTION" class="form-control input-sm input75" />
+                                    <input type="text" name="usenet_retention" value="${sickbeard.USENET_RETENTION}" class="form-control input-sm input75" />
                                     <p>age limit in days for usenet articles to be used (e.g. 500)</p>
                                 </span>
                             </label>
@@ -111,7 +102,7 @@
                             <label>
                                 <span class="component-title">Ignore words</span>
                                 <span class="component-desc">
-                                    <input type="text" name="ignore_words" value="$sickbeard.IGNORE_WORDS" class="form-control input-sm input350" />
+                                    <input type="text" name="ignore_words" value="${sickbeard.IGNORE_WORDS}" class="form-control input-sm input350" />
                                     <div class="clear-left">results with one or more word from this list will be ignored<br />
                                     separate words with a comma, e.g. "word1,word2,word3"
                                     </div>
@@ -123,7 +114,7 @@
                             <label>
                                 <span class="component-title">Require words</span>
                                 <span class="component-desc">
-                                    <input type="text" name="require_words" value="$sickbeard.REQUIRE_WORDS" class="form-control input-sm input350" />
+                                    <input type="text" name="require_words" value="${sickbeard.REQUIRE_WORDS}" class="form-control input-sm input350" />
                                     <div class="clear-left">results with no word from this list will be ignored<br />
                                     separate words with a comma, e.g. "word1,word2,word3"
                                     </div>
@@ -135,7 +126,7 @@
                             <label for="allow_high_priority">
                                 <span class="component-title">Allow high priority</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="allow_high_priority" id="allow_high_priority" <%= html_checked if sickbeard.ALLOW_HIGH_PRIORITY == True else '' %>/>
+                                    <input type="checkbox" name="allow_high_priority" id="allow_high_priority" ${('', 'checked="checked"')[bool(sickbeard.ALLOW_HIGH_PRIORITY)]}/>
                                     <p>set downloads of recently aired episodes to high priority</p>
                                 </span>
                             </label>
@@ -145,7 +136,7 @@
                             <label for="dailysearch_startup">
                                 <span class="component-title">Daily search on startup</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="dailysearch_startup" id="dailysearch_startup" <%= html_checked if sickbeard.DAILYSEARCH_STARTUP == True else '' %>/>
+                                    <input type="checkbox" name="dailysearch_startup" id="dailysearch_startup" ${('', 'checked="checked"')[bool(sickbeard.DAILYSEARCH_STARTUP)]}/>
                                     <p>start daily search on startup of SickRage</p>
                                 </span>
                             </label>
@@ -155,37 +146,37 @@
                             <label for="backlog_startup">
                                 <span class="component-title">Run backlog on startup</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="backlog_startup" id="backlog_startup" <%= html_checked if sickbeard.BACKLOG_STARTUP == True else '' %>/>
+                                    <input type="checkbox" name="backlog_startup" id="backlog_startup" ${('', 'checked="checked"')[bool(sickbeard.BACKLOG_STARTUP)]}/>
                                     <p>start processing backlogged episodes on startup of SickRage</p>
                                 </span>
                             </label>
                         </div>
 
-                                                 <div class="field-pair">
-                                                     <input id="use_failed_downloads" type="checkbox" class="enabler" name="use_failed_downloads" #if $sickbeard.USE_FAILED_DOWNLOADS == True then "checked=\"checked\"" else ""# />
-                                                     <label for="use_failed_downloads">
-                                                         <span class="component-title">Use Failed Downloads</span>
-                                                         <span class="component-desc">Use Failed Download Handling?</span>
-                                                     </label>
-                                                     <label class="nocheck" for="use_failed_downloads">
-                                                         <span class="component-title">&nbsp;</span>
-                                                         <span class="component-desc">Will only work with snatched/downloaded episodes after enabling this</span>
-                                                     </label>
-                                                 </div>
-
-                                                <div id="content_use_failed_downloads">
-                                                    <div class="field-pair">
-                                                        <input id="delete_failed" type="checkbox" name="delete_failed" #if $sickbeard.DELETE_FAILED == True then "checked=\"checked\"" else ""# />
-                                                        <label for="delete_failed">
-                                                            <span class="component-title">Delete Failed</span>
-                                                            <span class="component-desc">Delete files left over from a failed download?</span>
-                                                        </label>
-                                                        <label class="nocheck" for="delete_failed">
-                                                            <span class="component-title">&nbsp;</span>
-                                                            <span class="component-desc"><b>NOTE:</b> This only works if Use Failed Downloads is enabled.</span>
-                                                        </label>
-                                                    </div>
-                                                </div>
+                         <div class="field-pair">
+                             <input id="use_failed_downloads" type="checkbox" class="enabler" name="use_failed_downloads" ${('', 'checked="checked"')[bool(sickbeard.USE_FAILED_DOWNLOADS)]} />
+                             <label for="use_failed_downloads">
+                                 <span class="component-title">Use Failed Downloads</span>
+                                 <span class="component-desc">Use Failed Download Handling?</span>
+                             </label>
+                             <label class="nocheck" for="use_failed_downloads">
+                                 <span class="component-title">&nbsp;</span>
+                                 <span class="component-desc">Will only work with snatched/downloaded episodes after enabling this</span>
+                             </label>
+                         </div>
+
+                        <div id="content_use_failed_downloads">
+                            <div class="field-pair">
+                                <input id="delete_failed" type="checkbox" name="delete_failed" ${('', 'checked="checked"')[bool(sickbeard.DELETE_FAILED)]}/>
+                                <label for="delete_failed">
+                                    <span class="component-title">Delete Failed</span>
+                                    <span class="component-desc">Delete files left over from a failed download?</span>
+                                </label>
+                                <label class="nocheck" for="delete_failed">
+                                    <span class="component-title">&nbsp;</span>
+                                    <span class="component-desc"><b>NOTE:</b> This only works if Use Failed Downloads is enabled.</span>
+                                </label>
+                            </div>
+                        </div>
 
                         <input type="submit" class="btn config_submitter" value="Save Changes" />
 
@@ -205,7 +196,7 @@
                             <label for="use_nzbs">
                                 <span class="component-title">Search NZBs</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="use_nzbs" class="enabler" id="use_nzbs" <%= html_checked if sickbeard.USE_NZBS else '' %>/>
+                                    <input type="checkbox" name="use_nzbs" class="enabler" id="use_nzbs" ${('', 'checked="checked"')[bool(sickbeard.USE_NZBS)]}/>
                                     <p>enable NZB search providers</p></span>
                             </label>
                         </div>
@@ -216,11 +207,10 @@
                                 <span class="component-title">Send .nzb files to:</span>
                                 <span class="component-desc">
                                     <select name="nzb_method" id="nzb_method" class="form-control input-sm">
-#set $nzb_method_text = {'blackhole': "Black hole", 'sabnzbd': "SABnzbd", 'nzbget': "NZBget"}
-#for $curAction in ('sabnzbd', 'blackhole', 'nzbget'):
-    #set $selected = $html_selected if $sickbeard.NZB_METHOD == $curAction else ''
-                                    <option value="$curAction"$selected>$nzb_method_text[$curAction]</option>
-#end for
+<% nzb_method_text = {'blackhole': "Black hole", 'sabnzbd': "SABnzbd", 'nzbget': "NZBget"} %>
+% for curAction in ('sabnzbd', 'blackhole', 'nzbget'):
+                                    <option value="${curAction}" ${('', 'selected="selected"')[sickbeard.NZB_METHOD == curAction]}>${nzb_method_text[curAction]}</option>
+% endfor
                                     </select>
                                 </span>
                             </label>
@@ -231,7 +221,7 @@
                                 <label>
                                     <span class="component-title">Black hole folder location</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzb_dir" id="nzb_dir" value="$sickbeard.NZB_DIR" class="form-control input-sm input350" />
+                                        <input type="text" name="nzb_dir" id="nzb_dir" value="${sickbeard.NZB_DIR}" class="form-control input-sm input350" />
                                         <div class="clear-left"><p><b>.nzb</b> files are stored at this location for external software to find and use</p></div>
                                     </span>
                                 </label>
@@ -243,7 +233,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd server URL</span>
                                     <span class="component-desc">
-                                        <input type="text" id="sab_host" name="sab_host" value="$sickbeard.SAB_HOST" class="form-control input-sm input350" />
+                                        <input type="text" id="sab_host" name="sab_host" value="${sickbeard.SAB_HOST}" class="form-control input-sm input350" />
                                         <div class="clear-left"><p>URL to your SABnzbd server (e.g. http://localhost:8080/)</p></div>
                                     </span>
                                 </label>
@@ -253,7 +243,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd username</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_username" id="sab_username" value="$sickbeard.SAB_USERNAME" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_username" id="sab_username" value="${sickbeard.SAB_USERNAME}" class="form-control input-sm input200" />
                                         <p>(blank for none)</p>
                                     </span>
                                 </label>
@@ -263,7 +253,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd password</span>
                                     <span class="component-desc">
-                                        <input type="password" name="sab_password" id="sab_password" value="$sickbeard.SAB_PASSWORD" class="form-control input-sm input200" />
+                                        <input type="password" name="sab_password" id="sab_password" value="${sickbeard.SAB_PASSWORD}" class="form-control input-sm input200" />
                                         <p>(blank for none)</p>
                                     </span>
                                 </label>
@@ -273,7 +263,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd API key</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_apikey" id="sab_apikey" value="$sickbeard.SAB_APIKEY" class="form-control input-sm input350" />
+                                        <input type="text" name="sab_apikey" id="sab_apikey" value="${sickbeard.SAB_APIKEY}" class="form-control input-sm input350" />
                                         <div class="clear-left"><p>locate at... SABnzbd Config -> General -> API Key</p></div>
                                     </span>
                                 </label>
@@ -283,7 +273,7 @@
                                 <label>
                                     <span class="component-title">Use SABnzbd category</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_category" id="sab_category" value="$sickbeard.SAB_CATEGORY" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_category" id="sab_category" value="${sickbeard.SAB_CATEGORY}" class="form-control input-sm input200" />
                                         <p>add downloads to this category (e.g. TV)</p>
                                     </span>
                                 </label>
@@ -293,21 +283,21 @@
                                 <label>
                                     <span class="component-title">Use SABnzbd category for anime</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_category_anime" id="sab_category_anime" value="$sickbeard.SAB_CATEGORY_ANIME" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_category_anime" id="sab_category_anime" value="${sickbeard.SAB_CATEGORY_ANIME}" class="form-control input-sm input200" />
                                         <p>add anime downloads to this category (e.g. anime)</p>
                                     </span>
                                 </label>
                             </div>
-                            #if sickbeard.ALLOW_HIGH_PRIORITY == True
+                            % if sickbeard.ALLOW_HIGH_PRIORITY == True:
                             <div class="field-pair">
                                 <label for="sab_forced">
                                     <span class="component-title">Use forced priority</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="sab_forced" class="enabler" id="sab_forced" <%= html_checked if sickbeard.SAB_FORCED else '' %>/>
+                                        <input type="checkbox" name="sab_forced" class="enabler" id="sab_forced" ${('', 'selected="selected"')[bool(sickbeard.SAB_FORCED)]}/>
                                         <p>enable to change priority from HIGH to FORCED</p></span>
                                 </label>
                             </div>
-                            #end if
+                            % endif
                         </div>
 
                         <div id="nzbget_settings">
@@ -315,7 +305,7 @@
                                 <label for="nzbget_use_https">
                                     <span class="component-title">Connect using HTTPS</span>
                                     <span class="component-desc">
-                                        <input id="nzbget_use_https" type="checkbox" class="enabler" name="nzbget_use_https" <%= html_checked if sickbeard.NZBGET_USE_HTTPS == True else '' %>/>
+                                        <input id="nzbget_use_https" type="checkbox" class="enabler" name="nzbget_use_https" ${('', 'selected="selected"')[bool(sickbeard.NZBGET_USE_HTTPS)]}/>
                                         <p><b>note:</b> enable Secure control in NZBGet and set the correct Secure Port here</p>
                                     </span>
                                 </label>
@@ -326,7 +316,7 @@
                                 <label>
                                     <span class="component-title">NZBget host:port</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_host" id="nzbget_host" value="$sickbeard.NZBGET_HOST" class="form-control input-sm input350" />
+                                        <input type="text" name="nzbget_host" id="nzbget_host" value="${sickbeard.NZBGET_HOST}" class="form-control input-sm input350" />
                                         <p>(e.g. localhost:6789)</p>
                                         <div class="clear-left"><p>NZBget RPC host name and port number (not NZBgetweb!)</p></div>
                                     </span>
@@ -337,7 +327,7 @@
                                 <label>
                                     <span class="component-title">NZBget username</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_username" value="$sickbeard.NZBGET_USERNAME" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_username" value="${sickbeard.NZBGET_USERNAME}" class="form-control input-sm input200" />
                                         <p>locate in nzbget.conf (default:nzbget)</p>
                                     </span>
                                 </label>
@@ -347,7 +337,7 @@
                                 <label>
                                     <span class="component-title">NZBget password</span>
                                     <span class="component-desc">
-                                        <input type="password" name="nzbget_password" id="nzbget_password" value="$sickbeard.NZBGET_PASSWORD" class="form-control input-sm input200" />
+                                        <input type="password" name="nzbget_password" id="nzbget_password" value="${sickbeard.NZBGET_PASSWORD}" class="form-control input-sm input200" />
                                         <p>locate in nzbget.conf (default:tegbzn6789)</p>
                                     </span>
                                 </label>
@@ -357,7 +347,7 @@
                                 <label>
                                     <span class="component-title">Use NZBget category</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_category" id="nzbget_category" value="$sickbeard.NZBGET_CATEGORY" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_category" id="nzbget_category" value="${sickbeard.NZBGET_CATEGORY}" class="form-control input-sm input200" />
                                         <p>send downloads marked this category (e.g. TV)</p>
                                     </span>
                                 </label>
@@ -367,44 +357,23 @@
                                 <label>
                                     <span class="component-title">Use NZBget category for anime</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_category_anime" id="nzbget_category_anime" value="$sickbeard.NZBGET_CATEGORY_ANIME" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_category_anime" id="nzbget_category_anime" value="${sickbeard.NZBGET_CATEGORY_ANIME}" class="form-control input-sm input200" />
                                         <p>send anime downloads marked this category (e.g. anime)</p>
                                     </span>
                                 </label>
                             </div>
 
-#set $prio_verylow = ''
-#set $prio_low = ''
-#set $prio_normal = ''
-#set $prio_high = ''
-#set $prio_veryhigh = ''
-#set $prio_force = ''
-#if -100 == $sickbeard.NZBGET_PRIORITY:
-    #set $prio_verylow = $html_selected
-#elif -50 == $sickbeard.NZBGET_PRIORITY:
-    #set $prio_low = $html_selected
-#elif 0 == $sickbeard.NZBGET_PRIORITY:
-    #set $prio_normal = $html_selected
-#elif 50 == $sickbeard.NZBGET_PRIORITY:
-    #set $prio_high = $html_selected
-#elif 100 == $sickbeard.NZBGET_PRIORITY:
-    #set $prio_veryhigh = $html_selected
-#elif 900 == $sickbeard.NZBGET_PRIORITY:
-    #set $prio_force = $html_selected
-#else:
-    #set $prio_normal = $html_selected
-#end if
                             <div class="field-pair">
                                 <label>
                                     <span class="component-title">NZBget priority</span>
                                     <span class="component-desc">
                                         <select name="nzbget_priority" id="nzbget_priority" class="form-control input-sm">
-                                            <option value="-100"${prio_verylow}>Very low</option>
-                                            <option value="-50"${prio_low}>Low</option>
-                                            <option value="0"${prio_normal}>Normal</option>
-                                            <option value="50"${prio_high}>High</option>
-                                            <option value="100"${prio_veryhigh}>Very high</option>
-                                            <option value="900"${prio_force}>Force</option>
+                                            <option value="-100" ${('', 'selected="selected"')[sickbeard.NZBGET_PRIORITY == -100]}>Very low</option>
+                                            <option value="-50" ${('', 'selected="selected"')[sickbeard.NZBGET_PRIORITY == -50]}>Low</option>
+                                            <option value="0" ${('', 'selected="selected"')[sickbeard.NZBGET_PRIORITY == 0]}>Normal</option>
+                                            <option value="50" ${('', 'selected="selected"')[sickbeard.NZBGET_PRIORITY == 50]}>High</option>
+                                            <option value="100" ${('', 'selected="selected"')[sickbeard.NZBGET_PRIORITY == 100]}>Very high</option>
+                                            <option value="900" ${('', 'selected="selected"')[sickbeard.NZBGET_PRIORITY == 900]}>Force</option>
                                         </select>
                                         <span>priority for daily snatches (no backlog)</span>
                                     </span>
@@ -435,7 +404,7 @@
                             <label for="use_torrents">
                                 <span class="component-title">Search torrents</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" name="use_torrents" class="enabler" id="use_torrents" <%= html_checked if sickbeard.USE_TORRENTS == True else '' %>/>
+                                    <input type="checkbox" name="use_torrents" class="enabler" id="use_torrents" ${('', 'checked="checked"')[bool(sickbeard.USE_TORRENTS)]}/>
                                     <p>enable torrent search providers</p>
                                 </span>
                             </label>
@@ -447,11 +416,10 @@
                                 <span class="component-title">Send .torrent files to:</span>
                                 <span class="component-desc">
                                 <select name="torrent_method" id="torrent_method" class="form-control input-sm">
-#set $torrent_method_text = {'blackhole': "Black hole", 'utorrent': "uTorrent", 'transmission': "Transmission", 'deluge': "Deluge", 'download_station': "Synology DS", 'rtorrent': "rTorrent", 'qbittorrent': "qbittorrent"}
-#for $curAction in ('blackhole', 'utorrent', 'transmission', 'deluge', 'download_station', 'rtorrent', 'qbittorrent'):
-    #set $selected = $html_selected if $sickbeard.TORRENT_METHOD == $curAction else ''
-                                <option value="$curAction"$selected>$torrent_method_text[$curAction]</option>
-#end for
+<% torrent_method_text = {'blackhole': "Black hole", 'utorrent': "uTorrent", 'transmission': "Transmission", 'deluge': "Deluge (via WebUI)", 'deluged': "Deluge (via Daemon)", 'download_station': "Synology DS", 'rtorrent': "rTorrent", 'qbittorrent': "qbittorrent"} %>
+% for curAction in ('blackhole', 'utorrent', 'transmission', 'deluge', 'deluged', 'download_station', 'rtorrent', 'qbittorrent'):
+                                <option value="${curAction}" ${('', 'selected="selected"')[sickbeard.TORRENT_METHOD == curAction]}>${torrent_method_text[curAction]}</option>
+% endfor
                                 </select>
                             </label>
 
@@ -460,7 +428,7 @@
                                 <label>
                                     <span class="component-title">Black hole folder location</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_dir" id="torrent_dir" value="$sickbeard.TORRENT_DIR" class="form-control input-sm input350" />
+                                        <input type="text" name="torrent_dir" id="torrent_dir" value="${sickbeard.TORRENT_DIR}" class="form-control input-sm input350" />
                                         <div class="clear-left"><p><b>.torrent</b> files are stored at this location for external software to find and use</p></div>
                                     </span>
                                 </label>
@@ -476,7 +444,7 @@
                                 <label>
                                     <span class="component-title" id="host_title">Torrent host:port</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_host" id="torrent_host" value="$sickbeard.TORRENT_HOST" class="form-control input-sm input350" />
+                                        <input type="text" name="torrent_host" id="torrent_host" value="${sickbeard.TORRENT_HOST}" class="form-control input-sm input350" />
                                         <div class="clear-left">
                                             <p id="host_desc_torrent">URL to your torrent client (e.g. http://localhost:8000/)</p>
                                         </div>
@@ -488,7 +456,7 @@
                                 <label>
                                     <span class="component-title" id="rpcurl_title">Torrent RPC URL</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_rpcurl" id="torrent_rpcurl" value="$sickbeard.TORRENT_RPCURL" class="form-control input-sm input350"/>
+                                        <input type="text" name="torrent_rpcurl" id="torrent_rpcurl" value="${sickbeard.TORRENT_RPCURL}" class="form-control input-sm input350"/>
                                         <div class="clear-left">
                                             <p id="rpcurl_desc_">The path without leading and trailing slashes (e.g. transmission)</p>
                                         </div>
@@ -501,11 +469,10 @@
                                     <span class="component-title">Http Authentication</span>
                                     <span class="component-desc">
                                         <select name="torrent_auth_type" id="torrent_auth_type" class="form-control input-sm">
-                                        #set $http_authtype = {'none': "None", 'basic': "Basic", 'digest': "Digest"}
-                                        #for $authvalue,$authname in $http_authtype.items():
-                                        #set $selected = $html_selected if $sickbeard.TORRENT_AUTH_TYPE == $authvalue else ''
-                                            <option id="torrent_auth_type_value" value="$authvalue"$selected>$authname</option>
-                                        #end for
+                                        <% http_authtype = {'none': "None", 'basic': "Basic", 'digest': "Digest"} %>
+                                        % for authvalue, authname in http_authtype.iteritems():
+                                            <option id="torrent_auth_type_value" value="${authvalue}" ${('', 'selected="selected"')[sickbeard.TORRENT_AUTH_TYPE == authvalue]}>${authname}</option>
+                                        % endfor
                                         </select>
                                         <p></p>
                                     </span>
@@ -516,7 +483,7 @@
                                 <label for="torrent_verify_cert">
                                     <span class="component-title">Verify certificate</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="torrent_verify_cert" class="enabler" id="torrent_verify_cert" <%= html_checked if sickbeard.TORRENT_VERIFY_CERT == True else '' %>/>
+                                        <input type="checkbox" name="torrent_verify_cert" class="enabler" id="torrent_verify_cert" ${('', 'checked="checked"')[bool(sickbeard.TORRENT_VERIFY_CERT)]}/>
                                         <p id="torrent_verify_deluge">disable if you get "Deluge: Authentication Error" in your log</p>
                                         <p id="torrent_verify_rtorrent">Verify SSL certificates for HTTPS requests</p>
                                     </span>
@@ -527,7 +494,7 @@
                                 <label>
                                     <span class="component-title" id="username_title">Client username</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_username" id="torrent_username" value="$sickbeard.TORRENT_USERNAME" class="form-control input-sm input200" />
+                                        <input type="text" name="torrent_username" id="torrent_username" value="${sickbeard.TORRENT_USERNAME}" class="form-control input-sm input200" />
                                         <p>(blank for none)</p>
                                     </span>
                                 </label>
@@ -537,7 +504,7 @@
                                 <label>
                                     <span class="component-title" id="password_title">Client password</span>
                                     <span class="component-desc">
-                                        <input type="password" name="torrent_password" id="torrent_password" value="$sickbeard.TORRENT_PASSWORD" class="form-control input-sm input200" />
+                                        <input type="password" name="torrent_password" id="torrent_password" value="${sickbeard.TORRENT_PASSWORD}" class="form-control input-sm input200" />
                                         <p>(blank for none)</p>
                                     </span>
                                 </label>
@@ -547,7 +514,7 @@
                                 <label>
                                     <span class="component-title">Add label to torrent</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_label" id="torrent_label" value="$sickbeard.TORRENT_LABEL" class="form-control input-sm input200" />
+                                        <input type="text" name="torrent_label" id="torrent_label" value="${sickbeard.TORRENT_LABEL}" class="form-control input-sm input200" />
                                         <span id="label_warning_deluge" style="display:none"><p>(blank spaces are not allowed)</p>
                                             <div class="clear-left"><p>note: label plugin must be enabled in Deluge clients</p></div>
                                         </span>
@@ -559,7 +526,7 @@
                                 <label>
                                     <span class="component-title">Add label to torrent for anime</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_label_anime" id="torrent_label_anime" value="$sickbeard.TORRENT_LABEL_ANIME" class="form-control input-sm input200" />
+                                        <input type="text" name="torrent_label_anime" id="torrent_label_anime" value="${sickbeard.TORRENT_LABEL_ANIME}" class="form-control input-sm input200" />
                                         <span id="label_anime_warning_deluge" style="display:none"><p>(blank spaces are not allowed)</p>
                                             <div class="clear-left"><p>note: label plugin must be enabled in Deluge clients</p></div>
                                         </span>
@@ -571,7 +538,7 @@
                                 <label>
                                     <span class="component-title" id="directory_title">Downloaded files location</span>
                                     <span class="component-desc">
-                                        <input type="text" name="torrent_path" id="torrent_path" value="$sickbeard.TORRENT_PATH" class="form-control input-sm input350" />
+                                        <input type="text" name="torrent_path" id="torrent_path" value="${sickbeard.TORRENT_PATH}" class="form-control input-sm input350" />
                                         <div class="clear-left"><p>where <span id="torrent_client">the torrent client</span> will save downloaded files (blank for client default)
                                             <span id="path_synology"> <b>note:</b> the destination has to be a shared folder for Synology DS</span></p>
                                         </div>
@@ -582,7 +549,7 @@
                             <div class="field-pair" id="torrent_seed_time_option">
                                 <label>
                                     <span class="component-title" id="torrent_seed_time_label">Minimum seeding time is</span>
-                                    <span class="component-desc"><input type="number" step="1" name="torrent_seed_time" id="torrent_seed_time" value="$sickbeard.TORRENT_SEED_TIME" class="form-control input-sm input100" />
+                                    <span class="component-desc"><input type="number" step="1" name="torrent_seed_time" id="torrent_seed_time" value="${sickbeard.TORRENT_SEED_TIME}" class="form-control input-sm input100" />
                                     <p>hours. (default:'0' passes blank to client and '-1' passes nothing)</p></span>
                                 </label>
                             </div>
@@ -591,7 +558,7 @@
                                 <label>
                                     <span class="component-title">Start torrent paused</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="torrent_paused" class="enabler" id="torrent_paused" <%= html_checked if sickbeard.TORRENT_PAUSED == True else '' %>/>
+                                        <input type="checkbox" name="torrent_paused" class="enabler" id="torrent_paused" ${('', 'checked="checked"')[bool(sickbeard.TORRENT_PAUSED)]}/>
                                         <p>add .torrent to client but do <b style="font-weight:900">not</b> start downloading</p>
                                     </span>
                                 </label>
@@ -601,7 +568,7 @@
                                 <label>
                                     <span class="component-title">Allow high bandwidth</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" name="torrent_high_bandwidth" class="enabler" id="torrent_high_bandwidth" <%= html_checked if sickbeard.TORRENT_HIGH_BANDWIDTH == True else '' %>/>
+                                        <input type="checkbox" name="torrent_high_bandwidth" class="enabler" id="torrent_high_bandwidth" ${('', 'checked="checked"')[bool(sickbeard.TORRENT_HIGH_BANDWIDTH)]}/>
                                         <p>use high bandwidth allocation if priority is high</p>
                                     </span>
                                 </label>
@@ -616,7 +583,7 @@
                 </div><!-- /component-group3 //-->
 
                 <br/>
-                <h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">$sickbeard.DATA_DIR</span></b> </h6>
+                <h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">${sickbeard.DATA_DIR}</span></b> </h6>
                 <input type="submit" class="btn pull-left config_submitter button" value="Save Changes" />
 
             </div><!-- /config-components //-->
@@ -628,13 +595,11 @@
 <div></div>
 
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#config-components').tabs();
     jQuery('#nzb_dir').fileBrowser({ title: 'Select .nzb black hole/watch location' });
     jQuery('#torrent_dir').fileBrowser({ title: 'Select .torrent black hole/watch location' });
     jQuery('#torrent_path').fileBrowser({ title: 'Select .torrent download location' });
     jQuery('#tv_download_dir').fileBrowser({ title: 'Select TV download location' });
-//-->
 </script>
 
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/config_subtitles.tmpl b/gui/slick/interfaces/default/config_subtitles.mako
similarity index 74%
rename from gui/slick/interfaces/default/config_subtitles.tmpl
rename to gui/slick/interfaces/default/config_subtitles.mako
index b04050dc90cf38c8113fc26e846bccd2857eeeaf..58243ba97b9f515b3ce66fdfe8be8a0e47e2f854 100644
--- a/gui/slick/interfaces/default/config_subtitles.tmpl
+++ b/gui/slick/interfaces/default/config_subtitles.mako
@@ -1,46 +1,32 @@
-#from sickbeard import subtitles
-#import sickbeard
-#from sickbeard.helpers import anon_url
-#set global $title="Config - Subtitles"
-#set global $header="Subtitles"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="config"
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/configSubtitles.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/config.js"></script>
-<script type="text/javascript" src="$sbRoot/js/lib/jquery.tokeninput.js"></script>
+<%!
+    from sickbeard import subtitles
+    import sickbeard
+    from sickbeard.helpers import anon_url
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/configSubtitles.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/config.js"></script>
+<script type="text/javascript" src="${sbRoot}/js/lib/jquery.tokeninput.js"></script>
 
 <script type="text/javascript">
-      \$(document).ready(function() {
-        \$("#subtitles_languages").tokenInput(
-                [
-                    <%=",\r\n".join("{id: \"" + lang.opensubtitles + "\", name: \"" + lang.name + "\"}" for lang in subtitles.subtitleLanguageFilter())%>
-                ],
+      $(document).ready(function() {
+        $("#subtitles_languages").tokenInput(
+                [${",\r\n".join("{id: \"" + lang.opensubtitles + "\", name: \"" + lang.name + "\"}" for lang in subtitles.subtitleLanguageFilter())}],
                 {
                     method: "POST",
                     hintText: "Write to search a language and select it",
                     preventDuplicates: true,
-                    prePopulate:
-
-                            [
-                                <%=
-                                        ",\r\n".join("{id: \"" + subtitles.fromietf(lang).opensubtitles + "\", name: \"" + subtitles.fromietf(lang).name + "\"}" for lang in subtitles.wantedLanguages()) if subtitles.wantedLanguages() else ''
-                                %>
-                            ]
+                    prePopulate: [${",\r\n".join("{id: \"" + subtitles.fromietf(lang).opensubtitles + "\", name: \"" + subtitles.fromietf(lang).name + "\"}" for lang in subtitles.wantedLanguages()) if subtitles.wantedLanguages() else ''}]
                 }
             );
     });
 </script>
 
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="config">
 <div id="config-content">
@@ -65,7 +51,7 @@
                             <label for="use_subtitles" class="clearfix">
                                 <span class="component-title">Search Subtitles</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" class="enabler" #if $sickbeard.USE_SUBTITLES then " checked=\"checked\"" else ""# id="use_subtitles" name="use_subtitles">
+                                    <input type="checkbox" class="enabler" ${("", " checked=\"checked\"")[bool(sickbeard.USE_SUBTITLES)]} id="use_subtitles" name="use_subtitles">
                                 </span>
                             </label>
                         </div>
@@ -79,7 +65,7 @@
                                 <div class="field-pair">
                                     <label>
                                         <span class="component-title">Subtitle Directory</span>
-                                        <input type="text" value="$sickbeard.SUBTITLES_DIR" id="subtitles_dir" name="subtitles_dir" class="form-control input-sm input350">
+                                        <input type="text" value="${sickbeard.SUBTITLES_DIR}" id="subtitles_dir" name="subtitles_dir" class="form-control input-sm input350">
                                     </label>
                                     <label>
                                             <span class="component-title">&nbsp;</span>
@@ -93,7 +79,7 @@
                                 <div class="field-pair">
                                     <label>
                                         <span class="component-title">Subtitle Find Frequency</span>
-                                        <input type="number" name="subtitles_finder_frequency" value="$sickbeard.SUBTITLES_FINDER_FREQUENCY" hours="1" class="form-control input-sm input75" />
+                                        <input type="number" name="subtitles_finder_frequency" value="${sickbeard.SUBTITLES_FINDER_FREQUENCY}" hours="1" class="form-control input-sm input75" />
                                         <span class="component-desc">time in hours between scans (default: 1)</span>
                                     </label>
                                 </div>
@@ -101,7 +87,7 @@
                                     <label class="clearfix" for="subtitles_history">
                                         <span class="component-title">Subtitles History</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="subtitles_history" id="subtitles_history" #if $sickbeard.SUBTITLES_HISTORY then " checked=\"checked\"" else ""#/>
+                                            <input type="checkbox" name="subtitles_history" id="subtitles_history" ${('', 'checked="checked"')[bool(sickbeard.SUBTITLES_HISTORY)]}/>
                                             <p>Log downloaded Subtitle on History page?</p>
                                         </span>
                                     </label>
@@ -110,7 +96,7 @@
                                     <label class="clearfix" for="subtitles_multi">
                                         <span class="component-title">Subtitles Multi-Language</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="subtitles_multi" id="subtitles_multi" #if $sickbeard.SUBTITLES_MULTI then " checked=\"checked\"" else ""#/>
+                                            <input type="checkbox" name="subtitles_multi" id="subtitles_multi" ${('', 'checked="checked"')[bool(sickbeard.SUBTITLES_MULTI)]}/>
                                             <p>Append language codes to subtitle filenames?</p>
                                         </span>
                                     </label>
@@ -119,7 +105,7 @@
                                     <label class="clearfix" for="embedded_subtitles_all">
                                         <span class="component-title">Embedded Subtitles</span>
                                         <span class="component-desc">
-                                            <input type="checkbox" name="embedded_subtitles_all" id="embedded_subtitles_all" #if $sickbeard.EMBEDDED_SUBTITLES_ALL then " checked=\"checked\"" else ""#/>
+                                            <input type="checkbox" name="embedded_subtitles_all" id="embedded_subtitles_all" ${('', 'checked="checked"')[bool(sickbeard.EMBEDDED_SUBTITLES_ALL)]}/>
                                             <p>Ignore subtitles embedded inside video file?</p>
                                             <p><b>Warning: </b>this will ignore <u>all</u> embedded subtitles for every video file!</p>
                                         </span>
@@ -128,7 +114,7 @@
                                 <div class="field-pair">
                                     <label class="nocheck">
                                         <span class="component-title">Extra Scripts</span>
-                                           <input type="text" name="subtitles_extra_scripts" value="<%='|'.join(sickbeard.SUBTITLES_EXTRA_SCRIPTS)%>" class="form-control input-sm input350" />
+                                           <input type="text" name="subtitles_extra_scripts" value="<%'|'.join(sickbeard.SUBTITLES_EXTRA_SCRIPTS)%>" class="form-control input-sm input350" />
                                     </label>
                                     <label class="nocheck">
                                         <span class="component-title">&nbsp;</span>
@@ -167,18 +153,18 @@
 
                     <fieldset class="component-group-list" style="margin-left: 50px; margin-top:36px">
                         <ul id="service_order_list">
-                        #for $curService in $sickbeard.subtitles.sortedServiceList():
-                            <li class="ui-state-default" id="$curService['name']">
-                                <input type="checkbox" id="enable_$curService['name']" class="service_enabler" #if $curService['enabled'] then "checked=\"checked\"" else ""#/>
-                                <a href="<%= anon_url(curService['url']) %>" class="imgLink" target="_new">
-                                    <img src="$sbRoot/images/subtitles/$curService.image" alt="$curService['url']" title="$curService['url']" width="16" height="16" style="vertical-align:middle;"/>
+                        % for curService in sickbeard.subtitles.sortedServiceList():
+                            <li class="ui-state-default" id="${curService['name']}">
+                                <input type="checkbox" id="enable_${curService['name']}" class="service_enabler" ${('', 'checked="checked"')[curService['enabled'] == True]}/>
+                                <a href="${anon_url(curService['url'])}" class="imgLink" target="_new">
+                                    <img src="${sbRoot}/images/subtitles/${curService['image']}" alt="${curService['url']}" title="${curService['url']}" width="16" height="16" style="vertical-align:middle;"/>
                                 </a>
-                            <span style="vertical-align:middle;">$curService['name'].capitalize()</span>
+                            <span style="vertical-align:middle;">${curService['name'].capitalize()}</span>
                             <span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="vertical-align:middle;"></span>
                           </li>
-                        #end for
+                        % endfor
                         </ul>
-                        <input type="hidden" name="service_order" id="service_order" value="<%=" ".join(['%s:%d' % (x['name'], x['enabled']) for x in sickbeard.subtitles.sortedServiceList()])%>"/>
+                        <input type="hidden" name="service_order" id="service_order" value="<%" ".join(['%s:%d' % (x['name'], x['enabled']) for x in sickbeard.subtitles.sortedServiceList()])%>"/>
 
                         <br/><input type="submit" class="btn config_submitter" value="Save Changes" /><br/>
                     </fieldset>
@@ -194,9 +180,7 @@
 
 <div class="clearfix"></div>
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#config-components').tabs();
     jQuery('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' });
-//-->
 </script>
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/displayShow.mako b/gui/slick/interfaces/default/displayShow.mako
new file mode 100644
index 0000000000000000000000000000000000000000..83345e35dc8aefe4e2085c9b5e4ea028f0c1e615
--- /dev/null
+++ b/gui/slick/interfaces/default/displayShow.mako
@@ -0,0 +1,625 @@
+<%!
+    import datetime
+    import urllib
+    import ntpath
+    import sickbeard
+    from sickbeard import subtitles, sbdatetime, network_timezones
+    import sickbeard.helpers
+
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings, Overview
+
+    from sickbeard.helpers import anon_url
+%>
+
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/lib/jquery.bookmarkscroll.js?${sbPID}"></script>
+
+<input type="hidden" id="sbRoot" value="${sbRoot}" />
+
+<script type="text/javascript" src="${sbRoot}/js/displayShow.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/sceneExceptionsTooltip.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/ratingTooltip.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/ajaxEpSearch.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/ajaxEpSubtitles.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/lib/jquery.collapser.min.js?${sbPID}"></script>
+<script type="text/javascript" charset="utf-8">
+$(document).ready(function(){
+    <% fuzzydate = 'airdate' %>
+    % if sickbeard.FUZZY_DATING:
+    fuzzyMoment({
+        containerClass : '.${fuzzydate}',
+        dateHasTime : false,
+        dateFormat : '${sickbeard.DATE_PRESET}',
+        timeFormat : '${sickbeard.TIME_PRESET}',
+        trimZero : ${("false", "true")[bool(sickbeard.TRIM_ZERO)]}
+    });
+    % endif
+    $('.addQTip').each(function () {
+        $(this).css({'cursor':'help', 'text-shadow':'0px 0px 0.5px #666'});
+        $(this).qtip({
+            show: {solo:true},
+            position: {viewport:$(window), my:'left center', adjust:{ y: -10, x: 2 }},
+            style: {tip:{corner:true, method:'polygon'}, classes:'qtip-rounded qtip-shadow ui-tooltip-sb'}
+        });
+    });
+    $.fn.generateStars = function() {
+        return this.each(function(i,e){$(e).html($('<span/>').width($(e).text()*12));});
+    };
+
+    $('.imdbstars').generateStars();
+
+    $("${('#showTable', '#animeTable')[bool(show.is_anime)]}").tablesorter({
+        widgets: ['saveSort', 'stickyHeaders', 'columnSelector'],
+        widgetOptions : {
+            columnSelector_saveColumns: true,
+            columnSelector_layout : '<br/><label><input type="checkbox">{name}</label>',
+            columnSelector_mediaquery: false,
+            columnSelector_cssChecked : 'checked'
+        },
+    });
+
+    $('#popover').popover({
+        placement: 'bottom',
+        html: true, // required if content has HTML
+        content: '<div id="popover-target"></div>'
+    })
+    // bootstrap popover event triggered when the popover opens
+    .on('shown.bs.popover', function () {
+        $.tablesorter.columnSelector.attachTo( $("${('#showTable', '#animeTable')[bool(show.is_anime)]}"), '#popover-target');
+    });
+});
+</script>
+    <div class="pull-left form-inline">
+        Change Show:
+        <div class="navShow"><img id="prevShow" src="${sbRoot}/images/prev.png" alt="&lt;&lt;" title="Prev Show" /></div>
+            <select id="pickShow" class="form-control form-control-inline input-sm">
+            % for curShowList in sortedShowLists:
+                <% curShowType = curShowList[0] %>
+                <% curShowList = curShowList[1] %>
+
+                % if len(sortedShowLists) > 1:
+                    <optgroup label="${curShowType}">
+                % endif
+                    % for curShow in curShowList:
+                    <option value="${curShow.indexerid}" ${('', 'selected="selected"')[curShow == show]}>${curShow.name}</option>
+                    % endfor
+                % if len(sortedShowLists) > 1:
+                    </optgroup>
+                % endif
+            % endfor
+            </select>
+        <div class="navShow"><img id="nextShow" src="${sbRoot}/images/next.png" alt="&gt;&gt;" title="Next Show" /></div>
+    </div>
+
+    <div class="clearfix"></div>
+
+    <div id="showtitle" data-showname="${show.name}">
+        <h1 class="title" id="scene_exception_${show.indexerid}">${show.name}</h1>
+    </div>
+
+    % if seasonResults:
+        ##There is a special/season_0?##
+        % if int(seasonResults[-1]["season"]) == 0:
+            <% season_special = 1 %>
+        % else:
+            <% season_special = 0 %>
+        % endif
+        % if not sickbeard.DISPLAY_SHOW_SPECIALS and season_special:
+            <% lastSeason = seasonResults.pop(-1) %>
+        % endif
+        <span class="h2footer displayspecials pull-right">
+            % if season_special:
+            Display Specials:
+                <a class="inner" href="${sbRoot}/toggleDisplayShowSpecials/?show=${show.indexerid}">${('Show', 'Hide')[bool(sickbeard.DISPLAY_SHOW_SPECIALS)]}</a>
+            % endif
+        </span>
+
+        <div class="h2footer pull-right">
+            <span>
+            % if (len(seasonResults) > 14):
+                <select id="seasonJump" class="form-control input-sm" style="position: relative; top: -4px;">
+                    <option value="jump">Jump to Season</option>
+                % for seasonNum in seasonResults:
+                    <option value="#season-${seasonNum["season"]}">${('Specials', 'Season ' + str(seasonNum["season"]))[int(seasonNum["season"]) == 0]}</option>
+                % endfor
+                </select>
+            % else:
+                Season:
+                % for seasonNum in seasonResults:
+                    % if int(seasonNum["season"]) == 0:
+                        <a href="#season-${seasonNum["season"]}">Specials</a>
+                    % else:
+                        <a href="#season-${seasonNum["season"]}">${str(seasonNum["season"])}</a>
+                    % endif
+                    % if seasonNum != seasonResults[-1]:
+                        <span class="separator">|</span>
+                    % endif
+                % endfor
+            % endif
+            </span>
+
+        % endif
+        </div>
+
+    <div class="clearfix"></div>
+
+% if show_message:
+    <div class="alert alert-info">
+        ${show_message}
+    </div>
+% endif
+
+    <div id="container">
+        <div id="posterCol">
+            <a href="${sbRoot}/showPoster/?show=${show.indexerid}&amp;which=poster" rel="dialog" title="View Poster for ${show.name}"><img src="${sbRoot}/showPoster/?show=${show.indexerid}&amp;which=poster_thumb" class="tvshowImg" alt=""/></a>
+        </div>
+
+        <div id="showCol">
+
+            <div id="showinfo">
+% if 'rating' in show.imdb_info:
+    <% rating_tip = str(show.imdb_info['rating']) + " / 10" + " Stars" + "<br />" + str(show.imdb_info['votes']) + " Votes" %>
+    <span class="imdbstars" qtip-content="${rating_tip}">${show.imdb_info['rating']}</span>
+% endif
+
+<% _show = show %>
+% if not show.imdbid:
+    <span>(${show.startyear}) - ${show.runtime} minutes - </span>
+% else:
+    % if 'country_codes' in show.imdb_info:
+        % for country in show.imdb_info['country_codes'].split('|'):
+                <img src="${sbRoot}/images/blank.png" class="country-flag flag-${country}" width="16" height="11" style="margin-left: 3px; vertical-align:middle;" />
+        % endfor
+    % endif
+    % if 'year' in show.imdb_info:
+                <span>(${show.imdb_info['year']}) - ${show.imdb_info['runtimes']} minutes - </span>
+    % endif
+                <a href="${anon_url('http://www.imdb.com/title/', _show.imdbid)}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;" title="http://www.imdb.com/title/${show.imdbid}"><img alt="[imdb]" height="16" width="16" src="${sbRoot}/images/imdb.png" style="margin-top: -1px; vertical-align:middle;"/></a>
+% endif
+                <a href="${anon_url(sickbeard.indexerApi(_show.indexer).config['show_url'], _show.indexerid)}" onclick="window.open(this.href, '_blank'); return false;" title="${sickbeard.indexerApi(show.indexer).config["show_url"] + str(show.indexerid)}"><img alt="${sickbeard.indexerApi(show.indexer).name}" height="16" width="16" src="${sbRoot}/images/${sickbeard.indexerApi(show.indexer).config["icon"]}" style="margin-top: -1px; vertical-align:middle;"/></a>
+% if xem_numbering or xem_absolute_numbering:
+                <a href="${anon_url('http://thexem.de/search?q=', _show.name)}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;" title="http://thexem.de/search?q-${show.name}"><img alt="[xem]" height="16" width="16" src="${sbRoot}/images/xem.png" style="margin-top: -1px; vertical-align:middle;"/></a>
+% endif
+            </div>
+
+            <div id="tags">
+                <ul class="tags">
+                    % if not show.imdbid and show.genre:
+                        % for genre in show.genre[1:-1].split('|'):
+                            <a href="${anon_url('http://trakt.tv/shows/popular/?genres=', genre.lower())}" target="_blank" title="View other popular ${genre} shows on trakt.tv."><li>${genre}</li></a>
+                        % endfor
+                    % endif
+                    % if 'year' in show.imdb_info:
+                        % for imdbgenre in show.imdb_info['genres'].replace('Sci-Fi','Science-Fiction').split('|'):
+                            <a href="${anon_url('http://trakt.tv/shows/popular/?genres=', imdbgenre.lower())}" target="_blank" title="View other popular ${imdbgenre} shows on trakt.tv."><li>${imdbgenre}</li></a>
+                        % endfor
+                    % endif
+                </ul>
+            </div>
+
+            <div id="summary">
+                <table class="summaryTable pull-left">
+                <% anyQualities, bestQualities = Quality.splitQuality(int(show.quality)) %>
+                    <tr><td class="showLegend">Quality: </td><td>
+                % if show.quality in qualityPresets:
+                    <span class="quality ${qualityPresetStrings[show.quality]}">${qualityPresetStrings[show.quality]}</span>
+                % else:
+                % if anyQualities:
+                    <i>Initial:</i> ${", ".join([Quality.qualityStrings[x] for x in sorted(anyQualities)])}${("", "</br>")[bool(bestQualities)]}
+                % endif
+                % if bestQualities:
+                    <i>Replace with:</i> ${", ".join([Quality.qualityStrings[x] for x in sorted(bestQualities)])}
+                % endif
+                % endif
+
+                % if show.network and show.airs:
+                    <tr><td class="showLegend">Originally Airs: </td><td>${show.airs} ${("<font color='#FF0000'><b>(invalid Timeformat)</b></font> ", "")[network_timezones.test_timeformat(show.airs)]} on ${show.network}</td></tr>
+                % elif show.network:
+                    <tr><td class="showLegend">Originally Airs: </td><td>${show.network}</td></tr>
+                % elif show.airs:
+                    <tr><td class="showLegend">Originally Airs: </td><td>${show.airs} ${("<font color='#FF0000'><b>(invalid Timeformat)</b></font>", "")[network_timezones.test_timeformat(show.airs)]}</td></tr>
+                % endif
+                    <tr><td class="showLegend">Show Status: </td><td>${show.status}</td></tr>
+                    <tr><td class="showLegend">Default EP Status: </td><td>${statusStrings[show.default_ep_status]}</td></tr>
+                % if showLoc[1]:
+                    <tr><td class="showLegend">Location: </td><td>${showLoc[0]}</td></tr>
+                % else:
+                    <tr><td class="showLegend"><span style="color: red;">Location: </span></td><td><span style="color: red;">${showLoc[0]}</span> (Missing)</td></tr>
+                % endif
+                    <tr><td class="showLegend">Scene Name:</td><td>${(show.name, " | ".join(show.exceptions))[show.exceptions != 0]}</td></tr>
+
+                % if show.rls_require_words:
+                    <tr><td class="showLegend">Required Words: </td><td>${show.rls_require_words}</td></tr>
+                % endif
+                % if show.rls_ignore_words:
+                    <tr><td class="showLegend">Ignored Words: </td><td>${show.rls_ignore_words}</td></tr>
+                % endif
+                % if bwl and bwl.whitelist:
+                    <tr>
+                        <td class="showLegend">Wanted Group${("", "s")[len(bwl.whitelist) > 1]}:</td>
+                        <td>${', '.join(bwl.whitelist)}</td>
+                    </tr>
+                % endif
+                % if bwl and bwl.blacklist:
+                    <tr>
+                        <td class="showLegend">Unwanted Group${("", "s")[len(bwl.blacklist) > 1]}:</td>
+                        <td>${', '.join(bwl.blacklist)}</td>
+                    </tr>
+                % endif
+
+                <tr><td class="showLegend">Size:</td><td>${sickbeard.helpers.pretty_filesize(sickbeard.helpers.get_size(showLoc[0]))}</td></tr>
+
+                </table>
+
+                <table style="width:180px; float: right; vertical-align: middle; height: 100%;">
+                    <% info_flag = subtitles.fromietf(show.lang).opensubtitles if show.lang else '' %>
+                    <tr><td class="showLegend">Info Language:</td><td><img src="${sbRoot}/images/subtitles/flags/${info_flag}.png" width="16" height="11" alt="${show.lang}" title="${show.lang}" onError="this.onerror=null;this.src='${sbRoot}/images/flags/unknown.png';"/></td></tr>
+                    % if sickbeard.USE_SUBTITLES:
+                    <tr><td class="showLegend">Subtitles: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.subtitles)]}" alt="${("N", "Y")[bool(show.subtitles)]}" width="16" height="16" /></td></tr>
+                    % endif
+                    <tr><td class="showLegend">Flat Folders: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.flatten_folders or sickbeard.NAMING_FORCE_FOLDERS)]}" alt=="${("N", "Y")[bool(show.flatten_folders or sickbeard.NAMING_FORCE_FOLDERS)]}" width="16" height="16" /></td></tr>
+                    <tr><td class="showLegend">Paused: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.paused)]}" alt="${("N", "Y")[bool(show.paused)]}" width="16" height="16" /></td></tr>
+                    <tr><td class="showLegend">Air-by-Date: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.air_by_date)]}" alt="${("N", "Y")[bool(show.air_by_date)]}" width="16" height="16" /></td></tr>
+                    <tr><td class="showLegend">Sports: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.is_sports)]}" alt="${("N", "Y")[bool(show.is_sports)]}" width="16" height="16" /></td></tr>
+                    <tr><td class="showLegend">Anime: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.is_anime)]}" alt="${("N", "Y")[bool(show.is_anime)]}" width="16" height="16" /></td></tr>
+                    <tr><td class="showLegend">DVD Order: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.dvdorder)]}" alt="${("N", "Y")[bool(show.dvdorder)]}" width="16" height="16" /></td></tr>
+                    <tr><td class="showLegend">Scene Numbering: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.scene)]}" alt="${("N", "Y")[bool(show.scene)]}" width="16" height="16" /></td></tr>
+                    % if anyQualities and bestQualities:
+                    <tr><td class="showLegend">Archive First Match: </td><td><img src="${sbRoot}/images/${("no16.png", "yes16.png")[bool(show.archive_firstmatch)]}" alt="${("N", "Y")[bool(show.archive_firstmatch)]}" width="16" height="16" /></td></tr>
+                    % endif
+                </table>
+            </div>
+        </div>
+    </div>
+
+    <div class="clearfix"></div>
+
+    <div class="pull-left" >
+        Change selected episodes to:</br>
+        <select id="statusSelect" class="form-control form-control-inline input-sm">
+        % for curStatus in [WANTED, SKIPPED, ARCHIVED, IGNORED, FAILED] + sorted(Quality.DOWNLOADED):
+            % if curStatus != DOWNLOADED:
+            <option value="${curStatus}">${statusStrings[curStatus]}</option>
+            % endif
+        % endfor
+        </select>
+        <input type="hidden" id="showID" value="${show.indexerid}" />
+        <input type="hidden" id="indexer" value="${show.indexer}" />
+        <input class="btn btn-inline" type="button" id="changeStatus" value="Go" />
+    </div>
+
+    </br>
+
+    <div class="pull-right clearfix" id="checkboxControls">
+        <div style="padding-bottom: 5px;">
+            <label for="wanted"><span class="wanted"><input type="checkbox" id="wanted"checked="checked" /> Wanted: <b>${epCounts[Overview.WANTED]}</b></span></label>
+            <label for="qual"><span class="qual"><input type="checkbox" id="qual"checked="checked" /> Low Quality: <b>${epCounts[Overview.QUAL]}</b></span></label>
+            <label for="good"><span class="good"><input type="checkbox" id="good"checked="checked" /> Downloaded: <b>${epCounts[Overview.GOOD]}</b></span></label>
+            <label for="skipped"><span class="skipped"><input type="checkbox" id="skipped"checked="checked" /> Skipped: <b>${epCounts[Overview.SKIPPED]}</b></span></label>
+            <label for="snatched"><span class="snatched"><input type="checkbox" id="snatched"checked="checked" /> Snatched: <b>${epCounts[Overview.SNATCHED]}</b></span></label>
+        </div>
+
+        <button id="popover" type="button" class="btn btn-xs">Select Columns</button>
+        <div class="pull-right" >
+            <button class="btn btn-xs seriesCheck">Select Filtered Episodes</button>
+            <button class="btn btn-xs clearAll">Clear All</button>
+        </div>
+    </div>
+<br />
+<br />
+<br />
+
+<table id="${("showTable", "animeTable")[bool(show.is_anime)]}" class="displayShowTable display_show" cellspacing="0" border="0" cellpadding="0">
+    <% curSeason = -1 %>
+    <% odd = 0 %>
+    % for epResult in sqlResults:
+        <%
+        epStr = str(epResult["season"]) + "x" + str(epResult["episode"])
+        if not epStr in epCats:
+            continue
+
+        if not sickbeard.DISPLAY_SHOW_SPECIALS and int(epResult["season"]) == 0:
+            continue
+
+        scene = False
+        scene_anime = False
+        if not show.air_by_date and not show.is_sports and not show.is_anime and show.is_scene:
+            scene = True
+        elif not show.air_by_date and not show.is_sports and show.is_anime and show.is_scene:
+            scene_anime = True
+
+        (dfltSeas, dfltEpis, dfltAbsolute) = (0, 0, 0)
+        if (epResult["season"], epResult["episode"]) in xem_numbering:
+            (dfltSeas, dfltEpis) = xem_numbering[(epResult["season"], epResult["episode"])]
+
+        if epResult["absolute_number"] in xem_absolute_numbering:
+            dfltAbsolute = xem_absolute_numbering[epResult["absolute_number"]]
+
+        if epResult["absolute_number"] in scene_absolute_numbering:
+            scAbsolute = scene_absolute_numbering[epResult["absolute_number"]]
+            dfltAbsNumbering = False
+        else:
+            scAbsolute = dfltAbsolute
+            dfltAbsNumbering = True
+
+        if (epResult["season"], epResult["episode"]) in scene_numbering:
+            (scSeas, scEpis) = scene_numbering[(epResult["season"], epResult["episode"])]
+            dfltEpNumbering = False
+        else:
+            (scSeas, scEpis) = (dfltSeas, dfltEpis)
+            dfltEpNumbering = True
+
+        epLoc = epResult["location"]
+        %>
+        % if int(epResult["season"]) != curSeason:
+            % if curSeason == -1:
+    <thead>
+        <tr class="seasoncols" style="display:none;">
+                <th data-sorter="false" data-priority="critical" class="col-checkbox"><input type="checkbox" class="seasonCheck"/></th>
+                <th data-sorter="false" class="col-metadata">NFO</th>
+                <th data-sorter="false" class="col-metadata">TBN</th>
+                <th data-sorter="false" class="col-ep">Episode</th>
+                <th data-sorter="false" ${("class=\"col-ep columnSelector-false\"", "class=\"col-ep\"")[bool(show.is_anime)]}>Absolute</th>
+                <th data-sorter="false" ${("class=\"col-ep columnSelector-false\"", "class=\"col-ep\"")[bool(scene)]}>Scene</th>
+                <th data-sorter="false" ${("class=\"col-ep columnSelector-false\"", "class=\"col-ep\"")[bool(scene_anime)]}>Scene Absolute</th>
+                <th data-sorter="false" class="col-name">Name</th>
+                <th data-sorter="false" class="col-name columnSelector-false">File Name</th>
+                <th data-sorter="false" class="col-ep columnSelector-false">Size</th>
+                <th data-sorter="false" class="col-airdate">Airdate</th>
+                <th data-sorter="false" ${("class=\"col-ep columnSelector-false\"", "class=\"col-ep\"")[bool(sickbeard.DOWNLOAD_URL)]}>Download</th>
+                <th data-sorter="false" ${("class=\"col-ep columnSelector-false\"", "class=\"col-ep\"")[bool(sickbeard.USE_SUBTITLES)]}>Subtitles</th>
+                <th data-sorter="false" class="col-status">Status</th>
+                <th data-sorter="false" class="col-search">Search</th>
+        </tr>
+    </thead>
+    <tbody class="tablesorter-no-sort">
+        <tr style="height: 60px;">
+            <th class="row-seasonheader displayShowTable" colspan="13" style="vertical-align: bottom; width: auto;">
+                <h3 style="display: inline;"><a name="season-${epResult["season"]}"></a>${("Specials", "Season " + str(epResult["season"]))[int(epResult["season"]) > 0]}</h3>
+                % if sickbeard.DISPLAY_ALL_SEASONS == False:
+                    <button id="showseason-${epResult['season']}" type="button" class="btn btn-xs pull-right" data-toggle="collapse" data-target="#collapseSeason-${epResult['season']}">Show Episodes</button>
+                    <script type="text/javascript">
+                    <!--
+                        $(function() {
+                            $('#collapseSeason-${epResult['season']}').on('hide.bs.collapse', function () {
+                                $('#showseason-${epResult['season']}').text('Show Episodes');
+                            })
+                            $('#collapseSeason-${epResult['season']}').on('show.bs.collapse', function () {
+                                $('#showseason-${epResult['season']}').text('Hide Episodes');
+                            })
+                        });
+                    //-->
+                    </script>
+                % endif
+            </th>
+        </tr>
+    </tbody>
+    <tbody class="tablesorter-no-sort">
+        <tr id="season-${epResult["season"]}-cols" class="seasoncols">
+            <th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="${epResult["season"]}" /></th>
+            <th class="col-metadata">NFO</th>
+            <th class="col-metadata">TBN</th>
+            <th class="col-ep">Episode</th>
+            <th class="col-ep">Absolute</th>
+            <th class="col-ep">Scene</th>
+            <th class="col-ep">Scene Absolute</th>
+            <th class="col-name">Name</th>
+            <th class="col-name">File Name</th>
+            <th class="col-ep">Size</th>
+            <th class="col-airdate">Airdate</th>
+            <th class="col-ep">Download</th>
+            <th class="col-ep">Subtitles</th>
+            <th class="col-status">Status</th>
+            <th class="col-search">Search</th>
+        </tr>
+            % else:
+    </tbody>
+    <tbody class="tablesorter-no-sort">
+        <tr style="height: 60px;">
+            <th class="row-seasonheader displayShowTable" colspan="13" style="vertical-align: bottom; width: auto;">
+                <h3 style="display: inline;"><a name="season-${epResult["season"]}"></a>${("Specials", "Season " + str(epResult["season"]))[bool(int(epResult["season"]))]}</h3>
+                % if sickbeard.DISPLAY_ALL_SEASONS == False:
+                    <button id="showseason-${epResult['season']}" type="button" class="btn btn-xs pull-right" data-toggle="collapse" data-target="#collapseSeason-${epResult['season']}">Show Episodes</button>
+                    <script type="text/javascript">
+                        $(function() {
+                            $('#collapseSeason-${epResult['season']}').on('hide.bs.collapse', function () {
+                                $('#showseason-${epResult['season']}').text('Show Episodes');
+                            })
+                            $('#collapseSeason-${epResult['season']}').on('show.bs.collapse', function () {
+                                $('#showseason-${epResult['season']}').text('Hide Episodes');
+                            })
+                        });
+                    </script>
+                % endif
+            </th>
+        </tr>
+    </tbody>
+    <tbody class="tablesorter-no-sort">
+        <tr id="season-${epResult["season"]}-cols" class="seasoncols">
+            <th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="${epResult["season"]}" /></th>
+            <th class="col-metadata">NFO</th>
+            <th class="col-metadata">TBN</th>
+            <th class="col-ep">Episode</th>
+            <th class="col-ep">Absolute</th>
+            <th class="col-ep">Scene</th>
+            <th class="col-ep">Scene Absolute</th>
+            <th class="col-name">Name</th>
+            <th class="col-name">File Name</th>
+            <th class="col-ep">Size</th>
+            <th class="col-airdate">Airdate</th>
+            <th class="col-ep">Download</th>
+            <th class="col-ep">Subtitles</th>
+            <th class="col-status">Status</th>
+            <th class="col-search">Search</th>
+        </tr>
+            % endif
+    </tbody>
+        % if sickbeard.DISPLAY_ALL_SEASONS == False:
+        <tbody class="collapse${("", " in")[curSeason == -1]}" id="collapseSeason-${epResult['season']}">
+        % else:
+        <tbody>
+        % endif
+        <% curSeason = int(epResult["season"]) %>
+        % endif
+        <% epLoc = str(epResult["location"]) %>
+        <tr class="${Overview.overviewStrings[epCats[epStr]]} season-${curSeason} seasonstyle">
+            <td class="col-checkbox">
+                % if int(epResult["status"]) != UNAIRED:
+                    <input type="checkbox" class="epCheck" id="${str(epResult["season"])+'x'+str(epResult["episode"])}" name="${str(epResult["season"]) +"x"+str(epResult["episode"])}" />
+                % endif
+            </td>
+            <td align="center"><img src="${sbRoot}/images/${("nfo-no.gif", "nfo.gif")[epResult["hasnfo"]]}" alt="${("N", "Y")[epResult["hasnfo"]]}" width="23" height="11" /></td>
+            <td align="center"><img src="${sbRoot}/images/${("tbn-no.gif", "tbn.gif")[epResult["hastbn"]]}" alt="${("N", "Y")[epResult["hastbn"]]}" width="23" height="11" /></td>
+            <td align="center">
+            <%
+                if epLoc and show._location and epLoc.lower().startswith(show._location.lower()):
+                    epLoc = epLoc[len(show._location)+1:]
+
+                if epLoc != '' and epLoc != None:
+                    text = '<span title="' + epLoc + '" class="addQTip">' + str(epResult['episode']) + "</span>"
+                else:
+                    text = str(epResult['episode'])
+            %>
+                ${text}
+            </td>
+            <td align="center">${epResult["absolute_number"]}</td>
+            <td align="center">
+                <input type="text" placeholder="${str(dfltSeas) + 'x' + str(dfltEpis)}" size="6" maxlength="8"
+                    class="sceneSeasonXEpisode form-control input-scene" data-for-season="${epResult["season"]}" data-for-episode="${epResult["episode"]}"
+                    id="sceneSeasonXEpisode_${show.indexerid}_${str(epResult["season"])}_${str(epResult["episode"])}"
+                    title="Change the value here if scene numbering differs from the indexer episode numbering"
+                    % if dfltEpNumbering:
+                        value=""
+                    % else:
+                        value="${str(scSeas)}x${str(scEpis)}"
+                    % endif
+                        style="padding: 0; text-align: center; max-width: 60px;" />
+            </td>
+            <td align="center">
+                <input type="text" placeholder="${str(dfltAbsolute)}" size="6" maxlength="8"
+                    class="sceneAbsolute form-control input-scene" data-for-absolute="${epResult["absolute_number"]}"
+                    id="sceneAbsolute_${show.indexerid}${"_"+str(epResult["absolute_number"])}"
+                    title="Change the value here if scene absolute numbering differs from the indexer absolute numbering"
+                    % if dfltAbsNumbering:
+                        value=""
+                    % else:
+                        value="${str(scAbsolute)}"
+                    % endif
+                        style="padding: 0; text-align: center; max-width: 60px;" />
+            </td>
+            <td class="col-name">
+            % if epResult["description"] != "" and epResult["description"] != None:
+                <img src="${sbRoot}/images/info32.png" width="16" height="16" class="plotInfo" alt="" id="plot_info_${str(show.indexerid)}_${str(epResult["season"])}_${str(epResult["episode"])}" />
+            % else:
+                <img src="${sbRoot}/images/info32.png" width="16" height="16" class="plotInfoNone" alt="" />
+            % endif
+            ${epResult["name"]}
+            </td>
+            <td class="col-name]">
+                % if epResult['location']:
+                    <%
+                    filename = epResult['location']
+                    for rootDir in sickbeard.ROOT_DIRS.split('|'):
+                        if not rootDir.startswith('/'):
+                            filename = filename.replace('\\','\\\\')
+
+                        filename = ntpath.basename(filename)
+                    %>
+                    ${filename}
+                % endif
+            </td>
+            <td class="col-ep">
+                % if epResult["file_size"]:
+                    <% file_size = sickbeard.helpers.pretty_filesize(epResult["file_size"]) %>
+                    ${file_size}
+                % endif
+            </td>
+            <td class="col-airdate">
+                <span class="${fuzzydate}">${(sbdatetime.sbdatetime.sbfdate(sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(epResult['airdate'], show.airs, show.network))), 'never')[int(epResult['airdate']) == 1]}</span>
+            </td>
+            <td>
+                % if sickbeard.DOWNLOAD_URL and epResult['location']:
+                    <%
+                        filename = epResult['location']
+                        for rootDir in sickbeard.ROOT_DIRS.split('|'):
+                            if rootDir.startswith('/'):
+                                filename = filename.replace(rootDir, "")
+                        filename = sickbeard.DOWNLOAD_URL + urllib.quote(filename.encode('utf8'))
+                    %>
+                    <center><a href="${filename}">Download</a></center>
+                % endif
+            </td>
+            <td class="col-subtitles" align="center">
+            % for sub_lang in [subtitles.fromietf(x) for x in epResult["subtitles"].split(',') if epResult["subtitles"]]:
+                <% flag = sub_lang.opensubtitles %>
+                <img src="${sbRoot}/images/subtitles/flags/${flag}.png" width="16" height="11" alt="${sub_lang.name}" onError="this.onerror=null;this.src='${sbRoot}/images/flags/unknown.png';" />
+            % endfor
+            </td>
+                <% curStatus, curQuality = Quality.splitCompositeStatus(int(epResult["status"])) %>
+                % if curQuality != Quality.NONE:
+                    <td class="col-status">${statusStrings[curStatus]} <span class="quality ${Quality.qualityStrings[curQuality].replace("720p","HD720p").replace("1080p","HD1080p").replace("HDTV", "HD720p")}">${Quality.qualityStrings[curQuality]}</span></td>
+                % else:
+                    <td class="col-status">${statusStrings[curStatus]}</td>
+                % endif
+            <td class="col-search">
+                % if int(epResult["season"]) != 0:
+                    % if ( int(epResult["status"]) in Quality.SNATCHED + Quality.DOWNLOADED ) and sickbeard.USE_FAILED_DOWNLOADS:
+                        <a class="epRetry" id="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" name="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" href="retryEpisode?show=${show.indexerid}&amp;season=${epResult["season"]}&amp;episode=${epResult["episode"]}"><img src="${sbRoot}/images/search16.png" height="16" alt="retry" title="Retry Download" /></a>
+                    % else:
+                        <a class="epSearch" id="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" name="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" href="searchEpisode?show=${show.indexerid}&amp;season=${epResult["season"]}&amp;episode=${epResult["episode"]}"><img src="${sbRoot}/images/search16.png" width="16" height="16" alt="search" title="Manual Search" /></a>
+                    % endif
+                % endif
+                % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"] and frozenset(subtitles.wantedLanguages()).difference(epResult["subtitles"].split(',')):
+                    <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=${show.indexerid}&amp;season=${epResult["season"]}&amp;episode=${epResult["episode"]}"><img src="${sbRoot}/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a>
+                % endif
+            </td>
+        </tr>
+    % endfor
+    </tbody>
+</table>
+
+<!--Begin - Bootstrap Modal-->
+
+<div id="manualSearchModalFailed" class="modal fade">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                <h4 class="modal-title">Manual Search</h4>
+            </div>
+            <div class="modal-body">
+                <p>Do you want to mark this episode as failed?</p>
+                <p class="text-warning"><small>The episode release name will be added to the failed history, preventing it to be downloaded again.</small></p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" data-dismiss="modal">No</button>
+                <button type="button" class="btn btn-success" data-dismiss="modal">Yes</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<div id="manualSearchModalQuality" class="modal fade">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                <h4 class="modal-title">Manual Search</h4>
+            </div>
+            <div class="modal-body">
+                <p>Do you want to include the current episode quality in the search?</p>
+                <p class="text-warning"><small>Choosing No will ignore any releases with the same episode quality as the one currently downloaded/snatched.</small></p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" data-dismiss="modal">No</button>
+                <button type="button" class="btn btn-success" data-dismiss="modal">Yes</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!--End - Bootstrap Modal-->
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/displayShow.tmpl b/gui/slick/interfaces/default/displayShow.tmpl
deleted file mode 100644
index b1928280774ba230b6b512a31efd69fc3d9fb04d..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/displayShow.tmpl
+++ /dev/null
@@ -1,662 +0,0 @@
-#import sickbeard
-#from sickbeard import subtitles, sbdatetime, network_timezones
-#import sickbeard.helpers
-#from sickbeard.common import *
-#from sickbeard.helpers import anon_url
-#import os.path, os
-#import datetime
-#import urllib
-#import ntpath
-
-#set global $title=$show.name
-##set global $header = '<a></a>' %
-#set global $topmenu="manageShows"#
-#set $exceptions_string = " | ".join($show.exceptions)
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/lib/jquery.bookmarkscroll.js?$sbPID"></script>
-
-
-<input type="hidden" id="sbRoot" value="$sbRoot" />
-
-<script type="text/javascript" src="$sbRoot/js/displayShow.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/sceneExceptionsTooltip.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/ratingTooltip.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/ajaxEpSubtitles.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/lib/jquery.collapser.min.js?$sbPID"></script>
-<script type="text/javascript" charset="utf-8">
-<!--
-\$(document).ready(function(){
-    #set $fuzzydate = 'airdate'
-    #if $sickbeard.FUZZY_DATING:
-    fuzzyMoment({
-        containerClass : '.${fuzzydate}',
-        dateHasTime : false,
-        dateFormat : '${sickbeard.DATE_PRESET}',
-        timeFormat : '${sickbeard.TIME_PRESET}',
-        trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#
-    });
-    #end if
-    #raw
-    $('.addQTip').each(function () {
-        $(this).css({'cursor':'help', 'text-shadow':'0px 0px 0.5px #666'});
-        $(this).qtip({
-            show: {solo:true},
-            position: {viewport:$(window), my:'left center', adjust:{ y: -10, x: 2 }},
-            style: {tip:{corner:true, method:'polygon'}, classes:'qtip-rounded qtip-shadow ui-tooltip-sb'}
-        });
-    });
-    #end raw
-
-    \$.fn.generateStars = function() {
-        return this.each(function(i,e){\$(e).html(\$('<span/>').width(\$(e).text()*12));});
-    };
-
-    \$('.imdbstars').generateStars();
-
-
-
-            #if $show.is_anime:
-    \$("#animeTable").tablesorter({
-            #else:
-    \$("#showTable").tablesorter({
-            #end if
-        widgets: ['saveSort', 'stickyHeaders', 'columnSelector'],
-        widgetOptions : {
-            columnSelector_saveColumns: true,
-            columnSelector_layout : '<br/><label><input type="checkbox">{name}</label>',
-            columnSelector_mediaquery: false,
-            columnSelector_cssChecked : 'checked'
-            },
-        });
-
-
-
-    \$('#popover')
-        .popover({
-          placement: 'bottom',
-          html: true, // required if content has HTML
-          content: '<div id="popover-target"></div>'
-        })
-        // bootstrap popover event triggered when the popover opens
-        .on('shown.bs.popover', function () {
-                #if $show.is_anime:
-            \$.tablesorter.columnSelector.attachTo( \$('#animeTable'), '#popover-target');
-                #else:
-            \$.tablesorter.columnSelector.attachTo( \$('#showTable'), '#popover-target');
-                #end if
-        });
-});
-//-->
-</script>
-
-    <div class="pull-left form-inline">
-        Change Show:
-        <div class="navShow"><img id="prevShow" src="$sbRoot/images/prev.png" alt="&lt;&lt;" title="Prev Show" /></div>
-            <select id="pickShow" class="form-control form-control-inline input-sm">
-            #for $curShowList in $sortedShowLists:
-                #set $curShowType = $curShowList[0]
-                #set $curShowList = $curShowList[1]
-
-                #if len($sortedShowLists) > 1:
-                    <optgroup label="$curShowType">
-                #end if
-                    #for $curShow in $curShowList:
-                    <option value="$curShow.indexerid" #if $curShow == $show then "selected=\"selected\"" else ""#>$curShow.name</option>
-                    #end for
-                #if len($sortedShowLists) > 1:
-                    </optgroup>
-                #end if
-            #end for
-            </select>
-        <div class="navShow"><img id="nextShow" src="$sbRoot/images/next.png" alt="&gt;&gt;" title="Next Show" /></div>
-    </div>
-
-    <div class="clearfix"></div>
-
-    <div id="showtitle" data-showname="$show.name">
-        <h1 class="title" id="scene_exception_$show.indexerid">$show.name</h1>
-    </div>
-
-
-        #if $seasonResults:
-        ##There is a special/season_0?##
-        #if int($seasonResults[-1]["season"]) == 0:
-            #set $season_special = 1
-        #else:
-            #set $season_special = 0
-        #end if
-
-        #if not $sickbeard.DISPLAY_SHOW_SPECIALS and $season_special:
-            #set lastSeason = $seasonResults.pop(-1); del lastSeason
-        #end if
-
-        <span class="h2footer displayspecials pull-right">
-            #if $season_special:
-            Display Specials:
-                #if sickbeard.DISPLAY_SHOW_SPECIALS:
-                    <a class="inner" href="$sbRoot/toggleDisplayShowSpecials/?show=$show.indexerid">Hide</a>
-                #else:
-                    <a class="inner" href="$sbRoot/toggleDisplayShowSpecials/?show=$show.indexerid">Show</a>
-                #end if
-            #end if
-        </span>
-
-        <div class="h2footer pull-right">
-            <span>
-            #if (len($seasonResults) > 14):
-                <select id="seasonJump" class="form-control input-sm" style="position: relative; top: -4px;">
-                    <option value="jump">Jump to Season</option>
-                #for $seasonNum in $seasonResults:
-                    <option value="#season-$seasonNum["season"]">#if int($seasonNum["season"]) == 0 then "Specials" else "Season " + str($seasonNum["season"])#</option>
-                #end for
-                </select>
-            #else:
-                Season:
-                #for $seasonNum in $seasonResults:
-                    #if int($seasonNum["season"]) == 0:
-                        <a href="#season-$seasonNum["season"]">Specials</a>
-                    #else:
-                        <a href="#season-$seasonNum["season"]">${str($seasonNum["season"])}</a>
-                    #end if
-                    #if $seasonNum != $seasonResults[-1]:
-                        <span class="separator">|</span>
-                    #end if
-                #end for
-            #end if
-            </span>
-
-        #end if
-        </div>
-
-    <div class="clearfix"></div>
-
-#if $show_message:
-    <div class="alert alert-info">
-        $show_message
-    </div>
-#end if
-
-    <div id="container">
-        <div id="posterCol">
-            <a href="$sbRoot/showPoster/?show=$show.indexerid&amp;which=poster" rel="dialog" title="View Poster for $show.name"><img src="$sbRoot/showPoster/?show=$show.indexerid&amp;which=poster_thumb" class="tvshowImg" alt=""/></a>
-        </div>
-
-        <div id="showCol">
-
-            <div id="showinfo">
-#if 'rating' in $show.imdb_info:
-    #set $rating_tip = str($show.imdb_info['rating']) + " / 10" + " Stars" + "<br />" + str($show.imdb_info['votes']) + " Votes"
-                <span class="imdbstars" qtip-content="$rating_tip">$show.imdb_info['rating']</span>
-#end if
-
-#set $_show = $show
-#if not $show.imdbid
-                <span>($show.startyear) - $show.runtime minutes - </span>
-#else
-    #if 'country_codes' in $show.imdb_info:
-        #for $country in $show.imdb_info['country_codes'].split('|')
-                <img src="$sbRoot/images/blank.png" class="country-flag flag-${$country}" width="16" height="11" style="margin-left: 3px; vertical-align:middle;" />
-        #end for
-    #end if
-    #if 'year' in $show.imdb_info:
-                <span>($show.imdb_info['year']) - $show.imdb_info['runtimes'] minutes - </span>
-    #end if
-                <a href="<%= anon_url('http://www.imdb.com/title/', _show.imdbid) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;" title="http://www.imdb.com/title/$show.imdbid"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" style="margin-top: -1px; vertical-align:middle;"/></a>
-#end if
-                <a href="<%= anon_url(sickbeard.indexerApi(_show.indexer).config['show_url'], _show.indexerid) %>" onclick="window.open(this.href, '_blank'); return false;" title="$sickbeard.indexerApi($show.indexer).config["show_url"]$show.indexerid"><img alt="$sickbeard.indexerApi($show.indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($show.indexer).config["icon"] "style="margin-top: -1px; vertical-align:middle;"/></a>
-#if $xem_numbering or $xem_absolute_numbering:
-                <a href="<%= anon_url('http://thexem.de/search?q=', _show.name) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;" title="http://thexem.de/search?q-$show.name"><img alt="[xem]" height="16" width="16" src="$sbRoot/images/xem.png" style="margin-top: -1px; vertical-align:middle;"/></a>
-#end if
-            </div>
-
-            <div id="tags">
-                <ul class="tags">
-                    #if not $show.imdbid
-                    #if $show.genre:
-                    #for $genre in $show.genre[1:-1].split('|')
-                        <a href="<%= anon_url('http://trakt.tv/shows/popular/?genres=', genre.lower()) %>" target="_blank" title="View other popular $genre shows on trakt.tv."><li>$genre</li></a>
-                    #end for
-                    #end if
-                    #end if
-                    #if 'year' in $show.imdb_info:
-                    #for $imdbgenre in $show.imdb_info['genres'].replace('Sci-Fi','Science-Fiction').split('|')
-                        <a href="<%= anon_url('http://trakt.tv/shows/popular/?genres=', imdbgenre.lower()) %>" target="_blank" title="View other popular $imdbgenre shows on trakt.tv."><li>$imdbgenre</li></a>
-                    #end for
-                    #end if
-                </ul>
-            </div>
-
-            <div id="summary">
-                <table class="summaryTable pull-left">
-                #set $anyQualities, $bestQualities = $Quality.splitQuality(int($show.quality))
-                    <tr><td class="showLegend">Quality: </td><td>
-                #if $show.quality in $qualityPresets:
-                    <span class="quality $qualityPresetStrings[$show.quality]">$qualityPresetStrings[$show.quality]</span>
-                #else:
-                #if $anyQualities:
-                    <i>Initial:</i> <%=", ".join([Quality.qualityStrings[x] for x in sorted(anyQualities)])%> #if $bestQualities then " </br> " else ""#
-                #end if
-                #if $bestQualities:
-                    <i>Replace with:</i> <%=", ".join([Quality.qualityStrings[x] for x in sorted(bestQualities)])%>
-                #end if
-                #end if
-
-                #if $show.network and $show.airs:
-                    <tr><td class="showLegend">Originally Airs: </td><td>$show.airs #if not $network_timezones.test_timeformat($show.airs) then " <font color='#FF0000'><b>(invalid Timeformat)</b></font> " else ""# on $show.network</td></tr>
-                #else if $show.network:
-                    <tr><td class="showLegend">Originally Airs: </td><td>$show.network</td></tr>
-                #else if $show.airs:
-                    <tr><td class="showLegend">Originally Airs: </td><td>>$show.airs #if not $network_timezones.test_timeformat($show.airs) then " <font color='#FF0000'><b>(invalid Timeformat)</b></font> " else ""#</td></tr>
-                #end if
-                    <tr><td class="showLegend">Show Status: </td><td>$show.status</td></tr>
-                    <tr><td class="showLegend">Default EP Status: </td><td>$statusStrings[$show.default_ep_status]</td></tr>
-                #if $showLoc[1]:
-                    <tr><td class="showLegend">Location: </td><td>$showLoc[0]</td></tr>
-                #else:
-                    <tr><td class="showLegend"><span style="color: red;">Location: </span></td><td><span style="color: red;">$showLoc[0]</span> (dir is missing)</td></tr>
-                #end if
-                    <tr><td class="showLegend">Scene Name:</td><td>#if $show.exceptions then $exceptions_string else $show.name#</td></tr>
-
-                #if $show.rls_require_words:
-                    <tr><td class="showLegend">Required Words: </td><td>#echo $show.rls_require_words#</td></tr>
-                #end if
-                #if $show.rls_ignore_words:
-                    <tr><td class="showLegend">Ignored Words: </td><td>#echo $show.rls_ignore_words#</td></tr>
-                #end if
-                #if $bwl and $bwl.whitelist:
-                    <tr>
-                        <td class="showLegend">Wanted Group#if len($bwl.whitelist)>1 then "s" else ""#:</td>
-                        <td>#echo ', '.join($bwl.whitelist)#</td>
-                    </tr>
-                #end if
-                #if $bwl and $bwl.blacklist:
-                    <tr>
-                        <td class="showLegend">Unwanted Group#if len($bwl.blacklist)>1 then "s" else ""#:</td>
-                        <td>#echo ', '.join($bwl.blacklist)#</td>
-                    </tr>
-                #end if
-
-                <tr><td class="showLegend">Size:</td><td>$sickbeard.helpers.pretty_filesize(sickbeard.helpers.get_size($showLoc[0]))</td></tr>
-
-                </table>
-
-                <table style="width:180px; float: right; vertical-align: middle; height: 100%;">
-                    #set $info_flag = $subtitles.fromietf($show.lang).opensubtitles if $show.lang else ''
-                    <tr><td class="showLegend">Info Language:</td><td><img src="$sbRoot/images/subtitles/flags/${info_flag}.png" width="16" height="11" alt="$show.lang" title="$show.lang" onError="this.onerror=null;this.src='$sbRoot/images/flags/unknown.png';"/></td></tr>
-                    #if $sickbeard.USE_SUBTITLES
-                    <tr><td class="showLegend">Subtitles: </td><td><img src="$sbRoot/images/#if $show.subtitles then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    #end if
-                    <tr><td class="showLegend">Flat Folders: </td><td><img src="$sbRoot/images/#if $show.flatten_folders == 1 or $sickbeard.NAMING_FORCE_FOLDERS then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    <tr><td class="showLegend">Paused: </td><td><img src="$sbRoot/images/#if int($show.paused) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    <tr><td class="showLegend">Air-by-Date: </td><td><img src="$sbRoot/images/#if int($show.air_by_date) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    <tr><td class="showLegend">Sports: </td><td><img src="$sbRoot/images/#if int($show.is_sports) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    <tr><td class="showLegend">Anime: </td><td><img src="$sbRoot/images/#if int($show.is_anime) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    <tr><td class="showLegend">DVD Order: </td><td><img src="$sbRoot/images/#if int($show.dvdorder) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    <tr><td class="showLegend">Scene Numbering: </td><td><img src="$sbRoot/images/#if int($show.scene) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    #if $anyQualities + $bestQualities
-                    <tr><td class="showLegend">Archive First Match: </td><td><img src="$sbRoot/images/#if int($show.archive_firstmatch) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
-                    #end if
-                </table>
-            </div>
-        </div>
-    </div>
-
-    <div class="clearfix"></div>
-
-    <div class="pull-left" >
-        Change selected episodes to:</br>
-        <select id="statusSelect" class="form-control form-control-inline input-sm">
-            #for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED, $FAILED] + sorted($Quality.DOWNLOADED):
-            #if $curStatus == $DOWNLOADED:
-            #continue
-            #end if
-            <option value="$curStatus">$statusStrings[$curStatus]</option>
-            #end for
-        </select>
-        <input type="hidden" id="showID" value="$show.indexerid" />
-        <input type="hidden" id="indexer" value="$show.indexer" />
-        <input class="btn btn-inline" type="button" id="changeStatus" value="Go" />
-    </div>
-
-    </br>
-
-    <div class="pull-right clearfix" id="checkboxControls">
-        <div style="padding-bottom: 5px;">
-            <label for="wanted"><span class="wanted"><input type="checkbox" id="wanted" checked="checked" /> Wanted: <b>$epCounts[$Overview.WANTED]</b></span></label>
-            <label for="qual"><span class="qual"><input type="checkbox" id="qual" checked="checked" /> Low Quality: <b>$epCounts[$Overview.QUAL]</b></span></label>
-            <label for="good"><span class="good"><input type="checkbox" id="good" checked="checked" /> Downloaded: <b>$epCounts[$Overview.GOOD]</b></span></label>
-            <label for="skipped"><span class="skipped"><input type="checkbox" id="skipped" checked="checked" /> Skipped: <b>$epCounts[$Overview.SKIPPED]</b></span></label>
-            <label for="snatched"><span class="snatched"><input type="checkbox" id="snatched" checked="checked" /> Snatched: <b>$epCounts[$Overview.SNATCHED]</b></span></label>
-        </div>
-
-        <button id="popover" type="button" class="btn btn-xs">Select Columns</button>
-        <div class="pull-right" >
-            <button class="btn btn-xs seriesCheck">Select Filtered Episodes</button>
-            <button class="btn btn-xs clearAll">Clear All</button>
-        </div>
-    </div>
-<br />
-<br />
-<br />
-
-<table #if not $show.is_anime then "id=\"showTable\"" else "id=\"animeTable\""# class="displayShowTable display_show" cellspacing="0" border="0" cellpadding="0">
-#set $curSeason = -1
-#set $odd = 0
-    #for $epResult in $sqlResults:
-        #set $epStr = str($epResult["season"]) + "x" + str($epResult["episode"])
-        #if not $epStr in $epCats:
-            #continue
-        #end if
-        #if not $sickbeard.DISPLAY_SHOW_SPECIALS and int($epResult["season"]) == 0:
-            #continue
-        #end if
-        #set $scene = False
-        #set $scene_anime = False
-        #if not $show.air_by_date and not $show.is_sports and not $show.is_anime and $show.is_scene:
-            #set $scene = True
-        #elif not $show.air_by_date and not $show.is_sports and $show.is_anime and $show.is_scene:
-            #set $scene_anime = True
-        #end if
-        #set ($dfltSeas, $dfltEpis, $dfltAbsolute) = (0, 0, 0)
-        #if (epResult["season"], epResult["episode"]) in $xem_numbering:
-            #set ($dfltSeas, $dfltEpis) = $xem_numbering[(epResult["season"], epResult["episode"])]
-        #end if
-        #if epResult["absolute_number"] in $xem_absolute_numbering:
-            #set $dfltAbsolute = $xem_absolute_numbering[epResult["absolute_number"]]
-        #end if
-
-        #if epResult["absolute_number"] in $scene_absolute_numbering:
-            #set $scAbsolute = $scene_absolute_numbering[epResult["absolute_number"]]
-            #set $dfltAbsNumbering = False
-        #else
-            #set $scAbsolute = $dfltAbsolute
-            #set $dfltAbsNumbering = True
-        #end if
-
-        #if (epResult["season"], epResult["episode"]) in $scene_numbering:
-            #set ($scSeas, $scEpis) = $scene_numbering[(epResult["season"], epResult["episode"])]
-            #set $dfltEpNumbering = False
-        #else
-            #set ($scSeas, $scEpis) = ($dfltSeas, $dfltEpis)
-            #set $dfltEpNumbering = True
-        #end if
-
-        #set $epLoc = $epResult["location"]
-
-        #if int($epResult["season"]) != $curSeason:
-            #if $curSeason == -1:
-    <thead>
-        <tr class="seasoncols" style="display:none;">
-                <th data-sorter="false" data-priority="critical" class="col-checkbox"><input type="checkbox" class="seasonCheck"/></th>
-                <th data-sorter="false" class="col-metadata">NFO</th>
-                <th data-sorter="false" class="col-metadata">TBN</th>
-                <th data-sorter="false" class="col-ep">Episode</th>
-                <th data-sorter="false" #if not $show.is_anime then "class=\"col-ep columnSelector-false\"" else "class=\"col-ep\""#>Absolute</th>
-                <th data-sorter="false" #if not $scene then "class=\"col-ep columnSelector-false\"" else "class=\"col-ep\""#>Scene</th>
-                <th data-sorter="false" #if not $scene_anime then "class=\"col-ep columnSelector-false\"" else "class=\"col-ep\""#>Scene Absolute</th>
-                <th data-sorter="false" class="col-name">Name</th>
-                <th data-sorter="false" class="col-name columnSelector-false">File Name</th>
-                <th data-sorter="false" class="col-ep columnSelector-false">Size</th>
-                <th data-sorter="false" class="col-airdate">Airdate</th>
-                <th data-sorter="false" #if not $sickbeard.DOWNLOAD_URL then "class=\"col-ep columnSelector-false\"" else "class=\"col-ep\""#>Download</th>
-                <th data-sorter="false" #if not $sickbeard.USE_SUBTITLES then "class=\"col-ep columnSelector-false\"" else "class=\"col-ep\""#>Subtitles</th>
-                <th data-sorter="false" class="col-status">Status</th>
-                <th data-sorter="false" class="col-search">Search</th>
-        </tr>
-    </thead>
-    <tbody class="tablesorter-no-sort">
-        <tr style="height: 60px;">
-            <th class="row-seasonheader displayShowTable" colspan="13" style="vertical-align: bottom; width: auto;">
-                <h3 style="display: inline;"><a name="season-$epResult["season"]"></a>#if int($epResult["season"]) == 0 then "Specials" else "Season " + str($epResult["season"])#</h3>
-                #if $sickbeard.DISPLAY_ALL_SEASONS == False:
-                    <button id="showseason-$epResult['season']" type="button" class="btn btn-xs pull-right" data-toggle="collapse" data-target="#collapseSeason-$epResult['season']">Show Episodes</button>
-                    <script type="text/javascript">
-                    <!--
-                        \$(function() {
-                            \$('#collapseSeason-$epResult['season']').on('hide.bs.collapse', function () {
-                                \$('#showseason-$epResult['season']').text('Show Episodes');
-                            })
-                            \$('#collapseSeason-$epResult['season']').on('show.bs.collapse', function () {
-                                \$('#showseason-$epResult['season']').text('Hide Episodes');
-                            })
-                        });
-                    //-->
-                    </script>
-                #end if
-            </th>
-        </tr>
-    </tbody>
-    <tbody class="tablesorter-no-sort">
-        <tr id="season-$epResult["season"]-cols" class="seasoncols">
-            <th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="$epResult["season"]" /></th>
-            <th class="col-metadata">NFO</th>
-            <th class="col-metadata">TBN</th>
-            <th class="col-ep">Episode</th>
-            <th class="col-ep">Absolute</th>
-            <th class="col-ep">Scene</th>
-            <th class="col-ep">Scene Absolute</th>
-            <th class="col-name">Name</th>
-            <th class="col-name">File Name</th>
-            <th class="col-ep">Size</th>
-            <th class="col-airdate">Airdate</th>
-            <th class="col-ep">Download</th>
-            <th class="col-ep">Subtitles</th>
-            <th class="col-status">Status</th>
-            <th class="col-search">Search</th>
-        </tr>
-            #else:
-    </tbody>
-    <tbody class="tablesorter-no-sort">
-        <tr style="height: 60px;">
-            <th class="row-seasonheader displayShowTable" colspan="13" style="vertical-align: bottom; width: auto;">
-                <h3 style="display: inline;"><a name="season-$epResult["season"]"></a>#if int($epResult["season"]) == 0 then "Specials" else "Season " + str($epResult["season"])#</h3>
-                #if $sickbeard.DISPLAY_ALL_SEASONS == False:
-                    <button id="showseason-$epResult['season']" type="button" class="btn btn-xs pull-right" data-toggle="collapse" data-target="#collapseSeason-$epResult['season']">Show Episodes</button>
-                    <script type="text/javascript">
-                    <!--
-                        \$(function() {
-                            \$('#collapseSeason-$epResult['season']').on('hide.bs.collapse', function () {
-                                \$('#showseason-$epResult['season']').text('Show Episodes');
-                            })
-                            \$('#collapseSeason-$epResult['season']').on('show.bs.collapse', function () {
-                                \$('#showseason-$epResult['season']').text('Hide Episodes');
-                            })
-                        });
-                    //-->
-                    </script>
-                #end if
-            </th>
-        </tr>
-    </tbody>
-    <tbody class="tablesorter-no-sort">
-        <tr id="season-$epResult["season"]-cols" class="seasoncols">
-            <th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="$epResult["season"]" /></th>
-            <th class="col-metadata">NFO</th>
-            <th class="col-metadata">TBN</th>
-            <th class="col-ep">Episode</th>
-            <th class="col-ep">Absolute</th>
-            <th class="col-ep">Scene</th>
-            <th class="col-ep">Scene Absolute</th>
-            <th class="col-name">Name</th>
-            <th class="col-name">File Name</th>
-            <th class="col-ep">Size</th>
-            <th class="col-airdate">Airdate</th>
-            <th class="col-ep">Download</th>
-            <th class="col-ep">Subtitles</th>
-            <th class="col-status">Status</th>
-            <th class="col-search">Search</th>
-        </tr>
-            #end if
-    </tbody>
-                #if $sickbeard.DISPLAY_ALL_SEASONS == False:
-    <tbody class="collapse#if $curSeason == -1 then ' in' else ''#" id="collapseSeason-$epResult['season']">
-                #else
-    <tbody>
-                #end if
-            #set $curSeason = int($epResult["season"])
-        #end if
-        #set $epLoc = $epResult["location"]
-        <tr class="$Overview.overviewStrings[$epCats[$epStr]] season-$curSeason seasonstyle">
-            <td class="col-checkbox">
-                #if int($epResult["status"]) != $UNAIRED
-                    <input type="checkbox" class="epCheck" id="<%=str(epResult["season"])+'x'+str(epResult["episode"])%>" name="<%=str(epResult["season"]) +"x"+str(epResult["episode"]) %>" />
-                #end if
-            </td>
-            <td align="center"><img src="$sbRoot/images/#if $epResult["hasnfo"] == 1 then "nfo.gif\" alt=\"Y" else "nfo-no.gif\" alt=\"N"#" width="23" height="11" /></td>
-            <td align="center"><img src="$sbRoot/images/#if $epResult["hastbn"] == 1 then "tbn.gif\" alt=\"Y" else "tbn-no.gif\" alt=\"N"#" width="23" height="11" /></td>
-            <td align="center">
-                #if $epLoc and $show._location and $epLoc.lower().startswith($show._location.lower()):
-                    #set $epLoc = $epLoc[len($show._location)+1:]
-                #elif $epLoc and (not $epLoc.lower().startswith($show._location.lower()) or not $show._location):
-                    #set $epLoc = $epLoc
-                #end if
-
-                #if $epLoc != "" and $epLoc != None:
-                    <span title="$epLoc" class="addQTip">$epResult["episode"]</span>
-                #else
-                    $epResult["episode"]
-                #end if
-            </td>
-            <td align="center">$epResult["absolute_number"]</td>
-            <td align="center">
-                <input type="text" placeholder="<%=str(dfltSeas) + 'x' + str(dfltEpis)%>" size="6" maxlength="8"
-                    class="sceneSeasonXEpisode form-control input-scene" data-for-season="$epResult["season"]" data-for-episode="$epResult["episode"]"
-                    id="sceneSeasonXEpisode_$show.indexerid<%="_"+str(epResult["season"])+"_"+str(epResult["episode"])%>"
-                    title="Change the value here if scene numbering differs from the indexer episode numbering"
-                    #if $dfltEpNumbering:
-                        value=""
-                    #else
-                        value="<%=str(scSeas) + 'x' + str(scEpis)%>"
-                    #end if
-                        style="padding: 0; text-align: center; max-width: 60px;" />
-            </td>
-            <td align="center">
-                <input type="text" placeholder="<%=str(dfltAbsolute)%>" size="6" maxlength="8"
-                    class="sceneAbsolute form-control input-scene" data-for-absolute="$epResult["absolute_number"]"
-                    id="sceneAbsolute_$show.indexerid<%="_"+str(epResult["absolute_number"])%>"
-                    title="Change the value here if scene absolute numbering differs from the indexer absolute numbering"
-                    #if $dfltAbsNumbering:
-                        value=""
-                    #else
-                        value="<%=str(scAbsolute)%>"
-                    #end if
-                        style="padding: 0; text-align: center; max-width: 60px;" />
-            </td>
-            <td class="col-name">
-            #if $epResult["description"] != "" and $epResult["description"] != None:
-                <img src="$sbRoot/images/info32.png" width="16" height="16" class="plotInfo" alt="" id="plot_info_$show.indexerid<%="_" + str(epResult["season"]) + "_" + str(epResult["episode"])%>" />
-            #else:
-                <img src="$sbRoot/images/info32.png" width="16" height="16" class="plotInfoNone" alt="" />
-            #end if
-            $epResult["name"]
-            </td>
-            <td class="col-name]">
-                #if $epResult['location']
-                    #set $filename = $epResult['location']
-                    #for $rootDir in $sickbeard.ROOT_DIRS.split('|')
-                        #if not $rootDir.startswith('/')
-                            #set $filename = $filename.replace('\\','\\\\')
-                        #end if
-                        #set $filename = ntpath.basename($filename)
-                    #end for
-                    $filename
-                #end if
-            </td>
-            <td class="col-ep">
-                #if $epResult["file_size"]:
-                    #set $file_size = $sickbeard.helpers.pretty_filesize($epResult["file_size"])
-                    $file_size
-                #end if
-            </td>
-            <td class="col-airdate">
-                <span class="${fuzzydate}">#if int($epResult['airdate']) == 1 then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($epResult['airdate'],$show.airs,$show.network)))#</span>
-            </td>
-            <td>
-                #if $sickbeard.DOWNLOAD_URL and $epResult['location']
-                    #if $epResult['location']
-                        #set $filename = $epResult['location']
-                        #for $rootDir in $sickbeard.ROOT_DIRS.split('|')
-                            #if $rootDir.startswith('/')
-                                #set $filename = $filename.replace($rootDir, "")
-                            #end if
-                        #end for
-                        #set $filename = $sickbeard.DOWNLOAD_URL + $urllib.quote($filename.encode('utf8'))
-                <center><a href="$filename">Download</a></center>
-                    #end if
-                #end if
-            </td>
-            <td class="col-subtitles" align="center">
-            #for $sub_lang in [$subtitles.fromietf(x) for x in $epResult["subtitles"].split(',') if $epResult["subtitles"]]:
-                #set $flag = $sub_lang.opensubtitles
-                <img src="$sbRoot/images/subtitles/flags/${flag}.png" width="16" height="11" alt="${sub_lang.name}" onError="this.onerror=null;this.src='$sbRoot/images/flags/unknown.png';" />
-            #end for
-            </td>
-            #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($epResult["status"]))
-                #if $curQuality != Quality.NONE:
-                    <td class="col-status">$statusStrings[$curStatus] <span class="quality $Quality.qualityStrings[$curQuality].replace("720p","HD720p").replace("1080p","HD1080p").replace("HDTV", "HD720p")">$Quality.qualityStrings[$curQuality]</span></td>
-                #else:
-                    <td class="col-status">$statusStrings[$curStatus]</td>
-                #end if
-            <td class="col-search">
-                #if int($epResult["season"]) != 0:
-                    #if ( int($epResult["status"]) in $Quality.SNATCHED or int($epResult["status"]) in $Quality.DOWNLOADED ) and $sickbeard.USE_FAILED_DOWNLOADS:
-                        <a class="epRetry" id="#echo $str($show.indexerid)+'x'+$str(epResult["season"])+'x'+$str(epResult["episode"])#" name="#echo $str($show.indexerid)+'x'+$str(epResult["season"])+'x'+$str(epResult["episode"])#" href="retryEpisode?show=$show.indexerid&amp;season=$epResult["season"]&amp;episode=$epResult["episode"]"><img src="$sbRoot/images/search16.png" height="16" alt="retry" title="Retry Download" /></a>
-                    #else:
-                        <a class="epSearch" id="#echo $str($show.indexerid)+'x'+$str(epResult["season"])+'x'+$str(epResult["episode"])#" name="#echo $str($show.indexerid)+'x'+$str(epResult["season"])+'x'+$str(epResult["episode"])#" href="searchEpisode?show=$show.indexerid&amp;season=$epResult["season"]&amp;episode=$epResult["episode"]"><img src="$sbRoot/images/search16.png" width="16" height="16" alt="search" title="Manual Search" /></a>
-                    #end if
-                #end if
-                #if $sickbeard.USE_SUBTITLES and $show.subtitles and $epResult["location"] and frozenset($subtitles.wantedLanguages()).difference($epResult["subtitles"].split(',')):
-                    <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=$show.indexerid&amp;season=$epResult["season"]&amp;episode=$epResult["episode"]"><img src="$sbRoot/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a>
-                #end if
-            </td>
-        </tr>
-    #end for
-    </tbody>
-</table>
-
-<!--Begin - Bootstrap Modal-->
-
-<div id="manualSearchModalFailed" class="modal fade">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-                <h4 class="modal-title">Manual Search</h4>
-            </div>
-            <div class="modal-body">
-                <p>Do you want to mark this episode as failed?</p>
-                <p class="text-warning"><small>The episode release name will be added to the failed history, preventing it to be downloaded again.</small></p>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-danger" data-dismiss="modal">No</button>
-                <button type="button" class="btn btn-success" data-dismiss="modal">Yes</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div id="manualSearchModalQuality" class="modal fade">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-                <h4 class="modal-title">Manual Search</h4>
-            </div>
-            <div class="modal-body">
-                <p>Do you want to include the current episode quality in the search?</p>
-                <p class="text-warning"><small>Choosing No will ignore any releases with the same episode quality as the one currently downloaded/snatched.</small></p>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-danger" data-dismiss="modal">No</button>
-                <button type="button" class="btn btn-success" data-dismiss="modal">Yes</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<!--End - Bootstrap Modal-->
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/editShow.mako b/gui/slick/interfaces/default/editShow.mako
new file mode 100644
index 0000000000000000000000000000000000000000..6c00f1d9dddcd982dc0c9150cc776e7c3c23498b
--- /dev/null
+++ b/gui/slick/interfaces/default/editShow.mako
@@ -0,0 +1,197 @@
+<%!
+    import sickbeard
+    import adba
+    from sickbeard import common
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import statusStrings
+    from sickbeard import exceptions
+    from sickbeard import scene_exceptions
+%>
+<%include file="/inc_top.mako"/>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div id="editShow">
+<script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script>
+
+<form action="editShow" method="post">
+<input type="hidden" name="show" value="${show.indexerid}" />
+<b>Location:</b></br>
+<input type="text" name="location" id="location" value="${show._location}" class="form-control form-control-inline input-sm input350" /><br />
+<br />
+
+<b>Scene Exception:</b><br />
+<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
+<input class="btn btn-inline" type="button" value="Add" id="addSceneName"><br />
+This will <b>affect the episode show search</b> on nzb and torrent provider.<br />
+        This list overrides the original name, it doesn't append to it.<br />
+
+<div id="SceneException" >
+    <div class="pull-left" style="text-align:center;">
+        <h4>Exceptions List</h4>
+        <select id="exceptions_list" name="exceptions_list" multiple="multiple" style="min-width:10em;" >
+                % for cur_exception in show.exceptions:
+                    <option value="${cur_exception}">${cur_exception}</option>
+                % endfor
+        </select>
+        <div>
+            <input id="removeSceneName" value="Remove" class="btn float-left" type="button" style="margin-top: 10px;"/>
+        </div>
+        <br />
+    </div>
+</div>
+<div class="clearfix"></div>
+<br />
+
+<b>Quality:</b><br />
+<%
+    qualities = common.Quality.splitQuality(int(show.quality))
+    anyQualities = qualities[0]
+    bestQualities = qualities[1]
+%>
+<%include file="/inc_qualityChooser.mako"/>
+<br />
+
+<b>Default Episode Status:</b><br />
+(this will set the status for future episodes)<br />
+<select name="defaultEpStatus" id="defaultEpStatusSelect" class="form-control form-control-inline input-sm">
+    % for curStatus in [WANTED, SKIPPED, ARCHIVED, IGNORED]:
+    <option value="${curStatus}" ${('', 'selected="selected"')[curStatus == show.default_ep_status]}>${statusStrings[curStatus]}</option>
+    % endfor
+</select><br />
+<br />
+
+<b>Info Language:</b><br />
+(this will only affect the language of the retrieved metadata file contents and episode filenames)<br />
+<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm bfh-languages" data-language="${show.lang}" data-available="${','.join(sickbeard.indexerApi().config['valid_languages'])}"></select><br />
+<br />
+<b>Flatten files (no folders): </b> <input type="checkbox" name="flatten_folders" ${('', 'checked="checked"')[show.flatten_folders == 1 and not sickbeard.NAMING_FORCE_FOLDERS]} ${('', 'disabled="disabled"')[bool(sickbeard.NAMING_FORCE_FOLDERS)]}/><br />
+(Disabled: episodes folder-grouped by season. Enabled: no season folders)<br/>
+<br />
+
+<b>Paused: </b> <input type="checkbox" name="paused" ${('', 'checked="checked"')[show.paused == 1]} /><br />
+(check this if you wish to pause this show. Will not download anything until unpause)<br/>
+<br />
+
+<b>Subtitles: </b> <input type="checkbox" name="subtitles" ${('', 'checked="checked"')[show.subtitles == 1 and sickbeard.USE_SUBTITLES == True]} ${('disabled="disabled"', '')[bool(sickbeard.USE_SUBTITLES)]}/><br />
+(check this if you wish to search for subtitles in this show)<br/>
+<br />
+
+<b>Scene Numbering: </b>
+<input type="checkbox" name="scene" ${('', 'checked="checked"')[show.scene == 1]} /><br/>
+(check this if you wish to search by scene numbering, uncheck to search by indexer numbering)<br/>
+<br/>
+
+<b>Air by date: </b>
+<input type="checkbox" name="air_by_date" ${('', 'checked="checked"')[show.air_by_date == 1]} /><br />
+(check this if the show is released as Show.03.02.2010 rather than Show.S02E03. <span style="color:red">In case air date conflict between regular and special episodes, the later will be ignored.</span>)<br />
+<br />
+
+<b>Sports: </b>
+<input type="checkbox" name="sports" ${('', 'checked="checked"')[show.sports == 1]}/><br />
+(check this if the show is a sporting or MMA event and released as Show.03.02.2010 rather than Show.S02E03. <span style="color:red">In case air date conflict between regular and special episodes, the later will be ignored.</span>)<br />
+<br />
+
+<b>Anime: </b>
+<input type="checkbox" name="anime" ${('', 'checked="checked"')[show.is_anime == 1]}><br />
+(check this if the show is released as Show.265 rather than Show.S02E03, this show is an anime)<br />
+<br />
+
+<b>DVD Order: </b>
+<input type="checkbox" name="dvdorder" ${('', 'checked="checked"')[show.dvdorder == 1]} /><br/>
+(check this if you wish to use the DVD order instead of the Airing order. A "Force Full Update" is necessary, and if you have existing episodes you need to move them)
+<br/><br/>
+
+% if anyQualities + bestQualities:
+<b>Archive on first match:</b>
+<input type="checkbox" name="archive_firstmatch" ${('', 'checked="checked"')[show.archive_firstmatch == 1]} /><br>
+(check this to have the episode archived after the first best match is found from your archive quality list)</br>
+<br />
+% endif
+
+<b>Ignored Words:</b></br>
+<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="${show.rls_ignore_words}" class="form-control form-control-inline input-sm input350" /><br />
+Results with one or more word from this list will be ignored<br />
+Separate words with a comma, e.g. "word1,word2,word3"<br />
+<br />
+
+<b>Required Words:</b></br>
+<input type="text" name="rls_require_words" id="rls_require_words" value="${show.rls_require_words}" class="form-control form-control-inline input-sm input350" /><br />
+Results with no word from this list will be ignored<br />
+Separate words with a comma, e.g. "word1,word2,word3"<br />
+<br />
+
+% if show.is_anime:
+    <%include file="/inc_blackwhitelist.mako"/>
+    <script type="text/javascript" src="${sbRoot}/js/blackwhite.js?${sbPID}"></script>
+% endif
+
+<input type="submit" id="submit" value="Submit" class="btn btn-primary" />
+</form>
+
+<script type="text/javascript" charset="utf-8">
+    var all_exceptions = new Array;
+
+    jQuery('#location').fileBrowser({ title: 'Select Show Location' });
+
+    $('#submit').click(function(){
+        all_exceptions = []
+
+        $("#exceptions_list option").each  ( function() {
+            all_exceptions.push( $(this).val() );
+        });
+
+        $("#exceptions_list").val(all_exceptions);
+
+        % if show.is_anime:
+            generate_bwlist()
+        % endif
+        });
+    $('#addSceneName').click(function() {
+        var scene_ex = $('#SceneName').val()
+        var option = $("<option>")
+        all_exceptions = []
+
+        $("#exceptions_list option").each  ( function() {
+           all_exceptions.push( $(this).val() )
+        });
+
+        $('#SceneName').val('')
+
+        if (jQuery.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == ''))
+            return
+
+        $("#SceneException").show()
+
+        option.attr("value",scene_ex)
+        option.html(scene_ex)
+        return option.appendTo('#exceptions_list');
+    });
+
+    $('#removeSceneName').click(function() {
+        $('#exceptions_list option:selected').remove();
+
+        $(this).toggle_SceneException()
+    });
+
+   $.fn.toggle_SceneException = function() {
+        all_exceptions = []
+
+        $("#exceptions_list option").each  ( function() {
+            all_exceptions.push( $(this).val() );
+        });
+
+        if (all_exceptions == '')
+            $("#SceneException").hide();
+        else
+            $("#SceneException").show();
+    }
+
+    $(this).toggle_SceneException();
+</script>
+</div>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/editShow.tmpl b/gui/slick/interfaces/default/editShow.tmpl
deleted file mode 100644
index 8049440494acf625fd7fb8997186da1664260deb..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/editShow.tmpl
+++ /dev/null
@@ -1,210 +0,0 @@
-#import sickbeard
-#import adba
-#from sickbeard import common
-#from sickbeard.common import *
-#from sickbeard import exceptions
-#from sickbeard import scene_exceptions
-#from sickbeard.blackandwhitelist import *
-#set global $title="Edit " + $show.name
-#set global $header="Edit " + $show.name
-
-#set global $sbPath=".."
-
-#set global $topmenu="home"
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<div id="editShow">
-<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/lib/bootstrap-formhelpers.min-2.3.0.js?$sbPID"></script>
-
-
-
-<form action="editShow" method="post">
-<input type="hidden" name="show" value="$show.indexerid" />
-<b>Location:</b></br>
-<input type="text" name="location" id="location" value="$show._location" class="form-control form-control-inline input-sm input350" /><br />
-<br />
-
-<b>Scene Exception:</b><br />
-<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
-<input class="btn btn-inline" type="button" value="Add" id="addSceneName"><br />
-This will <b>affect the episode show search</b> on nzb and torrent provider.<br />
-        This list overrides the original name, it doesn't append to it.<br />
-
-<div id="SceneException" >
-    <div class="pull-left" style="text-align:center;">
-        <h4>Exceptions List</h4>
-        <select id="exceptions_list" name="exceptions_list" multiple="multiple" style="min-width:10em;" >
-                #for $cur_exception in $show.exceptions:
-                    <option value="$cur_exception">$cur_exception</option>
-                #end for
-        </select>
-        <div>
-            <input id="removeSceneName" value="Remove" class="btn float-left" type="button" style="margin-top: 10px;"/>
-        </div>
-        <br />
-    </div>
-</div>
-<div class="clearfix"></div>
-<br />
-
-<b>Quality:</b><br />
-#set $qualities = $common.Quality.splitQuality(int($show.quality))
-#set global $anyQualities = $qualities[0]
-#set global $bestQualities = $qualities[1]
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_qualityChooser.tmpl")
-<br />
-
-<b>Default Episode Status:</b><br />
-(this will set the status for future episodes)<br />
-<select name="defaultEpStatus" id="defaultEpStatusSelect" class="form-control form-control-inline input-sm">
-    #for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED]:
-    <option value="$curStatus" #if $curStatus == $show.default_ep_status then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
-    #end for
-</select><br />
-<br />
-
-<b>Info Language:</b><br />
-(this will only affect the language of the retrieved metadata file contents and episode filenames)<br />
-<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm bfh-languages" data-language="#echo $show.lang#" data-available="#echo ','.join($sickbeard.indexerApi().config['valid_languages'])#"></select><br />
-<br />
-
-<b>Flatten files (no folders): </b> <input type="checkbox" name="flatten_folders" #if $show.flatten_folders == 1 and not $sickbeard.NAMING_FORCE_FOLDERS then "checked=\"checked\"" else ""# #if $sickbeard.NAMING_FORCE_FOLDERS then "disabled=\"disabled\"" else ""#/><br />
-(Disabled: episodes folder-grouped by season. Enabled: no season folders)<br/>
-<br />
-
-<b>Paused: </b> <input type="checkbox" name="paused" #if $show.paused == 1 then "checked=\"checked\"" else ""# /><br />
-(check this if you wish to pause this show. Will not download anything until unpause)<br/>
-<br />
-
-<b>Subtitles: </b> <input type="checkbox" name="subtitles"#if $show.subtitles == 1 and $sickbeard.USE_SUBTITLES then " checked=\"checked\"" else ""##if not $sickbeard.USE_SUBTITLES then " disabled=\"disabled\"" else ""#/><br />
-(check this if you wish to search for subtitles in this show)<br/>
-<br />
-
-<b>Scene Numbering: </b>
-<input type="checkbox" name="scene" #if $show.scene == 1 then "checked=\"checked\"" else ""# /><br/>
-(check this if you wish to search by scene numbering, uncheck to search by indexer numbering)<br/>
-<br/>
-
-<b>Air by date: </b>
-<input type="checkbox" name="air_by_date" #if $show.air_by_date == 1 then "checked=\"checked\"" else ""# /><br />
-(check this if the show is released as Show.03.02.2010 rather than Show.S02E03. <span style="color:red">In case air date conflict between regular and special episodes, the later will be ignored.</span>)<br />
-<br />
-
-<b>Sports: </b>
-<input type="checkbox" name="sports" #if $show.sports == 1 then "checked=\"checked\"" else ""# /><br />
-(check this if the show is a sporting or MMA event and released as Show.03.02.2010 rather than Show.S02E03. <span style="color:red">In case air date conflict between regular and special episodes, the later will be ignored.</span>)<br />
-<br />
-
-<b>Anime: </b>
-<input type="checkbox" name="anime" #if $show.is_anime then "CHECKED" else ""#><br />
-(check this if the show is released as Show.265 rather than Show.S02E03, this show is an anime)<br />
-<br />
-
-<b>DVD Order: </b>
-<input type="checkbox" name="dvdorder" #if $show.dvdorder == 1 then "checked=\"checked\"" else ""# /><br/>
-(check this if you wish to use the DVD order instead of the Airing order. A "Force Full Update" is necessary, and if you have existing episodes you need to move them)
-<br/><br/>
-
-#if $anyQualities + $bestQualities
-<b>Archive on first match:</b>
-<input type="checkbox" name="archive_firstmatch" #if $show.archive_firstmatch == 1 then "checked=\"checked\"" else ""# /><br>
-(check this to have the episode archived after the first best match is found from your archive quality list)</br>
-<br />
-#end if
-
-<b>Ignored Words:</b></br>
-<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350" /><br />
-Results with one or more word from this list will be ignored<br />
-Separate words with a comma, e.g. "word1,word2,word3"<br />
-<br />
-
-<b>Required Words:</b></br>
-<input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350" /><br />
-Results with no word from this list will be ignored<br />
-Separate words with a comma, e.g. "word1,word2,word3"<br />
-<br />
-
-#if $show.is_anime:
-    #from sickbeard.blackandwhitelist import *
-    #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_blackwhitelist.tmpl")
-    <script type="text/javascript" src="$sbRoot/js/blackwhite.js?$sbPID"></script>
-#end if
-
-<input type="submit" id="submit" value="Submit" class="btn btn-primary" />
-</form>
-
-<script type="text/javascript" charset="utf-8">
-<!--
-    var all_exceptions = new Array;
-
-    jQuery('#location').fileBrowser({ title: 'Select Show Location' });
-
-    \$('#submit').click(function(){
-        all_exceptions = []
-
-        \$("#exceptions_list option").each  ( function() {
-            all_exceptions.push( \$(this).val() );
-        });
-
-        \$("#exceptions_list").val(all_exceptions);
-
-        #if $show.is_anime:
-            generate_bwlist()
-
-        #end if
-        });
-    \$('#addSceneName').click(function() {
-        var scene_ex = \$('#SceneName').val()
-        var option = \$("<option>")
-        all_exceptions = []
-
-        \$("#exceptions_list option").each  ( function() {
-           all_exceptions.push( \$(this).val() )
-        });
-
-        \$('#SceneName').val('')
-
-        if (jQuery.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == ''))
-            return
-
-        \$("#SceneException").show()
-
-        option.attr("value",scene_ex)
-        option.html(scene_ex)
-        return option.appendTo('#exceptions_list');
-    });
-
-    \$('#removeSceneName').click(function() {
-        \$('#exceptions_list option:selected').remove();
-
-        \$(this).toggle_SceneException()
-    });
-
-   $.fn.toggle_SceneException = function() {
-        all_exceptions = []
-
-        \$("#exceptions_list option").each  ( function() {
-            all_exceptions.push( \$(this).val() );
-        });
-
-        if (all_exceptions == '')
-            \$("#SceneException").hide();
-        else
-            \$("#SceneException").show();
-    }
-
-    \$(this).toggle_SceneException();
-
-//-->
-</script>
-</div>
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/errorlogs.mako b/gui/slick/interfaces/default/errorlogs.mako
new file mode 100644
index 0000000000000000000000000000000000000000..5131bdd8f8c61e7b817bd8aa93793b498787266a
--- /dev/null
+++ b/gui/slick/interfaces/default/errorlogs.mako
@@ -0,0 +1,21 @@
+<%include file="/inc_top.mako"/>
+<%!
+    import sickbeard
+    from sickbeard import classes
+    from sickbeard.logger import reverseNames
+%>
+<h1 class="header">${title}</h1>
+<div class="align-left"><pre>
+% if classes.ErrorViewer.errors:
+    % for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
+        ${curError.time} ${curError.message}
+    % endfor
+% endif
+</pre>
+</div>
+
+<script type="text/javascript" charset="utf-8">
+window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes
+</script>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/errorlogs.tmpl b/gui/slick/interfaces/default/errorlogs.tmpl
deleted file mode 100644
index 3e8b9d9374e0a9d73f2c56c2a89c279bf0753341..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/errorlogs.tmpl
+++ /dev/null
@@ -1,34 +0,0 @@
-#import sickbeard
-#from sickbeard import classes
-#from sickbeard.common import *
-#set global $header="Logs &amp; Errors"
-#set global $title="Logs &amp; Errors"
-
-#set global $sbPath = ".."
-
-#set global $topmenu="errorlogs"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-<div class="align-left"><pre>
-#if $classes.ErrorViewer.errors:
-    #for $curError in sorted($classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
-        #filter WebSafe
-$curError.time $curError.message
-        #end filter
-    #end for
-#end if
-</pre>
-</div>
-
-<script type="text/javascript" charset="utf-8">
-<!--
-window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes
-//-->
-</script>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/genericMessage.mako b/gui/slick/interfaces/default/genericMessage.mako
new file mode 100644
index 0000000000000000000000000000000000000000..c7835dd385f359ccc802ed96db5ca7c38d5db7e9
--- /dev/null
+++ b/gui/slick/interfaces/default/genericMessage.mako
@@ -0,0 +1,4 @@
+<%include file="/inc_top.mako"/>
+<h2>${subject}</h2>
+${message}
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/genericMessage.tmpl b/gui/slick/interfaces/default/genericMessage.tmpl
deleted file mode 100644
index f62d0809b3d1f3de52e26a29cff6f1c2d3d6ea73..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/genericMessage.tmpl
+++ /dev/null
@@ -1,14 +0,0 @@
-#import sickbeard
-#set global $title=""
-
-#set global $sbPath="../.."
-
-
-#set global $topmenu="home"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<h2>$subject</h2>
-$message
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/history.mako b/gui/slick/interfaces/default/history.mako
new file mode 100644
index 0000000000000000000000000000000000000000..fc1818746a838e4647208cc9a2713df38ca87674
--- /dev/null
+++ b/gui/slick/interfaces/default/history.mako
@@ -0,0 +1,239 @@
+<%!
+    import sickbeard
+    import os.path
+    import datetime
+    import re
+    import time
+
+    from sickbeard import history
+    from sickbeard import providers
+    from sickbeard import sbdatetime
+    from sickbeard.providers import generic
+
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED, DOWNLOADED, SUBTITLED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings, Overview
+%>
+<%
+    layout = sickbeard.HISTORY_LAYOUT
+    history_limit = sickbeard.HISTORY_LIMIT
+%>
+<%include file="/inc_top.mako"/>
+
+<style type="text/css">
+.sort_data {display:none;}
+</style>
+
+<script type="text/javascript">
+$.tablesorter.addParser({
+    id: 'cDate',
+    is: function(s) {
+        return false;
+    },
+    format: function(s) {
+        return s;
+    },
+    type: 'numeric'
+});
+
+$(document).ready(function(){
+    $("#historyTable:has(tbody tr)").tablesorter({
+        widgets: ['zebra', 'filter'],
+        sortList: [[0,1]],
+      textExtraction: {
+        % if ( layout == 'detailed'):
+            0: function(node) { return $(node).find("span").text().toLowerCase(); },
+            4: function(node) { return $(node).find("span").text().toLowerCase(); }
+        % else:
+            0: function(node) { return $(node).find("span").text().toLowerCase(); },
+            1: function(node) { return $(node).find("span").text().toLowerCase(); },
+            2: function(node) { return $(node).attr("provider").toLowerCase(); },
+            5: function(node) { return $(node).attr("quality").toLowerCase(); }
+        % endif
+      },
+        headers: {
+        % if ( layout == 'detailed'):
+          0: { sorter: 'cDate' },
+          4: { sorter: 'quality' }
+        % else:
+          0: { sorter: 'cDate' },
+          4: { sorter: false },
+          5: { sorter: 'quality' }
+        % endif
+      }
+
+    });
+    $('#limit').change(function(){
+        url = '${sbRoot}/history/?limit='+$(this).val()
+        window.location.href = url
+    });
+
+    <% fuzzydate = 'airdate' %>
+    % if sickbeard.FUZZY_DATING:
+    fuzzyMoment({
+        containerClass : '.${fuzzydate}',
+        dateHasTime : true,
+        dateFormat : '${sickbeard.DATE_PRESET}',
+        timeFormat : '${sickbeard.TIME_PRESET_W_SECONDS}',
+        trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]},
+        dtGlue : ', ',
+    });
+    % endif
+
+});
+</script>
+% if not header is UNDEFINED:
+  <h1 class="header">${header}</h1>
+% else:
+  <h1 class="title">${title}</h1>
+% endif
+<div class="h2footer pull-right"><b>Limit:</b>
+    <select name="history_limit" id="history_limit" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+        <option value="${sbRoot}/setHistoryLimit/?history_limit=100" ${('', 'selected="selected"')[history_limit == 100]}>100</option>
+        <option value="${sbRoot}/setHistoryLimit/?history_limit=250" ${('', 'selected="selected"')[history_limit == 250]}>250</option>
+        <option value="${sbRoot}/setHistoryLimit/?history_limit=500" ${('', 'selected="selected"')[history_limit == 500]}>500</option>
+        <option value="${sbRoot}/setHistoryLimit/?history_limit=0"   ${('', 'selected="selected"')[history_limit == 0  ]}>All</option>
+    </select>
+
+    <span> Layout:
+        <select name="HistoryLayout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+            <option value="${sbRoot}/setHistoryLayout/?layout=compact"  ${('', 'selected="selected"')[sickbeard.HISTORY_LAYOUT == 'compact']}>Compact</option>
+            <option value="${sbRoot}/setHistoryLayout/?layout=detailed" ${('', 'selected="selected"')[sickbeard.HISTORY_LAYOUT == 'detailed']}>Detailed</option>
+        </select>
+    </span>
+</div>
+<br>
+
+% if layout == "detailed":
+    <table id="historyTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
+        <thead>
+            <tr>
+                <th class="nowrap">Time</th>
+                <th>Episode</th>
+                <th>Action</th>
+                <th>Provider</th>
+                <th>Quality</th>
+            </tr>
+        </thead>
+
+        <tfoot>
+            <tr>
+                <th class="nowrap" colspan="5">&nbsp;</th>
+            </tr>
+        </tfoot>
+
+        <tbody>
+        % for hItem in historyResults:
+            <% curStatus, curQuality = Quality.splitCompositeStatus(int(hItem["action"])) %>
+            <tr>
+                <% curdatetime = datetime.datetime.strptime(str(hItem["date"]), history.dateFormat) %>
+                <td align="center"><div class="${fuzzydate}">${sbdatetime.sbdatetime.sbfdatetime(curdatetime, show_seconds=True)}</div><span class="sort_data">${time.mktime(curdatetime.timetuple())}</span></td>
+                <td class="tvShow" width="35%"><a href="${sbRoot}/home/displayShow?show=${hItem["showid"]}#season-${hItem["season"]}">${hItem["show_name"]} - ${"S%02i" % int(hItem["season"])}${"E%02i" % int(hItem["episode"])} ${('', '<span class="quality Proper">Proper</span>')["proper" in hItem["resource"].lower() or "repack" in hItem["resource"].lower()]}</a></td>
+                <td align="center" ${('', 'class="subtitles_column"')[curStatus == SUBTITLED]}>
+                % if curStatus == SUBTITLED:
+                    <img width="16" height="11" style="vertical-align:middle;" src="${sbRoot}/images/subtitles/flags/${hItem['resource']}.png" onError="this.onerror=null;this.src='${sbRoot}/images/flags/unknown.png';">
+                % endif
+                    <span style="cursor: help; vertical-align:middle;" title="${os.path.basename(hItem['resource'])}">${statusStrings[curStatus]}</span>
+                </td>
+                <td align="center">
+                % if curStatus in [DOWNLOADED, ARCHIVED]:
+                    % if hItem["provider"] != "-1":
+                        <span style="vertical-align:middle;"><i>${hItem["provider"]}</i></span>
+                    % endif
+                % else:
+                    % if hItem["provider"] > 0:
+                        % if curStatus in [SNATCHED, FAILED]:
+                            <% provider = providers.getProviderClass(generic.GenericProvider.makeID(hItem["provider"])) %>
+                            % if provider != None:
+                                <img src="${sbRoot}/images/providers/${provider.imageName()}" width="16" height="16" style="vertical-align:middle;" /> <span style="vertical-align:middle;">${provider.name}</span>
+                            % else:
+                                <img src="${sbRoot}/images/providers/missing.png" width="16" height="16" style="vertical-align:middle;" title="missing provider"/> <span style="vertical-align:middle;">Missing Provider</span>
+                            % endif
+                        % else:
+                                <img src="${sbRoot}/images/subtitles/${hItem['provider']}.png" width="16" height="16" style="vertical-align:middle;" /> <span style="vertical-align:middle;">${hItem["provider"].capitalize()}</span>
+                        % endif
+                    % endif
+                % endif
+                </td>
+                <span style="display: none;">${curQuality}</span>
+                <td align="center"><span class="quality ${Quality.qualityStrings[curQuality].replace("720p","HD720p").replace("1080p","HD1080p").replace("HDTV", "HD720p")}">${Quality.qualityStrings[curQuality]}</span></td>
+            </tr>
+        % endfor
+        </tbody>
+    </table>
+% else:
+
+    <table id="historyTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
+        <thead>
+            <tr>
+                <th class="nowrap">Time</th>
+                <th>Episode</th>
+                <th>Snatched</th>
+                <th>Downloaded</th>
+                % if sickbeard.USE_SUBTITLES:
+                <th>Subtitled</th>
+                % endif
+                <th>Quality</th>
+            </tr>
+        </thead>
+
+        <tfoot>
+            <tr>
+                <th class="nowrap" colspan="6">&nbsp;</th>
+            </tr>
+        </tfoot>
+
+        <tbody>
+        % for hItem in compactResults:
+            <tr>
+                <% curdatetime = datetime.datetime.strptime(str(hItem["actions"][0]["time"]), history.dateFormat) %>
+                <td align="center"><div class="${fuzzydate}">${sbdatetime.sbdatetime.sbfdatetime(curdatetime, show_seconds=True)}</div><span class="sort_data">${time.mktime(curdatetime.timetuple())}</span></td>
+                <td class="tvShow" width="25%">
+                    <span><a href="${sbRoot}/home/displayShow?show=${hItem["show_id"]}#season-${hItem["season"]}">${hItem["show_name"]} - ${"S%02i" % int(hItem["season"])}${"E%02i" % int(hItem["episode"])}${('', ' <span class="quality Proper">Proper</span>')['proper' in hItem["resource"].lower() or 'repack' in hItem["resource"].lower()]}</a></span>
+                </td>
+                <td align="center" provider="${str(sorted(hItem["actions"])[0]["provider"])}">
+                    % for action in sorted(hItem["actions"]):
+                        <% curStatus, curQuality = Quality.splitCompositeStatus(int(action["action"])) %>
+                        % if curStatus in [SNATCHED, FAILED]:
+                            <% provider = providers.getProviderClass(generic.GenericProvider.makeID(action["provider"])) %>
+                            % if provider != None:
+                                <img src="${sbRoot}/images/providers/${provider.imageName()}" width="16" height="16" style="vertical-align:middle;" alt="${provider.name}" style="cursor: help;" title="${provider.name}: ${os.path.basename(action["resource"])}"/>
+                            % else:
+                                <img src="${sbRoot}/images/providers/missing.png" width="16" height="16" style="vertical-align:middle;" alt="missing provider" title="missing provider"/>
+                            % endif
+                        % endif
+                    % endfor
+                </td>
+                <td align="center">
+                    % for action in sorted(hItem["actions"]):
+                        <% curStatus, curQuality = Quality.splitCompositeStatus(int(action["action"])) %>
+                        % if curStatus in [DOWNLOADED, ARCHIVED]:
+                            % if action["provider"] != "-1":
+                                <span style="cursor: help;" title="${os.path.basename(action["resource"])}"><i>${action["provider"]}</i></span>
+                            % else:
+                                <span style="cursor: help;" title="${os.path.basename(action["resource"])}"></span>
+                            % endif
+                        % endif
+                    % endfor
+                </td>
+                % if sickbeard.USE_SUBTITLES:
+                <td align="center">
+                    % for action in sorted(hItem["actions"]):
+                        <% curStatus, curQuality = Quality.splitCompositeStatus(int(action["action"])) %>
+                        % if curStatus == SUBTITLED:
+                            <img src="${sbRoot}/images/subtitles/${action['provider']}.png" width="16" height="16" style="vertical-align:middle;" alt="${action["provider"]}" title="${action["provider"].capitalize()}: ${os.path.basename(action["resource"])}"/>
+                            <span style="vertical-align:middle;"> / </span>
+                            <img width="16" height="11" style="vertical-align:middle;" src="${sbRoot}/images/subtitles/flags/${action['resource']}.png" onError="this.onerror=null;this.src='${sbRoot}/images/flags/unknown.png';" style="vertical-align: middle !important;">
+                            &nbsp;
+                        % endif
+                    % endfor
+                </td>
+                % endif
+                <td align="center" width="14%" quality="${curQuality}"><span class="quality ${Quality.qualityStrings[curQuality].replace("720p","HD720p").replace("1080p","HD1080p").replace("RawHD TV", "RawHD").replace("HD TV", "HD720p")}">${Quality.qualityStrings[curQuality]}</span></td>
+            </tr>
+        % endfor
+        </tbody>
+    </table>
+
+% endif
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/history.tmpl b/gui/slick/interfaces/default/history.tmpl
deleted file mode 100644
index 01a0311158b2c830da6c73047eed4fc7f325c698..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/history.tmpl
+++ /dev/null
@@ -1,242 +0,0 @@
-#import sickbeard
-#import os.path
-#import datetime
-#import re
-#from sickbeard import history
-#from sickbeard import providers
-#from sickbeard import sbdatetime
-#from sickbeard.providers import generic
-#from sickbeard.common import *
-#set global $title="History"
-#set global $header="History"
-#set global $sbPath=".."
-#set global $topmenu="history"#
-#set $layout = $sickbeard.HISTORY_LAYOUT
-#set $history_limit = $sickbeard.HISTORY_LIMIT
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<style type="text/css">
-.sort_data {display:none}
-</style>
-
-<script type="text/javascript">
-<!--
-
-\$.tablesorter.addParser({
-    id: 'cDate',
-    is: function(s) {
-        return false;
-    },
-    format: function(s) {
-        return s;
-    },
-    type: 'numeric'
-});
-
-\$(document).ready(function()
-{
-    \$("#historyTable:has(tbody tr)").tablesorter({
-        widgets: ['zebra', 'filter'],
-        sortList: [[0,1]],
-      textExtraction: {
-        #if ( $layout == 'detailed'):
-            0: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            4: function(node) { return \$(node).find("span").text().toLowerCase(); }
-        #else
-            0: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            1: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            2: function(node) { return \$(node).attr("provider").toLowerCase(); },
-            5: function(node) { return \$(node).attr("quality").toLowerCase(); }
-        #end if
-      },
-        headers: {
-        #if ( $layout == 'detailed'):
-          0: { sorter: 'cDate' },
-          4: { sorter: 'quality' }
-        #else
-          0: { sorter: 'cDate' },
-          4: { sorter: false },
-          5: { sorter: 'quality' }
-        #end if
-      }
-
-    });
-    \$('#limit').change(function(){
-        url = '$sbRoot/history/?limit='+\$(this).val()
-        window.location.href = url
-    });
-
-    #set $fuzzydate = 'airdate'
-    #if $sickbeard.FUZZY_DATING:
-    fuzzyMoment({
-        containerClass : '.${fuzzydate}',
-        dateHasTime : true,
-        dateFormat : '${sickbeard.DATE_PRESET}',
-        timeFormat : '${sickbeard.TIME_PRESET_W_SECONDS}',
-        trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#,
-        dtGlue : ', ',
-    });
-    #end if
-
-});
-//-->
-</script>
-#if $varExists('header')
-  <h1 class="header">$header</h1>
-#else
-  <h1 class="title">$title</h1>
-#end if
-<div class="h2footer pull-right"><b>Limit:</b>
-    <select name="history_limit" id="history_limit" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-        <option value="$sbRoot/setHistoryLimit/?history_limit=100" #if $history_limit == "100" then "selected=\"selected\"" else ""#>100</option>
-        <option value="$sbRoot/setHistoryLimit/?history_limit=250" #if $history_limit == "250" then "selected=\"selected\"" else ""#>250</option>
-        <option value="$sbRoot/setHistoryLimit/?history_limit=500" #if $history_limit == "500" then "selected=\"selected\"" else ""#>500</option>
-        <option value="$sbRoot/setHistoryLimit/?history_limit=0" #if $history_limit == "0" then "selected=\"selected\"" else ""#>All</option>
-    </select>
-
-
-    <span> Layout:
-        <select name="HistoryLayout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-            <option value="$sbRoot/setHistoryLayout/?layout=compact" #if $sickbeard.HISTORY_LAYOUT == "compact" then "selected=\"selected\"" else ""#>Compact</option>
-            <option value="$sbRoot/setHistoryLayout/?layout=detailed" #if $sickbeard.HISTORY_LAYOUT == "detailed" then "selected=\"selected\"" else ""#>Detailed</option>
-        </select>
-    </span>
-</div>
-<br>
-
-#if $layout == "detailed"
-    <table id="historyTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
-        <thead>
-            <tr>
-                <th class="nowrap">Time</th>
-                <th>Episode</th>
-                <th>Action</th>
-                <th>Provider</th>
-                <th>Quality</th>
-            </tr>
-        </thead>
-
-        <tfoot>
-            <tr>
-                <th class="nowrap" colspan="5">&nbsp;</th>
-            </tr>
-        </tfoot>
-
-        <tbody>
-        #for $hItem in $historyResults:
-            #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($hItem["action"]))
-            <tr>
-                #set $curdatetime = $datetime.datetime.strptime(str($hItem["date"]), $history.dateFormat)
-                <td align="center"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($curdatetime, show_seconds=True)</div><span class="sort_data">$time.mktime($curdatetime.timetuple())</span></td>
-                <td class="tvShow" width="35%"><a href="$sbRoot/home/displayShow?show=$hItem["showid"]#season-$hItem["season"]">$hItem["show_name"] - <%="S%02i" % int(hItem["season"])+"E%02i" % int(hItem["episode"]) %>#if "proper" in $hItem["resource"].lower() or "repack" in $hItem["resource"].lower() then ' <span class="quality Proper">Proper</span>' else ""#</a></td>
-                <td align="center" #if $curStatus == SUBTITLED then 'class="subtitles_column"' else ''#>
-                #if $curStatus == SUBTITLED:
-                    <img width="16" height="11" style="vertical-align:middle;" src="$sbRoot/images/subtitles/flags/${hItem['resource']}.png" onError="this.onerror=null;this.src='$sbRoot/images/flags/unknown.png';">
-                #end if
-                    <span style="cursor: help; vertical-align:middle;" title="$os.path.basename($hItem['resource'])">$statusStrings[$curStatus]</span>
-                </td>
-                <td align="center">
-                #if $curStatus in [DOWNLOADED, ARCHIVED]:
-                    #if $hItem["provider"] != "-1":
-                        <span style="vertical-align:middle;"><i>$hItem["provider"]</i></span>
-                    #end if
-                #else
-                    #if $hItem["provider"] > 0
-                        #if $curStatus in [SNATCHED, FAILED]:
-                            #set $provider = $providers.getProviderClass($generic.GenericProvider.makeID($hItem["provider"]))
-                            #if $provider != None:
-                                <img src="$sbRoot/images/providers/<%=provider.imageName()%>" width="16" height="16" style="vertical-align:middle;" /> <span style="vertical-align:middle;">$provider.name</span>
-                            #else:
-                                <img src="$sbRoot/images/providers/missing.png" width="16" height="16" style="vertical-align:middle;" title="missing provider"/> <span style="vertical-align:middle;">Missing Provider</span>
-                            #end if
-                        #else:
-                                <img src="$sbRoot/images/subtitles/${hItem['provider']}.png" width="16" height="16" style="vertical-align:middle;" /> <span style="vertical-align:middle;"><%=hItem["provider"].capitalize()%></span>
-                        #end if
-                    #end if
-                #end if
-                </td>
-                <span style="display: none;">$curQuality</span>
-                <td align="center"><span class="quality $Quality.qualityStrings[$curQuality].replace("720p","HD720p").replace("1080p","HD1080p").replace("HDTV", "HD720p")">$Quality.qualityStrings[$curQuality]</span></td>
-            </tr>
-        #end for
-        </tbody>
-    </table>
-
-#else:
-
-    <table id="historyTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
-        <thead>
-            <tr>
-                <th class="nowrap">Time</th>
-                <th>Episode</th>
-                <th>Snatched</th>
-                <th>Downloaded</th>
-            #if sickbeard.USE_SUBTITLES
-                <th>Subtitled</th>
-            #end if
-                <th>Quality</th>
-            </tr>
-        </thead>
-
-        <tfoot>
-            <tr>
-                <th class="nowrap" colspan="6">&nbsp;</th>
-            </tr>
-        </tfoot>
-
-        <tbody>
-        #for $hItem in $compactResults:
-            <tr>
-                #set $curdatetime = $datetime.datetime.strptime(str($hItem["actions"][0]["time"]), $history.dateFormat)
-                <td align="center"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($curdatetime, show_seconds=True)</div><span class="sort_data">$time.mktime($curdatetime.timetuple())</span></td>
-                <td class="tvShow" width="25%">
-                    <span><a href="$sbRoot/home/displayShow?show=$hItem["show_id"]#season-$hItem["season"]">$hItem["show_name"] - <%="S%02i" % int(hItem["season"])+"E%02i" % int(hItem["episode"]) %>#if "proper" in $hItem["resource"].lower() or "repack" in $hItem["resource"].lower() then ' <span class="quality Proper">Proper</span>' else ""#</a></span>
-                </td>
-                <td align="center" provider="<%=str(sorted(hItem["actions"])[0]["provider"])%>">
-                    #for $action in sorted($hItem["actions"]):
-                        #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action["action"]))
-                        #if $curStatus in [SNATCHED, FAILED]:
-                            #set $provider = $providers.getProviderClass($generic.GenericProvider.makeID($action["provider"]))
-                            #if $provider != None:
-                                <img src="$sbRoot/images/providers/<%=provider.imageName()%>" width="16" height="16" style="vertical-align:middle;" alt="$provider.name" style="cursor: help;" title="$provider.name: $os.path.basename($action["resource"])"/>
-                            #else:
-                                <img src="$sbRoot/images/providers/missing.png" width="16" height="16" style="vertical-align:middle;" alt="missing provider" title="missing provider"/>
-                            #end if
-                        #end if
-                    #end for
-                </td>
-                <td align="center">
-                    #for $action in sorted($hItem["actions"]):
-                        #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action["action"]))
-                        #if $curStatus in [DOWNLOADED, ARCHIVED]:
-                            #if $action["provider"] != "-1":
-                                <span style="cursor: help;" title="$os.path.basename($action["resource"])"><i>$action["provider"]</i></span>
-                            #else:
-                                <span style="cursor: help;" title="$os.path.basename($action["resource"])"></span>
-                            #end if
-                        #end if
-                    #end for
-                </td>
-                #if sickbeard.USE_SUBTITLES:
-                <td align="center">
-                    #for $action in sorted($hItem["actions"]):
-                        #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action["action"]))
-                        #if $curStatus == SUBTITLED:
-                            <img src="$sbRoot/images/subtitles/${action['provider']}.png" width="16" height="16" style="vertical-align:middle;" alt="$action["provider"]" title="<%=action["provider"].capitalize()%>: $os.path.basename($action["resource"])"/>
-                            <span style="vertical-align:middle;"> / </span>
-                            <img width="16" height="11" style="vertical-align:middle;" src="$sbRoot/images/subtitles/flags/${action['resource']}.png" onError="this.onerror=null;this.src='$sbRoot/images/flags/unknown.png';" style="vertical-align: middle !important;">
-                            &nbsp;
-                        #end if
-                    #end for
-                </td>
-                #end if
-                <td align="center" width="14%" quality="$curQuality"><span class="quality $Quality.qualityStrings[$curQuality].replace("720p","HD720p").replace("1080p","HD1080p").replace("RawHD TV", "RawHD").replace("HD TV", "HD720p")">$Quality.qualityStrings[$curQuality]</span></td>
-            </tr>
-        #end for
-        </tbody>
-    </table>
-
-#end if
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/home.mako b/gui/slick/interfaces/default/home.mako
new file mode 100644
index 0000000000000000000000000000000000000000..2f27dc7ef1ccbdddde8056bd01f5f8f8cd175f36
--- /dev/null
+++ b/gui/slick/interfaces/default/home.mako
@@ -0,0 +1,847 @@
+<%!
+    import sickbeard
+    import calendar
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
+    from sickbeard import db, sbdatetime, network_timezones
+    import datetime
+    import re
+%>
+<%include file="/inc_top.mako"/>
+<%
+    myDB = db.DBConnection()
+    today = str(datetime.date.today().toordinal())
+    layout = sickbeard.HOME_LAYOUT
+
+    status_quality  = '(' + ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER]) + ')'
+    status_download = '(' + ','.join([str(x) for x in Quality.DOWNLOADED + [ARCHIVED]]) + ')'
+
+    sql_statement  = 'SELECT showid, '
+
+    sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE showid=tv_eps.showid AND season > 0 AND episode > 0 AND airdate > 1 AND status IN ' + status_quality + ') AS ep_snatched, '
+    sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE showid=tv_eps.showid AND season > 0 AND episode > 0 AND airdate > 1 AND status IN ' + status_download + ') AS ep_downloaded, '
+    sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE showid=tv_eps.showid AND season > 0 AND episode > 0 AND airdate > 1 '
+    sql_statement += ' AND ((airdate <= ' + today + ' AND (status = ' + str(SKIPPED) + ' OR status = ' + str(WANTED) + ' OR status = ' + str(FAILED) + ')) '
+    sql_statement += ' OR (status IN ' + status_quality + ') OR (status IN ' + status_download + '))) AS ep_total, '
+
+    sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate >= ' + today + ' AND (status = ' + str(UNAIRED) + ' OR status = ' + str(WANTED) + ') ORDER BY airdate ASC LIMIT 1) AS ep_airs_next, '
+    sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate > 1 AND status <> ' + str(UNAIRED) + ' ORDER BY airdate DESC LIMIT 1) AS ep_airs_prev '
+    sql_statement += ' FROM tv_episodes tv_eps GROUP BY showid'
+
+    sql_result = myDB.select(sql_statement)
+
+    show_stat = {}
+    max_download_count = 1000
+
+    for cur_result in sql_result:
+        show_stat[cur_result['showid']] = cur_result
+        if cur_result['ep_total'] > max_download_count:
+            max_download_count = cur_result['ep_total']
+
+    max_download_count = max_download_count * 100
+%>
+
+<script type="text/javascript" charset="utf-8">
+
+$.tablesorter.addParser({
+    id: 'loadingNames',
+    is: function(s) {
+        return false;
+    },
+    format: function(s) {
+        if (s.indexOf('Loading...') == 0)
+          return s.replace('Loading...','000');
+        else
+        % if not sickbeard.SORT_ARTICLE:
+            return (s || '').replace(/^(The|A|An)\s/i,'');
+        % else:
+            return (s || '');
+        % endif
+    },
+    type: 'text'
+});
+
+$.tablesorter.addParser({
+    id: 'quality',
+    is: function(s) {
+        return false;
+    },
+    format: function(s) {
+        return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7);
+    },
+    type: 'numeric'
+});
+
+$.tablesorter.addParser({
+    id: 'eps',
+    is: function(s) {
+        return false;
+    },
+    format: function(s) {
+        match = s.match(/^(.*)/);
+
+        if (match == null || match[1] == "?")
+          return -10;
+
+        var nums = match[1].split(" / ");
+        if (nums[0].indexOf("+") != -1) {
+            var num_parts = nums[0].split("+");
+            nums[0] = num_parts[0];
+        }
+
+        nums[0] = parseInt(nums[0])
+        nums[1] = parseInt(nums[1])
+
+        if (nums[0] === 0)
+          return nums[1];
+
+        var finalNum = parseInt(${max_download_count}*nums[0]/nums[1]);
+        var pct = Math.round((nums[0]/nums[1])*100) / 1000
+        if (finalNum > 0)
+          finalNum += nums[0];
+
+        return finalNum + pct;
+    },
+    type: 'numeric'
+});
+
+
+$(document).ready(function(){
+
+    $("img#network").on('error', function(){
+        $(this).parent().text($(this).attr('alt'));
+        $(this).remove();
+    });
+
+    $("#showListTableShows:has(tbody tr)").tablesorter({
+        sortList: [[6,1],[2,0]],
+        textExtraction: {
+            0: function(node) { return $(node).find("span").text().toLowerCase(); },
+            1: function(node) { return $(node).find("span").text().toLowerCase(); },
+            3: function(node) { return $(node).find("span").prop("title").toLowerCase(); },
+            4: function(node) { return $(node).find("span").text().toLowerCase(); },
+            5: function(node) { return $(node).find("span").text(); },
+            6: function(node) { return $(node).find("img").attr("alt"); }
+        },
+        widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'],
+        headers: {
+            0: { sorter: 'isoDate' },
+            1: { columnSelector: false },
+            2: { sorter: 'loadingNames' },
+            4: { sorter: 'quality' },
+            5: { sorter: 'eps' },
+            % if sickbeard.FILTER_ROW:
+                6: { filter : 'parsed' }
+            % endif
+        },
+        widgetOptions : {
+            % if sickbeard.FILTER_ROW:
+                filter_columnFilters: true,
+                filter_hideFilters : true,
+                filter_saveFilters : true,
+                filter_functions : {
+                   5:function(e, n, f, i, r, c) {
+                        var test = false;
+                        var pct = Math.floor((n % 1) * 1000);
+                        if (f === '') {
+                           test = true;
+                        } else {
+                            var result = f.match(/(<|<=|>=|>)\s(\d+)/i);
+                            if (result) {
+                                if (result[1] === "<") {
+                                    if (pct < parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                } else if (result[1] === "<=") {
+                                    if (pct <= parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                } else if (result[1] === ">=") {
+                                    if (pct >= parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                } else if (result[1] === ">") {
+                                    if (pct > parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                }
+                            }
+
+                            var result = f.match(/(\d+)\s(-|to)\s(\d+)/i);
+                            if (result) {
+                                if ((result[2] === "-") || (result[2] === "to")) {
+                                    if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) {
+                                        test = true;
+                                    }
+                                }
+                            }
+
+                            var result = f.match(/(=)?\s?(\d+)\s?(=)?/i);
+                            if (result) {
+                                if ((result[1] === "=") || (result[3] === "=")) {
+                                    if (parseInt(result[2]) === pct) {
+                                        test = true;
+                                    }
+                                }
+                            }
+
+                            if (!isNaN(parseFloat(f)) && isFinite(f)) {
+                                if (parseInt(f) === pct) {
+                                    test = true;
+                                }
+                            }
+                        }
+                        return test;
+                    },
+                },
+            % else:
+                filter_columnFilters: false,
+            % endif
+            filter_reset: '.resetshows',
+            columnSelector_mediaquery: false,
+        },
+        sortStable: true,
+        sortAppend: [[2,0]]
+    });
+
+    $("#showListTableAnime:has(tbody tr)").tablesorter({
+        sortList: [[6,1],[2,0]],
+        textExtraction: {
+            0: function(node) { return $(node).find("span").text().toLowerCase(); },
+            1: function(node) { return $(node).find("span").text().toLowerCase(); },
+            3: function(node) { return $(node).find("span").prop("title").toLowerCase(); },
+            4: function(node) { return $(node).find("span").text().toLowerCase(); },
+            5: function(node) { return $(node).find("span").text(); },
+            6: function(node) { return $(node).find("img").attr("alt"); }
+        },
+        widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'],
+        headers: {
+            0: { sorter: 'isoDate' },
+            1: { columnSelector: false },
+            2: { sorter: 'loadingNames' },
+            4: { sorter: 'quality' },
+            5: { sorter: 'eps' },
+            % if sickbeard.FILTER_ROW:
+            6: { filter : 'parsed' }
+            % endif
+        },
+        widgetOptions : {
+            % if sickbeard.FILTER_ROW:
+                filter_columnFilters: true,
+                filter_hideFilters : true,
+                filter_saveFilters : true,
+                filter_functions : {
+                   5:function(e, n, f, i, r, c) {
+                        var test = false;
+                        var pct = Math.floor((n % 1) * 1000);
+                        if (f === '') {
+                           test = true;
+                        } else {
+                            var result = f.match(/(<|<=|>=|>)\s(\d+)/i);
+                            if (result) {
+                                if (result[1] === "<") {
+                                    if (pct < parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                } else if (result[1] === "<=") {
+                                    if (pct <= parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                } else if (result[1] === ">=") {
+                                    if (pct >= parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                } else if (result[1] === ">") {
+                                    if (pct > parseInt(result[2])) {
+                                        test = true;
+                                    }
+                                }
+                            }
+
+                            var result = f.match(/(\d+)\s(-|to)\s(\d+)/i);
+                            if (result) {
+                                if ((result[2] === "-") || (result[2] === "to")) {
+                                    if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) {
+                                        test = true;
+                                    }
+                                }
+                            }
+
+                            var result = f.match(/(=)?\s?(\d+)\s?(=)?/i);
+                            if (result) {
+                                if ((result[1] === "=") || (result[3] === "=")) {
+                                    if (parseInt(result[2]) === pct) {
+                                        test = true;
+                                    }
+                                }
+                            }
+
+                            if (!isNaN(parseFloat(f)) && isFinite(f)) {
+                                if (parseInt(f) === pct) {
+                                    test = true;
+                                }
+                            }
+                        }
+                        return test;
+                    },
+                },
+            % else:
+                filter_columnFilters: false,
+            % endif
+            filter_reset: '.resetanime',
+            columnSelector_mediaquery: false,
+        },
+        sortStable: true,
+        sortAppend: [[2,0]]
+    });
+
+    if ($("#showListTableShows").find("tbody").find("tr").size() > 0)
+        $.tablesorter.filter.bindSearch( "#showListTableShows", $('.search') );
+
+    % if sickbeard.ANIME_SPLIT_HOME:
+        if ($("#showListTableAnime").find("tbody").find("tr").size() > 0)
+            $.tablesorter.filter.bindSearch( "#showListTableAnime", $('.search') );
+    % endif
+
+    <% fuzzydate = 'airdate' %>
+    % if sickbeard.FUZZY_DATING:
+    fuzzyMoment({
+        dtInline : ${('true', 'false')['poster' in sickbeard.layout]},
+        containerClass : '.${fuzzydate}',
+        dateHasTime : false,
+        dateFormat : '${sickbeard.DATE_PRESET}',
+        timeFormat : '${sickbeard.TIME_PRESET}',
+        trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]}
+    });
+    % endif
+
+    var $container = [$('#container'), $('#container-anime')];
+
+    jQuery.each($container, function (j) {
+        this.isotope({
+            itemSelector: '.show',
+            sortBy : '${sickbeard.POSTER_SORTBY}',
+            sortAscending: ${sickbeard.POSTER_SORTDIR},
+            layoutMode: 'masonry',
+            masonry: {
+                columnWidth: 13,
+                isFitWidth: true
+            },
+            getSortData: {
+                name: function( itemElem ) {
+                    var name = $( itemElem ).attr('data-name');
+                    % if not sickbeard.SORT_ARTICLE:
+                        return (name || '').replace(/^(The|A|An)\s/i,'');
+                    % else:
+                        return (name || '');
+                    % endif
+                },
+                network: '[data-network]',
+                date: function( itemElem ) {
+                    var date = $( itemElem ).attr('data-date');
+                    return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY;
+                },
+                progress: function( itemElem ) {
+                    var progress = $( itemElem ).attr('data-progress');
+                    return progress.length && parseInt( progress, 10 ) || Number.NEGATIVE_INFINITY;
+                }
+            }
+        });
+    });
+
+    $('#postersort').on( 'change', function() {
+        var sortValue = this.value;
+        $('#container').isotope({ sortBy: sortValue });
+        $('#container-anime').isotope({ sortBy: sortValue });
+        $.get(this.options[this.selectedIndex].getAttribute('data-sort'));
+    });
+
+    $('#postersortdirection').on( 'change', function() {
+        var sortDirection = this.value;
+        sortDirection = sortDirection == 'true';
+        $('#container').isotope({ sortAscending: sortDirection });
+        $('#container-anime').isotope({ sortAscending: sortDirection });
+        $.get(this.options[this.selectedIndex].getAttribute('data-sort'));
+    });
+
+    $('#popover')
+        .popover({
+          placement: 'bottom',
+          html: true, // required if content has HTML
+          content: '<div id="popover-target"></div>'
+        })
+        // bootstrap popover event triggered when the popover opens
+        .on('shown.bs.popover', function () {
+          // call this function to copy the column selection code into the popover
+          $.tablesorter.columnSelector.attachTo( $('#showListTableShows'), '#popover-target');
+          % if sickbeard.ANIME_SPLIT_HOME:
+          $.tablesorter.columnSelector.attachTo( $('#showListTableAnime'), '#popover-target');
+          % endif
+        });
+
+});
+</script>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div id="HomeLayout" class="pull-right" style="margin-top: -40px;">
+    % if layout != 'poster':
+        <button id="popover" type="button" class="btn btn-inline">Select Column</button>
+    % endif
+    <span> Layout:
+        <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+            <option value="${sbRoot}/setHomeLayout/?layout=poster" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'poster']}>Poster</option>
+            <option value="${sbRoot}/setHomeLayout/?layout=small" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'small']}>Small Poster</option>
+            <option value="${sbRoot}/setHomeLayout/?layout=banner" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'banner']}>Banner</option>
+            <option value="${sbRoot}/setHomeLayout/?layout=simple" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'simple']}>Simple</option>
+        </select>
+        % if layout != 'poster':
+        Search:
+            <input class="search form-control form-control-inline input-sm input200" type="search" data-column="2" placeholder="Search Show Name">
+            <button type="button" class="resetshows resetanime btn btn-inline">Reset Search</button>
+        % endif
+    </span>
+
+    % if layout == 'poster':
+    &nbsp;
+    <span> Sort By:
+        <select id="postersort" class="form-control form-control-inline input-sm">
+            <option value="name" data-sort="${sbRoot}/setPosterSortBy/?sort=name" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'name']}>Name</option>
+            <option value="date" data-sort="${sbRoot}/setPosterSortBy/?sort=date" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'date']}>Next Episode</option>
+            <option value="network" data-sort="${sbRoot}/setPosterSortBy/?sort=network" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'network']}>Network</option>
+            <option value="progress" data-sort="${sbRoot}/setPosterSortBy/?sort=progress" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'progress']}>Progress</option>
+        </select>
+    </span>
+    &nbsp;
+    <span> Sort Order:
+        <select id="postersortdirection" class="form-control form-control-inline input-sm">
+            <option value="true" data-sort="${sbRoot}/setPosterSortDir/?direction=1" ${('', 'selected="selected"')[sickbeard.POSTER_SORTDIR == 1]}>Asc</option>
+            <option value="false" data-sort="${sbRoot}/setPosterSortDir/?direction=0" ${('', 'selected="selected"')[sickbeard.POSTER_SORTDIR == 0]}>Desc</option>
+        </select>
+    </span>
+    &nbsp;
+
+    % endif
+</div>
+
+% for curShowlist in showlists:
+    <% curListType = curShowlist[0] %>
+    <% myShowList = list(curShowlist[1]) %>
+    % if curListType == "Anime":
+        <h1 class="header">Anime List</h1>
+    % endif
+% if layout == 'poster':
+<div id="${('container', 'container-anime')[curListType == 'Anime' and layout == 'poster']}" class="clearfix">
+<div class="posterview">
+% for curLoadingShow in sickbeard.showQueueScheduler.action.loadingShowList:
+    % if curLoadingShow.show == None:
+        <div class="show" data-name="0" data-date="010101" data-network="0" data-progress="101">
+            <img alt="" title="${curLoadingShow.show_name}" class="show-image" style="border-bottom: 1px solid #111;" src="${sbRoot}/images/poster.png" />
+            <div class="show-details">
+                <div class="show-add">Loading... (${curLoadingShow.show_name})</div>
+            </div>
+        </div>
+
+    % endif
+% endfor
+
+<% myShowList.sort(lambda x, y: cmp(x.name, y.name)) %>
+% for curShow in myShowList:
+
+<%
+    cur_airs_next = ''
+    cur_snatched = 0
+    cur_downloaded = 0
+    cur_total = 0
+    download_stat_tip = ''
+    display_status = curShow.status
+
+    if None is not display_status:
+        if re.search(r'(?i)(?:new|returning)\s*series', curShow.status):
+            display_status = 'Continuing'
+        elif re.search(r'(?i)(?:nded)', curShow.status):
+            display_status = 'Ended'
+
+    if curShow.indexerid in show_stat:
+        cur_airs_next = show_stat[curShow.indexerid]['ep_airs_next']
+
+        cur_snatched = show_stat[curShow.indexerid]['ep_snatched']
+        if not cur_snatched:
+            cur_snatched = 0
+
+        cur_downloaded = show_stat[curShow.indexerid]['ep_downloaded']
+        if not cur_downloaded:
+            cur_downloaded = 0
+
+        cur_total = show_stat[curShow.indexerid]['ep_total']
+        if not cur_total:
+            cur_total = 0
+
+    if cur_total != 0:
+        download_stat = str(cur_downloaded)
+        download_stat_tip = "Downloaded: " + str(cur_downloaded)
+        if cur_snatched > 0:
+            download_stat = download_stat
+            download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str(cur_snatched)
+
+        download_stat = download_stat + " / " + str(cur_total)
+        download_stat_tip = download_stat_tip + "&#013;" + "Total: " + str(cur_total)
+    else:
+        download_stat = '?'
+        download_stat_tip = "no data"
+
+    nom = cur_downloaded
+    den = cur_total
+    if den == 0:
+        den = 1
+
+    progressbar_percent = nom * 100 / den
+
+    data_date = '6000000000.0'
+    if cur_airs_next:
+        data_date = calendar.timegm(sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(cur_airs_next, curShow.airs, curShow.network)).timetuple())
+    elif None is not display_status:
+        if 'nded' not in display_status and 1 == int(curShow.paused):
+            data_date = '5000000500.0'
+        elif 'ontinu' in display_status:
+            data_date = '5000000000.0'
+        elif 'nded' in display_status:
+            data_date = '5000000100.0'
+%>
+    <div class="show" id="show${curShow.indexerid}" data-name="${curShow.name}" data-date="${data_date}" data-network="${curShow.network}" data-progress="${progressbar_percent}">
+        <div class="show-image">
+            <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}"><img alt="" class="show-image" src="${sbRoot}/showPoster/?show=${curShow.indexerid}&amp;which=poster_thumb" /></a>
+        </div>
+
+        <div id="progressbar${curShow.indexerid}"></div>
+            <script type="text/javascript">
+                $(function() {
+                    $("#progressbar${curShow.indexerid}").progressbar({
+                    value: ${progressbar_percent} });
+                    classvalue = ${progressbar_percent}
+                    if (classvalue<20) {
+                        classtoadd = "progress-20"
+                    }
+                    if (classvalue>=20 && classvalue<40) {
+                        classtoadd = "progress-40"
+                    }
+                    if (classvalue>=40 && classvalue<80) {
+                        classtoadd = "progress-60"
+                    }
+                    if (classvalue>=80 && classvalue<100) {
+                        classtoadd = "progress-80"
+                    }
+                    if (classvalue==100) {
+                        classtoadd = "progress-100"
+                    }
+                    $("#progressbar${curShow.indexerid} > .ui-progressbar-value").addClass(classtoadd);
+                });
+            </script>
+
+        <div class="show-title">
+            ${curShow.name}
+        </div>
+
+        <div class="show-date">
+% if cur_airs_next:
+    <% ldatetime = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(cur_airs_next, curShow.airs, curShow.network)) %>
+    <span class="${fuzzydate}">
+    <%
+        try:
+            out = str(sbdatetime.sbdatetime.sbfdate(ldatetime))
+        except ValueError:
+            out = 'Invalid date'
+            pass
+    %>
+        ${out}
+    </span>
+% else:
+    <%
+    output_html = '?'
+    if None is not display_status:
+        if 'nded' not in display_status and 1 == int(curShow.paused):
+          output_html = 'Paused'
+        elif display_status:
+            output_html = display_status
+    %>
+    ${output_html}
+% endif
+        </div>
+
+        <table width="100%" cellspacing="1" border="0" cellpadding="0">
+            <tr>
+                <td class="show-table">
+                    <span class="show-dlstats" title="${download_stat_tip}">${download_stat}</span>
+                </td>
+
+                <td class="show-table">
+                    % if layout != 'simple':
+                        % if curShow.network:
+                            <span title="${curShow.network}"><img class="show-network-image" src="${sbRoot}/showNetworkLogo/?show=${curShow.indexerid}" alt="${curShow.network}" title="${curShow.network}" /></span>
+                        % else:
+                            <span title="No Network"><img class="show-network-image" src="${sbRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span>
+                        % endif
+                    % else:
+                        <span title="${curShow.network}">${curShow.network}</span>
+                    % endif
+                </td>
+
+                <td class="show-table">
+                    % if curShow.quality in qualityPresets:
+                        <span class="show-quality">${qualityPresetStrings[curShow.quality]}</span>
+                    % else:
+                        <span class="show-quality">Custom</span>
+                    % endif
+                </td>
+            </tr>
+        </table>
+
+    </div>
+
+% endfor
+</div>
+</div>
+
+% else:
+
+<table id="showListTable${curListType}" class="tablesorter" cellspacing="1" border="0" cellpadding="0">
+
+    <thead>
+        <tr>
+            <th class="nowrap">Next Ep</th>
+            <th class="nowrap">Prev Ep</th>
+            <th>Show</th>
+            <th>Network</th>
+            <th>Quality</th>
+            <th>Downloads</th>
+            <th>Active</th>
+            <th>Status</th>
+        </tr>
+    </thead>
+
+    <tfoot>
+        <tr>
+            <th rowspan="1" colspan="1" align="center"><a href="${sbRoot}/home/addShows/">Add Show</a></th>
+            <th>&nbsp;</th>
+            <th>&nbsp;</th>
+            <th>&nbsp;</th>
+            <th>&nbsp;</th>
+            <th>&nbsp;</th>
+            <th>&nbsp;</th>
+            <th>&nbsp;</th>
+        </tr>
+    </tfoot>
+
+
+% if sickbeard.showQueueScheduler.action.loadingShowList:
+    <tbody class="tablesorter-infoOnly">
+% for curLoadingShow in sickbeard.showQueueScheduler.action.loadingShowList:
+
+    % if curLoadingShow.show != None and curLoadingShow.show in sickbeard.showList:
+         <% continue %>
+    % endif
+  <tr>
+    <td align="center">(loading)</td>
+    <td></td>
+    <td>
+    % if curLoadingShow.show == None:
+    <span title="">Loading... (${curLoadingShow.show_name})</span>
+    % else:
+    <a href="displayShow?show=${curLoadingShow.show.indexerid}">${curLoadingShow.show.name}</a>
+    % endif
+    </td>
+    <td></td>
+    <td></td>
+    <td></td>
+    <td></td>
+  </tr>
+% endfor
+    </tbody>
+% endif
+
+    <tbody>
+
+<% myShowList.sort(lambda x, y: cmp(x.name, y.name)) %>
+% for curShow in myShowList:
+
+<%
+    cur_airs_next = ''
+    cur_airs_prev = ''
+    cur_snatched = 0
+    cur_downloaded = 0
+    cur_total = 0
+    download_stat_tip = ''
+
+    if curShow.indexerid in show_stat:
+        cur_airs_next = show_stat[curShow.indexerid]['ep_airs_next']
+        cur_airs_prev = show_stat[curShow.indexerid]['ep_airs_prev']
+
+        cur_snatched = show_stat[curShow.indexerid]['ep_snatched']
+        if not cur_snatched:
+            cur_snatched = 0
+
+        cur_downloaded = show_stat[curShow.indexerid]['ep_downloaded']
+        if not cur_downloaded:
+            cur_downloaded = 0
+
+        cur_total = show_stat[curShow.indexerid]['ep_total']
+        if not cur_total:
+            cur_total = 0
+
+    if cur_total != 0:
+        download_stat = str(cur_downloaded)
+        download_stat_tip = "Downloaded: " + str(cur_downloaded)
+        if cur_snatched > 0:
+            download_stat = download_stat + "+" + str(cur_snatched)
+            download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str(cur_snatched)
+
+        download_stat = download_stat + " / " + str(cur_total)
+        download_stat_tip = download_stat_tip + "&#013;" + "Total: " + str(cur_total)
+    else:
+        download_stat = '?'
+        download_stat_tip = "no data"
+
+    nom = cur_downloaded
+    den = cur_total
+    if den == 0:
+        den = 1
+
+    progressbar_percent = nom * 100 / den
+%>
+    <tr>
+
+    % if cur_airs_next:
+        <% ldatetime = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(cur_airs_next, curShow.airs, curShow.network)) %>
+        % try:
+            <% temp_sbfdate_next = sbdatetime.sbdatetime.sbfdate(ldatetime) %>
+            <% temp_timegm_next = calendar.timegm(ldatetime.timetuple()) %>
+            <td align="center" class="nowrap">
+                <div class="${fuzzydate}">${temp_sbfdate_next}</div>
+                <span class="sort_data">${temp_timegm_next}</span>
+            </td>
+        % except ValueError:
+            <td align="center" class="nowrap"></td>
+        % endtry
+    % else:
+        <td align="center" class="nowrap"></td>
+    % endif
+
+    % if cur_airs_prev:
+        <% pdatetime = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(cur_airs_prev, curShow.airs, curShow.network)) %>
+        % try:
+            <% temp_sbfdate_prev = sbdatetime.sbdatetime.sbfdate(pdatetime) %>
+            <% temp_timegm_prev = calendar.timegm(pdatetime.timetuple()) %>
+            <td align="center" class="nowrap">
+                <div class="${fuzzydate}">${temp_sbfdate_prev}</div>
+                <span class="sort_data">${temp_timegm_prev}</span>
+            </td>
+        % except ValueError:
+            <td align="center" class="nowrap"></td>
+        % endtry
+    % else:
+        <td align="center" class="nowrap"></td>
+    % endif
+
+    % if layout == 'small':
+        <td class="tvShow">
+            <div class="imgsmallposter ${layout}">
+                <a href="${sbRoot}/showPoster/?show=${curShow.indexerid}&amp;which=${layout}" rel="dialog" title="${curShow.name}">
+                    <img src="${sbRoot}/showPoster/?show=${curShow.indexerid}&amp;which=poster_thumb" class="${layout}" alt="${curShow.indexerid}"/>
+                </a>
+                <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}" style="vertical-align: middle;">${curShow.name}</a>
+            </div>
+        </td>
+    % elif layout == 'banner':
+        <td>
+            <span style="display: none;">${curShow.name}</span>
+            <div class="imgbanner ${layout}">
+                <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}">
+                <img src="${sbRoot}/showPoster/?show=${curShow.indexerid}&amp;which=banner" class="${layout}" alt="${curShow.indexerid}" title="${curShow.name}"/>
+            </div>
+        </td>
+    % elif layout == 'simple':
+        <td class="tvShow"><a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}">${curShow.name}</a></td>
+    % endif
+
+    % if layout != 'simple':
+        <td align="center">
+        % if curShow.network:
+            <span title="${curShow.network}"><img id="network" width="54" height="27" src="${sbRoot}/showNetworkLogo/?show=${curShow.indexerid}" alt="${curShow.network}" title="${curShow.network}" /></span>
+        % else:
+            <span title="No Network"><img id="network" width="54" height="27" src="${sbRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span>
+        % endif
+        </td>
+    % else:
+        <td>
+            <span title="${curShow.network}">${curShow.network}</span>
+        </td>
+    % endif
+
+    % if curShow.quality in qualityPresets:
+        <td align="center"><span class="quality ${qualityPresetStrings[curShow.quality]}">${qualityPresetStrings[curShow.quality]}</span></td>
+    % else:
+        <td align="center"><span class="quality Custom">Custom</span></td>
+    % endif
+
+        <td align="center"><span style="display: none;">${download_stat}</span><div id="progressbar${curShow.indexerid}" style="position:relative;"></div>
+            <script type="text/javascript">
+                $(function() {
+                    $("#progressbar${curShow.indexerid}").progressbar({
+                    value: ${progressbar_percent} });
+                    $("#progressbar${curShow.indexerid}").append( "<div class='progressbarText' title='${download_stat_tip}'>${download_stat}</div>" )
+                    classvalue = ${progressbar_percent}
+                    if (classvalue<20) {
+                        classtoadd = "progress-20"
+                    }
+                    if (classvalue>=20 && classvalue<40) {
+                        classtoadd = "progress-40"
+                    }
+                    if (classvalue>=40 && classvalue<80) {
+                        classtoadd = "progress-60"
+                    }
+                    if (classvalue>=80 && classvalue<100) {
+                        classtoadd = "progress-80"
+                    }
+                    if (classvalue==100) {
+                        classtoadd = "progress-100"
+                    }
+                    $("#progressbar${curShow.indexerid} > .ui-progressbar-value").addClass(classtoadd);
+                });
+            </script>
+        </td>
+
+        <td align="center">
+% if sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT:
+            <img src="${sbRoot}/images/${('no16.png", alt="No"', 'yes16.png", alt="Yes"')[int(curShow.paused) == 0]} width="16" height="16" />
+% else:
+            <img src="${sbRoot}/images/${('no16.png", alt="No"', 'yes16.png", alt="Yes"')[int(curShow.paused) == 0 and curShow.status == 'Continuing']} width="16" height="16" />
+% endif
+        </td>
+
+        <td align="center">
+<% display_status = curShow.status %>
+% if None is not display_status:
+    % if re.search(r'(?i)(?:new|returning)\s*series', curShow.status):
+        <% display_status = 'Continuing' %>
+    % elif re.search(r'(?i)(?:nded)', curShow.status):
+        <% display_status = 'Ended' %>
+    % endif
+% endif
+
+        ${display_status}
+
+        </td>
+
+    </tr>
+
+% endfor
+</tbody>
+</table>
+
+% endif
+% endfor
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home.tmpl b/gui/slick/interfaces/default/home.tmpl
deleted file mode 100644
index dd186c78a912867c6bf7ffef4def729afa6197dc..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/home.tmpl
+++ /dev/null
@@ -1,881 +0,0 @@
-#import sickbeard
-#import calendar
-#import datetime
-#from sickbeard.common import *
-#from sickbeard import db, sbdatetime, network_timezones
-
-#set global $title="Home"
-#set global $header="Show List"
-
-#set global $sbPath = ".."
-
-#set global $topmenu="home"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#set $myDB = $db.DBConnection()
-#set $today = str($datetime.date.today().toordinal())
-#set $layout = $sickbeard.HOME_LAYOUT
-
-#set status_quality = '(' + ','.join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER]) + ')'
-#set status_download = '(' + ','.join([str(x) for x in $Quality.DOWNLOADED + [$ARCHIVED]]) + ')'
-
-#set $sql_statement = 'SELECT showid, '
-
-#set $sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE showid=tv_eps.showid AND season > 0 AND episode > 0 AND airdate > 1 AND status IN ' + $status_quality + ') AS ep_snatched, '
-#set $sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE showid=tv_eps.showid AND season > 0 AND episode > 0 AND airdate > 1 AND status IN ' + $status_download + ') AS ep_downloaded, '
-
-#set $sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE showid=tv_eps.showid AND season > 0 AND episode > 0 AND airdate > 1 '
-#set $sql_statement += ' AND ((airdate <= ' + $today + ' AND (status = ' + str($SKIPPED) + ' OR status = ' + str($WANTED) + ' OR status = ' + str($FAILED) + ')) '
-#set $sql_statement += ' OR (status IN ' + status_quality + ') OR (status IN ' + status_download + '))) AS ep_total, '
-
-#set $sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate >= ' + $today + ' AND (status = ' + str($UNAIRED) + ' OR status = ' + str($WANTED) + ') ORDER BY airdate ASC LIMIT 1) AS ep_airs_next, '
-#set $sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate > 1 AND status <> ' + str($UNAIRED) + ' ORDER BY airdate DESC LIMIT 1) AS ep_airs_prev '
-#set $sql_statement += ' FROM tv_episodes tv_eps GROUP BY showid'
-
-#set $sql_result = $myDB.select($sql_statement)
-
-#set $show_stat = {}
-#set $max_download_count = 1000
-
-#for $cur_result in $sql_result:
-    #set $show_stat[$cur_result['showid']] = $cur_result
-    #if $cur_result['ep_total'] > $max_download_count:
-        #set $max_download_count = $cur_result['ep_total']
-    #end if
-#end for
-
-#set $max_download_count = $max_download_count * 100
-
-<script type="text/javascript" charset="utf-8">
-<!--
-
-\$.tablesorter.addParser({
-    id: 'loadingNames',
-    is: function(s) {
-        return false;
-    },
-    format: function(s) {
-        if (s.indexOf('Loading...') == 0)
-          return s.replace('Loading...','000');
-        else
-        #if not $sickbeard.SORT_ARTICLE:
-            return (s || '').replace(/^(The|A|An)\s/i,'');
-        #else:
-            return (s || '');
-        #end if
-    },
-    type: 'text'
-});
-
-\$.tablesorter.addParser({
-    id: 'quality',
-    is: function(s) {
-        return false;
-    },
-    format: function(s) {
-        return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7);
-    },
-    type: 'numeric'
-});
-
-\$.tablesorter.addParser({
-    id: 'eps',
-    is: function(s) {
-        return false;
-    },
-    format: function(s) {
-        match = s.match(/^(.*)/);
-
-        if (match == null || match[1] == "?")
-          return -10;
-
-        var nums = match[1].split(" / ");
-        if (nums[0].indexOf("+") != -1) {
-            var num_parts = nums[0].split("+");
-            nums[0] = num_parts[0];
-        }
-
-        nums[0] = parseInt(nums[0])
-        nums[1] = parseInt(nums[1])
-
-        if (nums[0] === 0)
-          return nums[1];
-
-        var finalNum = parseInt($max_download_count*nums[0]/nums[1]);
-        var pct = Math.round((nums[0]/nums[1])*100) / 1000
-        if (finalNum > 0)
-          finalNum += nums[0];
-
-        return finalNum + pct;
-    },
-    type: 'numeric'
-});
-
-
-\$(document).ready(function(){
-
-    \$("img#network").on('error', function(){
-        \$(this).parent().text(\$(this).attr('alt'));
-        \$(this).remove();
-    });
-
-    \$("#showListTableShows:has(tbody tr)").tablesorter({
-        sortList: [[6,1],[2,0]],
-        textExtraction: {
-            0: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            1: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            3: function(node) { return \$(node).find("span").prop("title").toLowerCase(); },
-            4: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            5: function(node) { return \$(node).find("span").text(); },
-            6: function(node) { return \$(node).find("img").attr("alt"); }
-        },
-        widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'],
-        headers: {
-            0: { sorter: 'isoDate' },
-            1: { columnSelector: false },
-            2: { sorter: 'loadingNames' },
-            4: { sorter: 'quality' },
-            5: { sorter: 'eps' },
-            #if $sickbeard.FILTER_ROW:
-                6: { filter : 'parsed' }
-                #end if
-        },
-        widgetOptions : {
-            #if $sickbeard.FILTER_ROW:
-                filter_columnFilters: true,
-                filter_hideFilters : true,
-                filter_saveFilters : true,
-                filter_functions : {
-                   5:function(e, n, f, i, r, c) {
-                        var test = false;
-                        var pct = Math.floor((n % 1) * 1000);
-                        if (f === '') {
-                           test = true;
-                        } else {
-                            var result = f.match(/(<|<=|>=|>)\s(\d+)/i);
-                            if (result) {
-                                if (result[1] === "<") {
-                                    if (pct < parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                } else if (result[1] === "<=") {
-                                    if (pct <= parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                } else if (result[1] === ">=") {
-                                    if (pct >= parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                } else if (result[1] === ">") {
-                                    if (pct > parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                }
-                            }
-
-                            var result = f.match(/(\d+)\s(-|to)\s(\d+)/i);
-                            if (result) {
-                                if ((result[2] === "-") || (result[2] === "to")) {
-                                    if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) {
-                                        test = true;
-                                    }
-                                }
-                            }
-
-                            var result = f.match(/(=)?\s?(\d+)\s?(=)?/i);
-                            if (result) {
-                                if ((result[1] === "=") || (result[3] === "=")) {
-                                    if (parseInt(result[2]) === pct) {
-                                        test = true;
-                                    }
-                                }
-                            }
-
-                            if (!isNaN(parseFloat(f)) && isFinite(f)) {
-                                if (parseInt(f) === pct) {
-                                    test = true;
-                                }
-                            }
-                        }
-                        return test;
-                    },
-                },
-            #else
-                filter_columnFilters: false,
-            #end if
-            filter_reset: '.resetshows',
-            columnSelector_mediaquery: false,
-        },
-        sortStable: true,
-        sortAppend: [[2,0]]
-    });
-
-    \$("#showListTableAnime:has(tbody tr)").tablesorter({
-        sortList: [[6,1],[2,0]],
-        textExtraction: {
-            0: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            1: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            3: function(node) { return \$(node).find("span").prop("title").toLowerCase(); },
-            4: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            5: function(node) { return \$(node).find("span").text(); },
-            6: function(node) { return \$(node).find("img").attr("alt"); }
-        },
-        widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'],
-        headers: {
-            0: { sorter: 'isoDate' },
-            1: { columnSelector: false },
-            2: { sorter: 'loadingNames' },
-            4: { sorter: 'quality' },
-            5: { sorter: 'eps' },
-            #if $sickbeard.FILTER_ROW:
-            6: { filter : 'parsed' }
-            #end if
-        },
-        widgetOptions : {
-            #if $sickbeard.FILTER_ROW:
-                filter_columnFilters: true,
-                filter_hideFilters : true,
-                filter_saveFilters : true,
-                filter_functions : {
-                   5:function(e, n, f, i, r, c) {
-                        var test = false;
-                        var pct = Math.floor((n % 1) * 1000);
-                        if (f === '') {
-                           test = true;
-                        } else {
-                            var result = f.match(/(<|<=|>=|>)\s(\d+)/i);
-                            if (result) {
-                                if (result[1] === "<") {
-                                    if (pct < parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                } else if (result[1] === "<=") {
-                                    if (pct <= parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                } else if (result[1] === ">=") {
-                                    if (pct >= parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                } else if (result[1] === ">") {
-                                    if (pct > parseInt(result[2])) {
-                                        test = true;
-                                    }
-                                }
-                            }
-
-                            var result = f.match(/(\d+)\s(-|to)\s(\d+)/i);
-                            if (result) {
-                                if ((result[2] === "-") || (result[2] === "to")) {
-                                    if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) {
-                                        test = true;
-                                    }
-                                }
-                            }
-
-                            var result = f.match(/(=)?\s?(\d+)\s?(=)?/i);
-                            if (result) {
-                                if ((result[1] === "=") || (result[3] === "=")) {
-                                    if (parseInt(result[2]) === pct) {
-                                        test = true;
-                                    }
-                                }
-                            }
-
-                            if (!isNaN(parseFloat(f)) && isFinite(f)) {
-                                if (parseInt(f) === pct) {
-                                    test = true;
-                                }
-                            }
-                        }
-                        return test;
-                    },
-                },
-            #else
-                filter_columnFilters: false,
-            #end if
-            filter_reset: '.resetanime',
-            columnSelector_mediaquery: false,
-        },
-        sortStable: true,
-        sortAppend: [[2,0]]
-    });
-
-    if (\$("#showListTableShows").find("tbody").find("tr").size() > 0)
-        \$.tablesorter.filter.bindSearch( "#showListTableShows", \$('.search') );
-
-    #if $sickbeard.ANIME_SPLIT_HOME:
-        if (\$("#showListTableAnime").find("tbody").find("tr").size() > 0)
-            \$.tablesorter.filter.bindSearch( "#showListTableAnime", \$('.search') );
-    #end if
-
-    #set $fuzzydate = 'airdate'
-    #if $sickbeard.FUZZY_DATING:
-    fuzzyMoment({
-        dtInline : #if $layout == 'poster' then "true" else "false"#,
-        containerClass : '.${fuzzydate}',
-        dateHasTime : false,
-        dateFormat : '${sickbeard.DATE_PRESET}',
-        timeFormat : '${sickbeard.TIME_PRESET}',
-        trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#
-    });
-    #end if
-
-    var \$container = [\$('#container'), \$('#container-anime')];
-
-    jQuery.each(\$container, function (j) {
-        this.isotope({
-            itemSelector: '.show',
-            sortBy : '$sickbeard.POSTER_SORTBY',
-            sortAscending: $sickbeard.POSTER_SORTDIR,
-            layoutMode: 'masonry',
-            masonry: {
-                columnWidth: 13,
-                isFitWidth: true
-            },
-            getSortData: {
-                name: function( itemElem ) {
-                    var name = \$( itemElem ).attr('data-name');
-                    #if not $sickbeard.SORT_ARTICLE:
-                        return (name || '').replace(/^(The|A|An)\s/i,'');
-                    #else:
-                        return (name || '');
-                    #end if
-                },
-                network: '[data-network]',
-                date: function( itemElem ) {
-                    var date = \$( itemElem ).attr('data-date');
-                    return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY;
-                },
-                progress: function( itemElem ) {
-                    var progress = \$( itemElem ).attr('data-progress');
-                    return progress.length && parseInt( progress, 10 ) || Number.NEGATIVE_INFINITY;
-                }
-            }
-        });
-    });
-
-    \$('#postersort').on( 'change', function() {
-        var sortValue = this.value;
-        \$('#container').isotope({ sortBy: sortValue });
-        \$('#container-anime').isotope({ sortBy: sortValue });
-        \$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
-    });
-
-    \$('#postersortdirection').on( 'change', function() {
-        var sortDirection = this.value;
-        sortDirection = sortDirection == 'true';
-        \$('#container').isotope({ sortAscending: sortDirection });
-        \$('#container-anime').isotope({ sortAscending: sortDirection });
-        \$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
-    });
-
-    \$('#popover')
-        .popover({
-          placement: 'bottom',
-          html: true, // required if content has HTML
-          content: '<div id="popover-target"></div>'
-        })
-        // bootstrap popover event triggered when the popover opens
-        .on('shown.bs.popover', function () {
-          // call this function to copy the column selection code into the popover
-          \$.tablesorter.columnSelector.attachTo( \$('#showListTableShows'), '#popover-target');
-          #if $sickbeard.ANIME_SPLIT_HOME:
-            \$.tablesorter.columnSelector.attachTo( \$('#showListTableAnime'), '#popover-target');
-          #end if
-        });
-
-});
-
-//-->
-</script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<div id="HomeLayout" class="pull-right" style="margin-top: -40px;">
-    #if $layout != 'poster':
-        <button id="popover" type="button" class="btn btn-inline">Select Column</button>
-    #end if
-    <span> Layout:
-        <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-            <option value="$sbRoot/setHomeLayout/?layout=poster" #if $sickbeard.HOME_LAYOUT == "poster" then "selected=\"selected\"" else ""#>Poster</option>
-            <option value="$sbRoot/setHomeLayout/?layout=small" #if $sickbeard.HOME_LAYOUT == "small" then "selected=\"selected\"" else ""#>Small Poster</option>
-            <option value="$sbRoot/setHomeLayout/?layout=banner" #if $sickbeard.HOME_LAYOUT == "banner" then "selected=\"selected\"" else ""#>Banner</option>
-            <option value="$sbRoot/setHomeLayout/?layout=simple" #if $sickbeard.HOME_LAYOUT == "simple" then "selected=\"selected\"" else ""#>Simple</option>
-        </select>
-        #if $layout != 'poster':
-        Search:
-            <input class="search form-control form-control-inline input-sm input200" type="search" data-column="2" placeholder="Search Show Name">
-            <button type="button" class="resetshows resetanime btn btn-inline">Reset Search</button>
-        #end if
-    </span>
-
-    #if $layout == 'poster':
-    &nbsp;
-    <span> Sort By:
-        <select id="postersort" class="form-control form-control-inline input-sm">
-            <option value="name" data-sort="$sbRoot/setPosterSortBy/?sort=name" #if $sickbeard.POSTER_SORTBY == "name" then "selected=\"selected\"" else ""#>Name</option>
-            <option value="date" data-sort="$sbRoot/setPosterSortBy/?sort=date" #if $sickbeard.POSTER_SORTBY == "date" then "selected=\"selected\"" else ""#>Next Episode</option>
-            <option value="network" data-sort="$sbRoot/setPosterSortBy/?sort=network" #if $sickbeard.POSTER_SORTBY == "network" then "selected=\"selected\"" else ""#>Network</option>
-            <option value="progress" data-sort="$sbRoot/setPosterSortBy/?sort=progress" #if $sickbeard.POSTER_SORTBY == "progress" then "selected=\"selected\"" else ""#>Progress</option>
-        </select>
-    </span>
-    &nbsp;
-    <span> Sort Order:
-        <select id="postersortdirection" class="form-control form-control-inline input-sm">
-            <option value="true" data-sort="$sbRoot/setPosterSortDir/?direction=1" #if $sickbeard.POSTER_SORTDIR == 1 then "selected=\"selected\"" else ""#>Asc</option>
-            <option value="false" data-sort="$sbRoot/setPosterSortDir/?direction=0" #if $sickbeard.POSTER_SORTDIR == 0 then "selected=\"selected\"" else ""#>Desc</option>
-        </select>
-    </span>
-    &nbsp;
-
-    #end if
-</div>
-
-#for $curShowlist in $showlists:
-#set $curListType = $curShowlist[0]
-#set $myShowList = $list($curShowlist[1])
-#if $curListType == "Anime":
-<h1 class="header">Anime List</h1>
-#end if
-
-#if $layout == 'poster':
-<div id=#if $curListType == "Anime" and $layout == 'poster' then "container-anime" else "container"# class="clearfix">
-<div class="posterview">
-#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList:
-
-    #if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList:
-    #continue
-    #end if
-
-    #if $curLoadingShow.show == None:
-        <div class="show" data-name="0" data-date="010101" data-network="0" data-progress="101">
-            <img alt="" title="$curLoadingShow.show_name" class="show-image" style="border-bottom: 1px solid #111;" src="$sbRoot/images/poster.png" />
-            <div class="show-details">
-                <div class="show-add">Loading... ($curLoadingShow.show_name)</div>
-            </div>
-        </div>
-
-    #end if
-
-#end for
-
-$myShowList.sort(lambda x, y: cmp(x.name, y.name))
-#for $curShow in $myShowList:
-
-    #set $cur_airs_next = ''
-    #set $cur_snatched = 0
-    #set $cur_downloaded = 0
-    #set $cur_total = 0
-    #set $download_stat_tip = ''
-    #set $display_status = $curShow.status
-    #if None is not $display_status
-        #if re.search(r'(?i)(?:new|returning)\s*series', $curShow.status)
-            #set $display_status = 'Continuing'
-        #else if re.search(r'(?i)(?:nded)', $curShow.status)
-            #set $display_status = 'Ended'
-        #end if
-    #end if
-
-    #if $curShow.indexerid in $show_stat:
-        #set $cur_airs_next = $show_stat[$curShow.indexerid]['ep_airs_next']
-
-        #set $cur_snatched = $show_stat[$curShow.indexerid]['ep_snatched']
-        #if not $cur_snatched:
-            #set $cur_snatched = 0
-        #end if
-
-        #set $cur_downloaded = $show_stat[$curShow.indexerid]['ep_downloaded']
-        #if not $cur_downloaded:
-            #set $cur_downloaded = 0
-        #end if
-
-        #set $cur_total = $show_stat[$curShow.indexerid]['ep_total']
-        #if not $cur_total:
-            #set $cur_total = 0
-        #end if
-    #end if
-
-    #if $cur_total != 0:
-        #set $download_stat = str($cur_downloaded)
-        #set $download_stat_tip = "Downloaded: " + str($cur_downloaded)
-        #if $cur_snatched > 0:
-            #set $download_stat = download_stat
-            #set $download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str($cur_snatched)
-        #end if
-        #set $download_stat = download_stat + " / " + str($cur_total)
-        #set $download_stat_tip = download_stat_tip + "&#013;" + "Total: " + str($cur_total)
-    #else
-        #set $download_stat = '?'
-        #set $download_stat_tip = "no data"
-    #end if
-
-    #set $nom = $cur_downloaded
-    #set $den = $cur_total
-    #if $den == 0:
-        #set $den = 1
-    #end if
-
-    #set $progressbar_percent = $nom * 100 / $den
-
-#set $data_date = '6000000000.0'
-#if $cur_airs_next:
-    #set $data_date = $calendar.timegm($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple())
-#else if None is not $display_status
-    #if 'nded' not in $display_status and 1 == int($curShow.paused)
-        #set $data_date = '5000000500.0'
-    #else if 'ontinu' in $display_status
-        #set $data_date = '5000000000.0'
-    #else if 'nded' in $display_status
-        #set $data_date = '5000000100.0'
-    #end if
-#end if
-    <div class="show" id="show$curShow.indexerid" data-name="$curShow.name" data-date="$data_date" data-network="$curShow.network" data-progress="$progressbar_percent">
-        <div class="show-image">
-            <a href="$sbRoot/home/displayShow?show=$curShow.indexerid"><img alt="" class="show-image" src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=poster_thumb" /></a>
-        </div>
-
-        <div id="progressbar$curShow.indexerid"></div>
-            <script type="text/javascript">
-            <!--
-                \$(function() {
-                    \$("\#progressbar$curShow.indexerid").progressbar({
-                    value: $progressbar_percent });
-                    classvalue = $progressbar_percent
-                    if (classvalue<20) {
-                        classtoadd = "progress-20"
-                    }
-                    if (classvalue>=20 && classvalue<40) {
-                        classtoadd = "progress-40"
-                    }
-                    if (classvalue>=40 && classvalue<80) {
-                        classtoadd = "progress-60"
-                    }
-                    if (classvalue>=80 && classvalue<100) {
-                        classtoadd = "progress-80"
-                    }
-                    if (classvalue==100) {
-                        classtoadd = "progress-100"
-                    }
-                    \$("\#progressbar$curShow.indexerid > .ui-progressbar-value").addClass(classtoadd);
-                });
-            //-->
-            </script>
-
-        <div class="show-title">
-            $curShow.name
-        </div>
-
-        <div class="show-date">
-#if $cur_airs_next
-    #set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
-            <span class="${fuzzydate}">
-                #try
-                    $sbdatetime.sbdatetime.sbfdate($ldatetime)
-                #except ValueError
-                    Invalid date
-                #end try
-            </span>
-#else
-    #set $output_html = '?'
-    #if None is not $display_status
-        #if 'nded' not in $display_status and 1 == int($curShow.paused)
-            #set $output_html = 'Paused'
-        #else if $display_status
-            #set $output_html = $display_status
-        #end if
-    #end if
-    $output_html
-#end if
-        </div>
-
-        <table width="100%" cellspacing="1" border="0" cellpadding="0">
-            <tr>
-                <td class="show-table">
-                    <span class="show-dlstats" title="$download_stat_tip">$download_stat</span>
-                </td>
-
-                <td class="show-table">
-                    #if $layout != 'simple':
-                        #if $curShow.network:
-                            <span title="$curShow.network"><img class="show-network-image" src="$sbRoot/showNetworkLogo/?show=$curShow.indexerid" alt="$curShow.network" title="$curShow.network" /></span>
-                        #else:
-                            <span title="No Network"><img class="show-network-image" src="$sbRoot/images/network/nonetwork.png" alt="No Network" title="No Network" /></span>
-                        #end if
-                    #else:
-                        <span title="$curShow.network">$curShow.network</span>
-                    #end if
-                </td>
-
-                <td class="show-table">
-                    #if $curShow.quality in $qualityPresets:
-                        <span class="show-quality">$qualityPresetStrings[$curShow.quality]</span>
-                    #else:
-                        <span class="show-quality">Custom</span>
-                    #end if
-                </td>
-            </tr>
-        </table>
-
-    </div>
-
-
-
-#end for
-</div>
-</div>
-
-#else
-
-<table id="showListTable$curListType" class="tablesorter" cellspacing="1" border="0" cellpadding="0">
-
-    <thead>
-        <tr>
-            <th class="nowrap">Next Ep</th>
-            <th class="nowrap">Prev Ep</th>
-            <th>Show</th>
-            <th>Network</th>
-            <th>Quality</th>
-            <th>Downloads</th>
-            <th>Active</th>
-            <th>Status</th>
-        </tr>
-    </thead>
-
-    <tfoot>
-        <tr>
-            <th rowspan="1" colspan="1" align="center"><a href="$sbRoot/home/addShows/">Add Show</a></th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-        </tr>
-    </tfoot>
-
-
-#if $sickbeard.showQueueScheduler.action.loadingShowList
-    <tbody class="tablesorter-infoOnly">
-#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList:
-
-  #if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList:
-    #continue
-  #end if
-
-  <tr>
-    <td align="center">(loading)</td>
-    <td></td>
-    <td>
-    #if $curLoadingShow.show == None:
-    <span title="">Loading... ($curLoadingShow.show_name)</span>
-    #else:
-    <a href="displayShow?show=$curLoadingShow.show.indexerid">$curLoadingShow.show.name</a>
-    #end if
-    </td>
-    <td></td>
-    <td></td>
-    <td></td>
-    <td></td>
-  </tr>
-#end for
-    </tbody>
-#end if
-
-    <tbody>
-
-$myShowList.sort(lambda x, y: cmp(x.name, y.name))
-#for $curShow in $myShowList:
-
-    #set $cur_airs_next = ''
-    #set $cur_airs_prev = ''
-    #set $cur_snatched = 0
-    #set $cur_downloaded = 0
-    #set $cur_total = 0
-    #set $download_stat_tip = ''
-
-    #if $curShow.indexerid in $show_stat:
-        #set $cur_airs_next = $show_stat[$curShow.indexerid]['ep_airs_next']
-        #set $cur_airs_prev = $show_stat[$curShow.indexerid]['ep_airs_prev']
-
-
-        #set $cur_snatched = $show_stat[$curShow.indexerid]['ep_snatched']
-        #if not $cur_snatched:
-            #set $cur_snatched = 0
-        #end if
-
-        #set $cur_downloaded = $show_stat[$curShow.indexerid]['ep_downloaded']
-        #if not $cur_downloaded:
-            #set $cur_downloaded = 0
-        #end if
-
-        #set $cur_total = $show_stat[$curShow.indexerid]['ep_total']
-        #if not $cur_total:
-            #set $cur_total = 0
-        #end if
-    #end if
-
-    #if $cur_total != 0:
-        #set $download_stat = str($cur_downloaded)
-        #set $download_stat_tip = "Downloaded: " + str($cur_downloaded)
-        #if $cur_snatched > 0:
-            #set $download_stat = download_stat + "+" + str($cur_snatched)
-            #set $download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str($cur_snatched)
-        #end if
-        #set $download_stat = download_stat + " / " + str($cur_total)
-        #set $download_stat_tip = download_stat_tip + "&#013;" + "Total: " + str($cur_total)
-    #else
-        #set $download_stat = '?'
-        #set $download_stat_tip = "no data"
-    #end if
-
-    #set $nom = $cur_downloaded
-    #set $den = $cur_total
-    #if $den == 0:
-        #set $den = 1
-    #end if
-
-    #set $progressbar_percent = $nom * 100 / $den
-
-    <tr>
-
-    #if $cur_airs_next
-    #set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
-        #try
-            #set $temp_sbfdate_next = $sbdatetime.sbdatetime.sbfdate($ldatetime)
-            #set $temp_timegm_next = $calendar.timegm($ldatetime.timetuple())
-            <td align="center" class="nowrap">
-                <div class="${fuzzydate}">$temp_sbfdate_next</div>
-                <span class="sort_data">$temp_timegm_next</span>
-            </td>
-        #except ValueError
-            <td align="center" class="nowrap"></td>
-        #end try
-    #else:
-        <td align="center" class="nowrap"></td>
-    #end if
-
-    #if $cur_airs_prev
-    #set $pdatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_prev,$curShow.airs,$curShow.network))
-        #try
-            #set $temp_sbfdate_prev = $sbdatetime.sbdatetime.sbfdate($pdatetime)
-            #set $temp_timegm_prev = $calendar.timegm($pdatetime.timetuple())
-            <td align="center" class="nowrap">
-                <div class="${fuzzydate}">$temp_sbfdate_prev</div>
-                <span class="sort_data">$temp_timegm_prev</span>
-            </td>
-        #except ValueError
-            <td align="center" class="nowrap"></td>
-        #end try
-    #else:
-        <td align="center" class="nowrap"></td>
-    #end if
-
-    #if $layout == 'small':
-        <td class="tvShow">
-            <div class="imgsmallposter $layout">
-                <a href="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=$layout" rel="dialog" title="$curShow.name">
-                    <img src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=poster_thumb" class="$layout" alt="$curShow.indexerid"/>
-                </a>
-                <a href="$sbRoot/home/displayShow?show=$curShow.indexerid" style="vertical-align: middle;">$curShow.name</a>
-            </div>
-        </td>
-    #else if $layout == 'banner':
-        <td>
-            <span style="display: none;">$curShow.name</span>
-            <div class="imgbanner $layout">
-                <a href="$sbRoot/home/displayShow?show=$curShow.indexerid">
-                <img src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=banner" class="$layout" alt="$curShow.indexerid" title="$curShow.name"/>
-            </div>
-        </td>
-    #else if $layout == 'simple':
-        <td class="tvShow"><a href="$sbRoot/home/displayShow?show=$curShow.indexerid">$curShow.name</a></td>
-    #end if
-
-    #if $layout != 'simple':
-        <td align="center">
-        #if $curShow.network:
-            <span title="$curShow.network"><img id="network" width="54" height="27" src="$sbRoot/showNetworkLogo/?show=$curShow.indexerid" alt="$curShow.network" title="$curShow.network" /></span>
-        #else:
-            <span title="No Network"><img id="network" width="54" height="27" src="$sbRoot/images/network/nonetwork.png" alt="No Network" title="No Network" /></span>
-        #end if
-        </td>
-    #else:
-        <td>
-            <span title="$curShow.network">$curShow.network</span>
-        </td>
-    #end if
-
-    #if $curShow.quality in $qualityPresets:
-        <td align="center"><span class="quality $qualityPresetStrings[$curShow.quality]">$qualityPresetStrings[$curShow.quality]</span></td>
-    #else:
-        <td align="center"><span class="quality Custom">Custom</span></td>
-    #end if
-
-        <td align="center"><span style="display: none;">$download_stat</span><div id="progressbar$curShow.indexerid" style="position:relative;"></div>
-            <script type="text/javascript">
-            <!--
-                \$(function() {
-                    \$("\#progressbar$curShow.indexerid").progressbar({
-                    value: $progressbar_percent });
-                    \$("\#progressbar$curShow.indexerid").append( "<div class='progressbarText' title='$download_stat_tip'>$download_stat</div>" )
-                    classvalue = $progressbar_percent
-                    if (classvalue<20) {
-                        classtoadd = "progress-20"
-                    }
-                    if (classvalue>=20 && classvalue<40) {
-                        classtoadd = "progress-40"
-                    }
-                    if (classvalue>=40 && classvalue<80) {
-                        classtoadd = "progress-60"
-                    }
-                    if (classvalue>=80 && classvalue<100) {
-                        classtoadd = "progress-80"
-                    }
-                    if (classvalue==100) {
-                        classtoadd = "progress-100"
-                    }
-                    \$("\#progressbar$curShow.indexerid > .ui-progressbar-value").addClass(classtoadd);
-                });
-            //-->
-            </script>
-        </td>
-
-        <td align="center">
-#if sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT
-            <img src="$sbRoot/images/#if int($curShow.paused) == 0 then "yes16.png\" alt=\"Yes\"" else "no16.png\" alt=\"No\""# width="16" height="16" />
-#else
-            <img src="$sbRoot/images/#if int($curShow.paused) == 0 and $curShow.status == "Continuing" then "yes16.png\" alt=\"Yes\"" else "no16.png\" alt=\"No\""# width="16" height="16" />
-#end if
-        </td>
-
-        <td align="center">
-#set $display_status = $curShow.status
-#if None is not $display_status
-    #if re.search(r'(?i)(?:new|returning)\s*series', $curShow.status)
-        #set $display_status = 'Continuing'
-    #else if re.search(r'(?i)(?:nded)', $curShow.status)
-        #set $display_status = 'Ended'
-    #end if
-#end if
-
-        $display_status
-
-        </td>
-
-    </tr>
-
-#end for
-</tbody>
-</table>
-
-#end if
-#end for
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/home_addExistingShow.mako b/gui/slick/interfaces/default/home_addExistingShow.mako
new file mode 100644
index 0000000000000000000000000000000000000000..4a1c068562b2945bec2a06bb97a2c8e658c76a44
--- /dev/null
+++ b/gui/slick/interfaces/default/home_addExistingShow.mako
@@ -0,0 +1,68 @@
+<%!
+    import sickbeard
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/addExistingShow.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/addShowOptions.js?${sbPID}"></script>
+
+<script type="text/javascript" charset="utf-8">
+$(document).ready(function(){
+    $( "#tabs" ).tabs({
+        collapsible: true,
+        selected: '${('0', '-1')[bool(sickbeard.ROOT_DIRS)]}'
+    });
+});
+</script>
+
+% if not header is UNDEFINED:
+<h1 class="header">${header}</h1>
+% else:
+<h1 class="title">${title}</h1>
+% endif
+
+<div id="newShowPortal">
+    <div id="config-components">
+        <ul><li><a href="#core-component-group1">Add Existing Show</a></li></ul>
+
+    <div id="core-component-group1" class="tab-pane active component-group">
+
+    <form id="addShowForm" method="post" action="${sbRoot}/home/addShows/addNewShow" accept-charset="utf-8">
+
+    <div id="tabs">
+        <ul>
+            <li><a href="#tabs-1">Manage Directories</a></li>
+            <li><a href="#tabs-2">Customize Options</a></li>
+        </ul>
+        <div id="tabs-1" class="existingtabs">
+            <%include file="/inc_rootDirs.mako"/>
+        </div>
+        <div id="tabs-2" class="existingtabs">
+            <%include file="/inc_addShowOptions.mako"/>
+        </div>
+    </div>
+    <br />
+
+    <p>SickRage can add existing shows, using the current options, by using locally stored NFO/XML metadata to eliminate user interaction.
+    If you would rather have SickRage prompt you to customize each show, then use the checkbox below.</p>
+
+    <p><input type="checkbox" name="promptForSettings" id="promptForSettings" /> <label for="promptForSettings">Prompt me to set settings for each show</label></p>
+
+    <hr />
+
+    <p><b>Displaying folders within these directories which aren't already added to SickRage:</b></p>
+
+    <ul id="rootDirStaticList"><li></li></ul>
+    <br />
+    <div id="tableDiv"></div>
+    <br />
+    <br />
+    <input class="btn btn-primary" type="button" value="Submit" id="submitShowDirs" />
+
+    </form>
+
+    </div>
+    </div>
+</div>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home_addExistingShow.tmpl b/gui/slick/interfaces/default/home_addExistingShow.tmpl
deleted file mode 100644
index dabf106c0cc7a821eb1cb3994bff25ddbca83921..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/home_addExistingShow.tmpl
+++ /dev/null
@@ -1,83 +0,0 @@
-#import os.path
-#import sickbeard
-#from sickbeard.common import *
-#set global $title="Existing Show"
-#set global $header="Existing Show"
-
-#set global $sbPath="../.."
-
-#set global $statpath="../.."#
-#set global $topmenu="home"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/addExistingShow.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?$sbPID"></script>
-
-<script type="text/javascript" charset="utf-8">
-<!--
-\$(document).ready(function(){
-    \$( "#tabs" ).tabs({
-        collapsible: true,
-        selected: #if $sickbeard.ROOT_DIRS then '-1' else '0'#
-    });
-});
-//-->
-</script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<div id="newShowPortal">
-    <div id="config-components">
-        <ul>
-            <li><a href="#core-component-group1">Add Existing Show</a></li>
-        </ul>
-
-    <div id="core-component-group1" class="tab-pane active component-group">
-
-    <form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
-
-    <div id="tabs">
-        <ul>
-            <li><a href="#tabs-1">Manage Directories</a></li>
-            <li><a href="#tabs-2">Customize Options</a></li>
-        </ul>
-        <div id="tabs-1" class="existingtabs">
-            #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl")
-        </div>
-        <div id="tabs-2" class="existingtabs">
-            #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl")
-        </div>
-    </div>
-    <br />
-
-    <p>SickRage can add existing shows, using the current options, by using locally stored NFO/XML metadata to eliminate user interaction.
-    If you would rather have SickRage prompt you to customize each show, then use the checkbox below.</p>
-
-    <p><input type="checkbox" name="promptForSettings" id="promptForSettings" /> <label for="promptForSettings">Prompt me to set settings for each show</label></p>
-
-    <hr />
-
-    <p><b>Displaying folders within these directories which aren't already added to SickRage:</b></p>
-
-    <ul id="rootDirStaticList"><li></li></ul>
-
-    <br />
-    <div id="tableDiv"></div>
-    <br />
-    <br />
-    <input class="btn btn-primary" type="button" value="Submit" id="submitShowDirs" />
-
-    </form>
-
-    </div>
-    </div>
-</div>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/home_addShows.tmpl b/gui/slick/interfaces/default/home_addShows.mako
similarity index 57%
rename from gui/slick/interfaces/default/home_addShows.tmpl
rename to gui/slick/interfaces/default/home_addShows.mako
index da8bd0a9fdb9c263b3a4c52ad7f6dcd3ff0a8b71..cbc02afb208e4b57be0cee9d41c26857c29b714b 100644
--- a/gui/slick/interfaces/default/home_addShows.tmpl
+++ b/gui/slick/interfaces/default/home_addShows.mako
@@ -1,26 +1,17 @@
-#import os.path
-#import urllib
-#import sickbeard
-#set global $title="Add Show"
-#set global $header="Add Show"
-
-#set global $sbPath="../.."
-
-#set global $statpath="../.."#
-#set global $topmenu="home"#
-#import os.path
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+<%!
+    import os.path
+    import urllib
+    import sickbeard
+%>
+<%include file="/inc_top.mako"/>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="addShowPortal">
-
-    <a href="$sbRoot/home/addShows/newShow/" id="btnNewShow" class="btn btn-large">
+    <a href="${sbRoot}/home/addShows/newShow/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addnewshow"></div></div>
         <div class="buttontext">
             <h3>Add New Show</h3>
@@ -29,8 +20,8 @@
     </a>
 
     <br/><br/>
-    #if $sickbeard.USE_TRAKT == True:
-    <a href="$sbRoot/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
+    % if sickbeard.USE_TRAKT == True:
+    <a href="${sbRoot}/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addtrendingshow"></div></div>
         <div class="buttontext">
             <h3>Add Trending Show</h3>
@@ -40,7 +31,7 @@
 
     <br/><br/>
 
-    <a href="$sbRoot/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
+    <a href="${sbRoot}/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addrecommendedshow"></div></div>
         <div class="buttontext">
             <h3>Add Recommended Shows</h3>
@@ -49,8 +40,23 @@
     </a>
 
     <br/><br/>
-    #end if
-    <a href="$sbRoot/home/addShows/existingShows/" id="btnExistingShow" class="btn btn-large">
+    % endif
+
+
+    % if sickbeard.USE_IMDB_POPULAR == True:
+    <a href="${sbRoot}/home/addShows/popularShows/" id="btnNewShow" class="btn btn-large">
+        <div class="button"><div class="icon-addtrendingshow"></div></div>
+        <div class="buttontext">
+            <h3>View Popular Shows</h3>
+            <p>View IMDB's list of the most popular shows. This feature uses IMDB's MOVIEMeter algorithm to identify popular TV Series.</p>
+        </div>
+    </a>
+
+    <br/><br/>
+    % endif
+
+
+    <a href="${sbRoot}/home/addShows/existingShows/" id="btnExistingShow" class="btn btn-large">
         <div class="button"><div class="icon-addexistingshow"></div></div>
         <div class="buttontext">
             <h3>Add Existing Shows</h3>
@@ -61,4 +67,4 @@
 </div>
 
 
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home_massAddTable.mako b/gui/slick/interfaces/default/home_massAddTable.mako
new file mode 100644
index 0000000000000000000000000000000000000000..5e249239d518e4e59e6ecc4731631db011272242
--- /dev/null
+++ b/gui/slick/interfaces/default/home_massAddTable.mako
@@ -0,0 +1,43 @@
+<%!
+    import sickbeard
+    from sickbeard.helpers import anon_url
+%>
+
+<table id="addRootDirTable" class="sickbeardTable tablesorter">
+    <thead><tr><th class="col-checkbox"><input type="checkbox" id="checkAll" checked=checked></th><th>Directory</th><th width="20%">Show Name (tvshow.nfo)<th width="20%">Indexer</td></tr></thead>
+    <tbody>
+% for curDir in dirList:
+    <%
+        if curDir['added_already']:
+            continue
+
+        show_id = curDir['dir']
+        if curDir['existing_info'][0]:
+            show_id = show_id + '|' + str(curDir['existing_info'][0]) + '|' + str(curDir['existing_info'][1])
+            indexer = curDir['existing_info'][2]
+
+        indexer = 0
+        if curDir['existing_info'][0]:
+            indexer = curDir['existing_info'][2]
+        elif sickbeard.INDEXER_DEFAULT > 0:
+            indexer = sickbeard.INDEXER_DEFAULT
+    %>
+    <tr>
+        <td class="col-checkbox"><input type="checkbox" id="${show_id}" class="dirCheck" checked=checked></td>
+        <td><label for="${show_id}">${curDir['display_dir']}</label></td>
+        % if curDir['existing_info'][1] and indexer > 0:
+            <td><a href="${anon_url(sickbeard.indexerApi(indexer).config['show_url'], curDir['existing_info'][0])}">${curDir['existing_info'][1]}</a></td>
+        % else:
+            <td>?</td>
+        % endif
+        <td align="center">
+            <select name="indexer">
+                % for curIndexer in sickbeard.indexerApi().indexers.iteritems():
+                    <option value="${curIndexer[0]}" ${('', 'selected="selected"')[curIndexer[0] == indexer]}>${curIndexer[1]}</option>
+                % endfor
+            </select>
+        </td>
+    </tr>
+% endfor
+    </tbody>
+</table>
diff --git a/gui/slick/interfaces/default/home_massAddTable.tmpl b/gui/slick/interfaces/default/home_massAddTable.tmpl
deleted file mode 100644
index c302351225438c4da09ea26c5bf20a7b87b3c903..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/home_massAddTable.tmpl
+++ /dev/null
@@ -1,44 +0,0 @@
-#import sickbeard
-#from sickbeard.helpers import anon_url
-
-<table id="addRootDirTable" class="sickbeardTable tablesorter">
-  <thead><tr><th class="col-checkbox"><input type="checkbox" id="checkAll" checked=checked></th><th>Directory</th><th width="20%">Show Name (tvshow.nfo)<th width="20%">Indexer</td></tr></thead>
-  <tbody>
-#for $curDir in $dirList:
-#if $curDir['added_already']:
-#continue
-#end if
-
-#set $show_id = $curDir['dir']
-#if $curDir['existing_info'][0]:
-#set $show_id = $show_id + '|' + $str($curDir['existing_info'][0]) + '|' + $str($curDir['existing_info'][1])
-#set $indexer = $curDir['existing_info'][2]
-#end if
-
-#set $indexer = 0
-#if $curDir['existing_info'][0]:
-    #set $indexer = $curDir['existing_info'][2]
-#elif $sickbeard.INDEXER_DEFAULT > 0:
-    #set $indexer = $sickbeard.INDEXER_DEFAULT
-#end if
-
-  <tr>
-    <td class="col-checkbox"><input type="checkbox" id="$show_id" class="dirCheck" checked=checked></td>
-    <td><label for="$show_id">$curDir['display_dir']</label></td>
-    #if $curDir['existing_info'][1] and $indexer > 0:
-        <td><a href="<%= anon_url(sickbeard.indexerApi(indexer).config['show_url'], curDir['existing_info'][0]) %>">$curDir['existing_info'][1]</a></td>
-    #else:
-        <td>?</td>
-    #end if
-    <td align="center">
-        <select name="indexer">
-            #for $curIndexer in $sickbeard.indexerApi().indexers.items():
-                <option value="$curIndexer[0]" #if $curIndexer[0] == $indexer then "selected=\"selected\"" else "UNKNOWN"#>$curIndexer[1]</option>
-            #end for
-        </select>
-    </td>
-  </tr>
-#end for
-  </tbody>
-</tbody>
-</table>
\ No newline at end of file
diff --git a/gui/slick/interfaces/default/home_newShow.mako b/gui/slick/interfaces/default/home_newShow.mako
new file mode 100644
index 0000000000000000000000000000000000000000..3916f9a55d317c19470de9c0c55101bde6030327
--- /dev/null
+++ b/gui/slick/interfaces/default/home_newShow.mako
@@ -0,0 +1,112 @@
+<%!
+    import sickbeard
+    from sickbeard.helpers import anon_url
+%>
+
+<%include file="/inc_top.mako"/>
+
+<script type="text/javascript" src="${sbRoot}/js/formwizard.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/newShow.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/addShowOptions.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div id="newShowPortal">
+    <div id="config-components">
+        <ul>
+            <li><a href="#core-component-group1">Add New Show</a></li>
+        </ul>
+
+    <div id="core-component-group1" class="tab-pane active component-group">
+
+    <div id="displayText">aoeu</div>
+    <br />
+
+    <form id="addShowForm" method="post" action="${sbRoot}/home/addShows/addNewShow" accept-charset="utf-8">
+
+    <fieldset class="sectionwrap">
+        <legend class="legendStep">Find a show on the TVDB or TVRAGE</legend>
+
+        <div class="stepDiv">
+            <input type="hidden" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" />
+
+            % if use_provided_info:
+                Show retrieved from existing metadata: <a href="${anon_url(sickbeard.indexerApi(provided_indexer).config['show_url'], provided_indexer_id)}">${provided_indexer_name}</a>
+                <input type="hidden" id="indexerLang" name="indexerLang" value="en" />
+                <input type="hidden" id="whichSeries" name="whichSeries" value="${provided_indexer_id}" />
+                <input type="hidden" id="providedIndexer" name="providedIndexer" value="${provided_indexer}" />
+                <input type="hidden" id="providedName" value="${provided_indexer_name}" />
+            % else:
+                <input type="text" id="nameToSearch" value="${default_show_name}" class="form-control form-control-inline input-sm input350" />
+                &nbsp;&nbsp;
+                <select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm bfh-languages" data-language="${sickbeard.INDEXER_DEFAULT_LANGUAGE}" data-available="${','.join(sickbeard.indexerApi().config['valid_languages'])}">
+                </select><b>*</b>
+                &nbsp;
+                <select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
+                    <option value="0" ${('', 'selected="selected"')[provided_indexer == 0]}>All Indexers</option>
+                    % for indexer in indexers:
+                        <option value="${indexer}" ${('', 'selected="selected"')[provided_indexer == indexer]}>
+                            ${(indexers[indexer], ''.join((indexers[indexer], ' **')))[indexers[indexer] == 'TVRage']}
+                        </option>
+                    % endfor
+                </select>
+                &nbsp;
+                <input class="btn btn-inline" type="button" id="searchName" value="Search" />
+
+                <br /><br />
+                <b>*</b> This will only affect the language of the retrieved metadata file contents and episode filenames.<br />
+                This <b>DOES NOT</b> allow SickRage to download non-english TV episodes!<br />
+                <b>** IMPORTANT: </b> TVRAGE indexer implementation doesn't currently support <b>specials</b> and <b>banners/posters</b>.<br />
+                <br />
+                <div id="searchResults" style="height: 100%;"><br/></div>
+            % endif
+
+        </div>
+    </fieldset>
+
+    <fieldset class="sectionwrap">
+        <legend class="legendStep">Pick the parent folder</legend>
+
+        <div class="stepDiv">
+            % if provided_show_dir:
+                Pre-chosen Destination Folder: <b>${provided_show_dir}</b> <br />
+                <input type="hidden" id="fullShowPath" name="fullShowPath" value="${provided_show_dir}" /><br />
+            % else:
+                <%include file="/inc_rootDirs.mako"/>
+            % endif
+        </div>
+    </fieldset>
+
+    <fieldset class="sectionwrap">
+        <legend class="legendStep">Customize options</legend>
+            <div class="stepDiv">
+                <%include file="/inc_addShowOptions.mako"/>
+            </div>
+    </fieldset>
+
+    % for curNextDir in other_shows:
+    <input type="hidden" name="other_shows" value="${curNextDir}" />
+    % endfor
+    <input type="hidden" name="skipShow" id="skipShow" value="" />
+    </form>
+
+<br />
+
+<div style="width: 100%; text-align: center;">
+<input class="btn" type="button" id="addShowButton" value="Add Show" disabled="disabled" />
+% if provided_show_dir:
+<input class="btn" type="button" id="skipShowButton" value="Skip Show" />
+% endif
+</div>
+
+<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/blackwhite.js?${sbPID}"></script>
+
+</div></div></div></div>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home_newShow.tmpl b/gui/slick/interfaces/default/home_newShow.tmpl
deleted file mode 100644
index 05f98857a9cf89d26c9bc79c976552d816c60b6e..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/home_newShow.tmpl
+++ /dev/null
@@ -1,121 +0,0 @@
-#import os.path
-#import sickbeard
-#from sickbeard.helpers import anon_url
-
-#set global $header="New Show"
-#set global $title="New Show"
-
-#set global $sbPath="../.."
-
-#set global $statpath="../.."#
-#set global $topmenu="home"#
-#import os.path
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/formwizard.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/newShow.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/lib/bootstrap-formhelpers.min-2.3.0.js?$sbPID"></script>
-
-   #if $varExists('header')
-       <h1 class="header">$header</h1>
-   #else
-       <h1 class="title">$title</h1>
-   #end if
-
-<div id="newShowPortal">
-    <div id="config-components">
-        <ul>
-            <li><a href="#core-component-group1">Add New Show</a></li>
-        </ul>
-
-    <div id="core-component-group1" class="tab-pane active component-group">
-
-    <div id="displayText">aoeu</div>
-    <br />
-
-    <form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
-
-    <fieldset class="sectionwrap">
-        <legend class="legendStep">Find a show on the TVDB or TVRAGE</legend>
-
-        <div class="stepDiv">
-            <input type="hidden" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" />
-
-            #if $use_provided_info:
-                Show retrieved from existing metadata: <a href="$anon_url($sickbeard.indexerApi($provided_indexer).config['show_url'], $provided_indexer_id)">$provided_indexer_name</a>
-                <input type="hidden" id="indexerLang" name="indexerLang" value="en" />
-                <input type="hidden" id="whichSeries" name="whichSeries" value="$provided_indexer_id" />
-                <input type="hidden" id="providedIndexer" name="providedIndexer" value="$provided_indexer" />
-                <input type="hidden" id="providedName" value="$provided_indexer_name" />
-            #else:
-
-                <input type="text" id="nameToSearch" value="$default_show_name" class="form-control form-control-inline input-sm input350" />
-                &nbsp;&nbsp;
-                <select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm bfh-languages" data-language="#echo $sickbeard.INDEXER_DEFAULT_LANGUAGE#" data-available="#echo ','.join($sickbeard.indexerApi().config['valid_languages'])#">
-                </select><b>*</b>
-                &nbsp;
-                <select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
-                    <option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option>
-                    #for $indexer in $indexers
-                        <option value="$indexer" #if $provided_indexer == $indexer then "selected=\"selected\"" else ""#>
-                        #if $indexers[$indexer] == 'TVRage' then ''.join(($indexers[$indexer], ' **')) else $indexers[$indexer]#</option>
-                    #end for
-                </select>
-                &nbsp;
-                <input class="btn btn-inline" type="button" id="searchName" value="Search" />
-
-                <br /><br />
-                <b>*</b> This will only affect the language of the retrieved metadata file contents and episode filenames.<br />
-                This <b>DOES NOT</b> allow SickRage to download non-english TV episodes!<br />
-                <b>** IMPORTANT: </b> TVRAGE indexer implementation doesn't currently support <b>specials</b> and <b>banners/posters</b>.<br />
-                <br />
-                <div id="searchResults" style="height: 100%;"><br/></div>
-            #end if
-
-        </div>
-    </fieldset>
-
-    <fieldset class="sectionwrap">
-        <legend class="legendStep">Pick the parent folder</legend>
-
-        <div class="stepDiv">
-            #if $provided_show_dir:
-                Pre-chosen Destination Folder: <b>$provided_show_dir</b> <br />
-                <input type="hidden" id="fullShowPath" name="fullShowPath" value="$provided_show_dir" /><br />
-            #else
-                #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl")
-            #end if
-        </div>
-    </fieldset>
-
-    <fieldset class="sectionwrap">
-        <legend class="legendStep">Customize options</legend>
-            <div class="stepDiv">
-                #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl")
-            </div>
-    </fieldset>
-
-    #for $curNextDir in $other_shows:
-    <input type="hidden" name="other_shows" value="$curNextDir" />
-    #end for
-    <input type="hidden" name="skipShow" id="skipShow" value="" />
-    </form>
-
-<br />
-
-<div style="width: 100%; text-align: center;">
-<input class="btn" type="button" id="addShowButton" value="Add Show" disabled="disabled" />
-#if $provided_show_dir:
-<input class="btn" type="button" id="skipShowButton" value="Skip Show" />
-#end if
-</div>
-
-<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/blackwhite.js?$sbPID"></script>
-
-</div></div></div></div>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/home_popularShows.mako b/gui/slick/interfaces/default/home_popularShows.mako
new file mode 100644
index 0000000000000000000000000000000000000000..9519cf5fa7fcac89f10c9862157c1298f049fb27
--- /dev/null
+++ b/gui/slick/interfaces/default/home_popularShows.mako
@@ -0,0 +1,34 @@
+<% from sickbeard.helpers import anon_url %>
+
+<%include file="/inc_top.mako"/>
+
+% if not popular_shows:
+    <h3>Fetching of IMDB Data failed. Are you online?</h3>
+
+% else:
+    % for cur_result in popular_shows:
+        <div class="popularShow">
+            <div class="left">
+                <img class="coverImage" src="${cur_result['image_url_large']}" />
+            </div>
+            <div class="right">
+                <h3>${cur_result['name']}</h3>
+
+                % if 'rating' in cur_result and cur_result['rating']:
+                    <span class="rating">${cur_result['rating']}/10 (${cur_result['votes']})</span>
+                % endif
+
+                <p>${cur_result['outline']}<span class="year"> - Released ${cur_result['year']}<span></p>
+                <span class="imdb_url"><a href="${anon_url(cur_result['imdb_url'])}">View on IMDB</a></span>&nbsp;&nbsp;|&nbsp;&nbsp;
+                <span class="imdb_sickrage_search"><a href="/home/addShows/newShow/?search_string=${cur_result['name']}">
+                    Add Show</a></span>
+
+            </div>
+            <br style="clear:both" />
+
+        </div>
+    % endfor
+% endif
+
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home_postprocess.tmpl b/gui/slick/interfaces/default/home_postprocess.mako
similarity index 68%
rename from gui/slick/interfaces/default/home_postprocess.tmpl
rename to gui/slick/interfaces/default/home_postprocess.mako
index 8a7e3b41660d4d482927e24fc3d0cf19c22ccd80..e57294027480689ef3f66bafebc6a27d4934d0ae 100644
--- a/gui/slick/interfaces/default/home_postprocess.tmpl
+++ b/gui/slick/interfaces/default/home_postprocess.mako
@@ -1,18 +1,13 @@
-#import sickbeard
-#set global $header="Post Processing"
-#set global $title="Post Processing"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="home"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
+<%
+    import sickbeard
+%>
+<%include file="/inc_top.mako"/>
 <div id="content800">
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="postProcess">
     <form name="processForm" method="post" action="processEpisode" style="line-height: 40px;">
@@ -32,15 +27,10 @@
             </td>
             <td>
                 <select name="process_method" id="process_method" class="form-control form-control-inline input-sm" >
-                #set $process_method_text = {'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"}
-                #for $curAction in ('copy', 'move', 'hardlink', 'symlink'):
-                #if $sickbeard.PROCESS_METHOD == $curAction:
-                    #set $process_method = "selected=\"selected\""
-                #else
-                    #set $process_method = ""
-                #end if
-                <option value="$curAction" $process_method>$process_method_text[$curAction]</option>
-                #end for
+                <% process_method_text = {'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"} %>
+                % for curAction in ('copy', 'move', 'hardlink', 'symlink'):
+                    <option value="${curAction}" ${('', 'selected="selected"')[sickbeard.PROCESS_METHOD == curAction]}>${process_method_text[curAction]}</option>
+                % endfor
                 </select>
             </td>
         </tr>
@@ -70,7 +60,7 @@
                 <span style="line-height: 0; font-size: 12px;"><i>&nbsp;(Check it to delete files and folders like auto processing)</i></span>
             </td>
         </tr>
-        #if $sickbeard.USE_FAILED_DOWNLOADS:
+        % if sickbeard.USE_FAILED_DOWNLOADS:
         <tr>
             <td>
                 <b>Mark download as failed:</b>
@@ -79,16 +69,14 @@
                 <input id="failed" name="failed" type="checkbox">
             </td>
         </tr>
-        #end if
+        % endif
     </table>
         <input id="submit" class="btn" type="submit" value="Process" />
     </form>
 
 <script type="text/javascript" charset="utf-8">
-<!--
     jQuery('#episodeDir').fileBrowser({ title: 'Select Unprocessed Episode Folder', key: 'postprocessPath' });
-//-->
 </script>
 </div>
 
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
\ No newline at end of file
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home_trendingShows.tmpl b/gui/slick/interfaces/default/home_recommendedShows.mako
similarity index 58%
rename from gui/slick/interfaces/default/home_trendingShows.tmpl
rename to gui/slick/interfaces/default/home_recommendedShows.mako
index 03a25bcbae54936af88ca26dc02c4a829fa4b88f..be633c5e3ce17c998375095faee14604a6241412 100644
--- a/gui/slick/interfaces/default/home_trendingShows.tmpl
+++ b/gui/slick/interfaces/default/home_recommendedShows.mako
@@ -1,48 +1,34 @@
-#import sickbeard
-#import datetime
-#import re
-#from sickbeard.common import *
-#from sickbeard import sbdatetime
-#from sickbeard.helpers import anon_url
-
-#set global $title='Trending Shows'
-#set global $header='Trending Shows'
-
-#set global $sbPath='..'
-
-#set global $topmenu='home'
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
-
-<script type="text/javascript" src="$sbRoot/js/addTrendingShow.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
+<%!
+    import sickbeard
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/recommendedShows.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script>
 
 <script type="text/javascript" charset="utf-8">
-<!--
-
-\$(document).ready(function(){
-    \$( "#tabs" ).tabs({
+$(document).ready(function(){
+    $( "#tabs" ).tabs({
         collapsible: true,
-        selected: #if $sickbeard.ROOT_DIRS then '-1' else '0'#
+        selected: ${('0', '-1')[bool(sickbeard.ROOT_DIRS)]}
     });
 
     // initialise combos for dirty page refreshes
-    \$('#showsort').val('original');
-    \$('#showsortdirection').val('asc');
+    $('#showsort').val('original');
+    $('#showsortdirection').val('asc');
 
-    var \$container = [\$('#container')];
-    jQuery.each(\$container, function (j) {
+    var $container = [$('#container')];
+    jQuery.each($container, function (j) {
         this.isotope({
             itemSelector: '.trakt_show',
             sortBy: 'original-order',
             layoutMode: 'fitRows',
             getSortData: {
                 name: function( itemElem ) {
-                    var name = \$( itemElem ).attr('data-name') || '';
-#if not $sickbeard.SORT_ARTICLE:
+                    var name = $( itemElem ).attr('data-name') || '';
+% if not sickbeard.SORT_ARTICLE:
                     name = name.replace(/^(The|A|An)\s/i, '');
-#end if
+% endif
                     return name.toLowerCase();
                 },
                 rating: '[data-rating] parseInt',
@@ -51,7 +37,7 @@
         });
     });
 
-    \$('#showsort').on( 'change', function() {
+    $('#showsort').on( 'change', function() {
         var sortCriteria;
         switch (this.value) {
             case 'original':
@@ -61,7 +47,7 @@
                 /* randomise, else the rating_votes can already
                  * have sorted leaving this with nothing to do.
                  */
-                \$('#container').isotope({sortBy: 'random'});
+                $('#container').isotope({sortBy: 'random'});
                 sortCriteria = 'rating';
                 break;
             case 'rating_votes':
@@ -74,22 +60,20 @@
                 sortCriteria = 'name'
                 break;
         }
-        \$('#container').isotope({sortBy: sortCriteria});
+        $('#container').isotope({sortBy: sortCriteria});
     });
 
-    \$('#showsortdirection').on( 'change', function() {
-        \$('#container').isotope({sortAscending: ('asc' == this.value)});
+    $('#showsortdirection').on( 'change', function() {
+        $('#container').isotope({sortAscending: ('asc' == this.value)});
     });
 });
-
-//-->
 </script>
 
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="tabs">
     <ul>
@@ -97,10 +81,10 @@
         <li><a href="#tabs-2">Customize Options</a></li>
     </ul>
     <div id="tabs-1" class="existingtabs">
-        #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl")
+        <%include file="/inc_rootDirs.mako"/>
     </div>
     <div id="tabs-2" class="existingtabs">
-        #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl")
+        <%include file="/inc_addShowOptions.mako"/>
     </div>
     <br>
 
@@ -125,9 +109,7 @@
 <br />
 
 <script type="text/javascript" charset="utf-8">
-<!--
 window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
-//-->
 </script>
 
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/home_recommendedShows.tmpl b/gui/slick/interfaces/default/home_trendingShows.mako
similarity index 57%
rename from gui/slick/interfaces/default/home_recommendedShows.tmpl
rename to gui/slick/interfaces/default/home_trendingShows.mako
index 5f4f21449e3b95b15c6fffbf7cb97b7ff4d19f1e..948459bde6b93fbf9858f8dc9cb80b1230ecd748 100644
--- a/gui/slick/interfaces/default/home_recommendedShows.tmpl
+++ b/gui/slick/interfaces/default/home_trendingShows.mako
@@ -1,48 +1,41 @@
-#import sickbeard
-#import datetime
-#import re
-#from sickbeard.common import *
-#from sickbeard import sbdatetime
-#from sickbeard.helpers import anon_url
-
-#set global $title="Recommended Shows"
-#set global $header="Recommended Shows"
-
-#set global $sbPath='..'
-
-#set global $topmenu='home'
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/recommendedShows.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
+<%!
+    import sickbeard
+    import datetime
+    import re
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
+    from sickbeard import sbdatetime
+    from sickbeard.helpers import anon_url
+%>
+<%include file="/inc_top.mako"/>
+
+<script type="text/javascript" src="${sbRoot}/js/addTrendingShow.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script>
 
 <script type="text/javascript" charset="utf-8">
-<!--
-
-\$(document).ready(function(){
-    \$( "#tabs" ).tabs({
+$(document).ready(function(){
+    $( "#tabs" ).tabs({
         collapsible: true,
-        selected: #if $sickbeard.ROOT_DIRS then '-1' else '0'#
+        selected: ${('0', '-1')[bool(sickbeard.ROOT_DIRS)]}
     });
 
     // initialise combos for dirty page refreshes
-    \$('#showsort').val('original');
-    \$('#showsortdirection').val('asc');
+    $('#showsort').val('original');
+    $('#showsortdirection').val('asc');
 
-    var \$container = [\$('#container')];
-    jQuery.each(\$container, function (j) {
+    var $container = [$('#container')];
+    jQuery.each($container, function (j) {
         this.isotope({
             itemSelector: '.trakt_show',
             sortBy: 'original-order',
             layoutMode: 'fitRows',
             getSortData: {
                 name: function( itemElem ) {
-                    var name = \$( itemElem ).attr('data-name') || '';
-#if not $sickbeard.SORT_ARTICLE:
+                    var name = $( itemElem ).attr('data-name') || '';
+% if not sickbeard.SORT_ARTICLE:
                     name = name.replace(/^(The|A|An)\s/i, '');
-#end if
+% endif
                     return name.toLowerCase();
                 },
                 rating: '[data-rating] parseInt',
@@ -51,7 +44,7 @@
         });
     });
 
-    \$('#showsort').on( 'change', function() {
+    $('#showsort').on( 'change', function() {
         var sortCriteria;
         switch (this.value) {
             case 'original':
@@ -61,7 +54,7 @@
                 /* randomise, else the rating_votes can already
                  * have sorted leaving this with nothing to do.
                  */
-                \$('#container').isotope({sortBy: 'random'});
+                $('#container').isotope({sortBy: 'random'});
                 sortCriteria = 'rating';
                 break;
             case 'rating_votes':
@@ -74,23 +67,20 @@
                 sortCriteria = 'name'
                 break;
         }
-        \$('#container').isotope({sortBy: sortCriteria});
+        $('#container').isotope({sortBy: sortCriteria});
     });
 
-    \$('#showsortdirection').on( 'change', function() {
-        \$('#container').isotope({sortAscending: ('asc' == this.value)});
+    $('#showsortdirection').on( 'change', function() {
+        $('#container').isotope({sortAscending: ('asc' == this.value)});
     });
 });
-
-//-->
 </script>
 
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
 
 <div id="tabs">
     <ul>
@@ -98,10 +88,10 @@
         <li><a href="#tabs-2">Customize Options</a></li>
     </ul>
     <div id="tabs-1" class="existingtabs">
-        #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl")
+        <%include file="/inc_rootDirs.mako"/>
     </div>
     <div id="tabs-2" class="existingtabs">
-        #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl")
+        <%include file="/inc_addShowOptions.mako"/>
     </div>
     <br>
 
@@ -126,9 +116,6 @@
 <br />
 
 <script type="text/javascript" charset="utf-8">
-<!--
 window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
-//-->
 </script>
-
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/inc_addShowOptions.tmpl b/gui/slick/interfaces/default/inc_addShowOptions.mako
similarity index 64%
rename from gui/slick/interfaces/default/inc_addShowOptions.tmpl
rename to gui/slick/interfaces/default/inc_addShowOptions.mako
index 1a462d03c4f05a19906bfd5b9002e42a71a5ab5b..512a4b0acc018ff3a9c71a2c19e161f2016c3624 100644
--- a/gui/slick/interfaces/default/inc_addShowOptions.tmpl
+++ b/gui/slick/interfaces/default/inc_addShowOptions.mako
@@ -1,27 +1,29 @@
-#import sickbeard
-#from sickbeard.common import *
-#from sickbeard import subtitles
-
-        #if $sickbeard.USE_SUBTITLES:
+<%
+    import sickbeard
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings
+    from sickbeard import subtitles
+%>
+        % if sickbeard.USE_SUBTITLES:
         <br><div class="field-pair">
             <label for="subtitles" class="clearfix">
                 <span class="component-title">Subtitles</span>
                 <span class="component-desc">
-                     <input type="checkbox" name="subtitles" id="subtitles" #if $sickbeard.SUBTITLES_DEFAULT then "checked=\"checked\"" else ""# />
+                     <input type="checkbox" name="subtitles" id="subtitles" ${('', 'checked="checked"')[bool(sickbeard.SUBTITLES_DEFAULT)]} />
                     <p>Download subtitles for this show?</p>
                 </span>
             </label>
         </div>
-        #end if
+        % endif
 
         <div class="field-pair">
             <label for="statusSelect">
                 <span class="component-title">Status for previously aired episodes</span>
                 <span class="component-desc">
                     <select name="defaultStatus" id="statusSelect" class="form-control form-control-inline input-sm">
-                    #for $curStatus in [$SKIPPED, $WANTED, $ARCHIVED, $IGNORED]:
-                        <option value="$curStatus" #if $sickbeard.STATUS_DEFAULT == $curStatus then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
-                    #end for
+                    % for curStatus in [SKIPPED, WANTED, ARCHIVED, IGNORED]:
+                        <option value="${curStatus}" ${('', 'selected="selected"')[sickbeard.STATUS_DEFAULT == curStatus]}>${statusStrings[curStatus]}</option>
+                    % endfor
                     </select>
                 </span>
             </label>
@@ -31,9 +33,9 @@
                 <span class="component-title">Status for all future episodes</span>
                 <span class="component-desc">
                     <select name="defaultStatusAfter" id="statusSelectAfter" class="form-control form-control-inline input-sm">
-                    #for $curStatus in [$SKIPPED, $WANTED, $ARCHIVED, $IGNORED]:
-                        <option value="$curStatus" #if $sickbeard.STATUS_DEFAULT_AFTER == $curStatus then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
-                    #end for
+                    % for curStatus in [SKIPPED, WANTED, ARCHIVED, IGNORED]:
+                        <option value="${curStatus}" ${('', 'selected="selected"')[sickbeard.STATUS_DEFAULT_AFTER == curStatus]}>${statusStrings[curStatus]}</option>
+                    % endfor
                     </select>
                 </span>
             </label>
@@ -42,39 +44,38 @@
             <label for="flatten_folders" class="clearfix">
                 <span class="component-title">Flatten Folders</span>
                 <span class="component-desc">
-                    <input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" #if $sickbeard.FLATTEN_FOLDERS_DEFAULT then "checked=\"checked\"" else ""# />
+                    <input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" ${('', 'checked="checked"')[bool(sickbeard.FLATTEN_FOLDERS_DEFAULT)]}/>
                     <p>Disregard sub-folders?</p>
                 </span>
             </label>
         </div>
 
-#if $enable_anime_options
+% if enable_anime_options:
         <div class="field-pair alt">
             <label for="anime" class="clearfix">
                 <span class="component-title">Anime</span>
                 <span class="component-desc">
-                    <input type="checkbox" name="anime" id="anime" #if $sickbeard.ANIME_DEFAULT then "checked=\"checked\"" else ""# />
+                    <input type="checkbox" name="anime" id="anime" ${('', 'checked="checked"')[bool(sickbeard.ANIME_DEFAULT)]} />
                     <p>Is this show an Anime?<p>
                 </span>
             </label>
         </div>
-#end if
+% endif
 
         <div class="field-pair alt">
-
             <label for="scene" class="clearfix">
                 <span class="component-title">Scene Numbering</span>
                 <span class="component-desc">
-                    <input type="checkbox" name="scene" id="scene" #if $sickbeard.SCENE_DEFAULT then "checked=\"checked\"" else ""# />
+                    <input type="checkbox" name="scene" id="scene" ${('', 'checked="checked"')[bool(sickbeard.SCENE_DEFAULT)]} />
                     <p>Is this show scene numbered?</p>
                 </span>
             </label>
         </div>
 
-        #set $qualities = $Quality.splitQuality($sickbeard.QUALITY_DEFAULT)
-        #set global $anyQualities = $qualities[0]
-        #set global $bestQualities = $qualities[1]
-        #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_qualityChooser.tmpl")
+        <% qualities = Quality.splitQuality(sickbeard.QUALITY_DEFAULT) %>
+        <% anyQualities = qualities[0] %>
+        <% bestQualities = qualities[1] %>
+        <%include file="/inc_qualityChooser.mako"/>
 
         <br>
         <div class="field-pair alt">
@@ -86,9 +87,9 @@
             </label>
         </div><br>
 
-#if $enable_anime_options
-#import sickbeard.blackandwhitelist
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_blackwhitelist.tmpl")
-#else
+% if enable_anime_options:
+    <% import sickbeard.blackandwhitelist %>
+    <%include file="/inc_blackwhitelist.mako"/>
+% else:
         <input type="hidden" name="anime" id="anime" value="0" />
-#end if
+% endif
diff --git a/gui/slick/interfaces/default/inc_blackwhitelist.tmpl b/gui/slick/interfaces/default/inc_blackwhitelist.mako
similarity index 78%
rename from gui/slick/interfaces/default/inc_blackwhitelist.tmpl
rename to gui/slick/interfaces/default/inc_blackwhitelist.mako
index 0ba3c159a8be419932ccd112bd1a7ff37a83dee5..0b088b327f878aee871180c2065fb2ae055a5210 100644
--- a/gui/slick/interfaces/default/inc_blackwhitelist.tmpl
+++ b/gui/slick/interfaces/default/inc_blackwhitelist.mako
@@ -15,9 +15,9 @@
                 <div class="blackwhitelist white">
                     <span><h4>Whitelist</h4></span>
                     <select id="white" multiple="multiple" size="12">
-                        #for $keyword in $whitelist:
-                            <option value="$keyword">$keyword</option>
-                        #end for
+                        % for keyword in whitelist:
+                            <option value="${keyword}">${keyword}</option>
+                        % endfor
                     </select>
                     <br/>
                     <input class="btn" id="removeW" value="Remove" type="button"/>
@@ -25,11 +25,11 @@
                 <div class="blackwhitelist pool">
                     <span><h4>Available Groups</h4></span>
                     <select id="pool" multiple="multiple" size="12">
-                    #for $group in $groups
-                        #if $group not in $whitelist and $group['name'] not in $blacklist:
-                            <option value="$group['name']">$group['name'] | $group['rating'] | $group['range']</option>
-                        #end if
-                    #end for
+                    % for group in groups:
+                        % if group not in whitelist and group['name'] not in blacklist:
+                            <option value="${group['name']}">${group['name']} | ${group['rating']} | ${group['range']}</option>
+                        % endif
+                    % endfor
                     </select>
                     <br/>
                     <input class="btn" id="addW" value="Add to Whitelist" type="button"/>
@@ -38,9 +38,9 @@
                 <div class="blackwhitelist black">
                     <span><h4>Blacklist</h4></span>
                     <select id="black" multiple="multiple" size="12">
-                        #for $keyword in $blacklist:
-                            <option value="$keyword">$keyword</option>
-                        #end for
+                        % for keyword in blacklist:
+                            <option value="${keyword}">${keyword}</option>
+                        % endfor
                     </select>
                     <br/>
                     <input class="btn" id="removeB" value="Remove" type="button"/>
@@ -55,4 +55,4 @@
         </div>
         <br style="clear:both" />
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/gui/slick/interfaces/default/inc_bottom.mako b/gui/slick/interfaces/default/inc_bottom.mako
new file mode 100644
index 0000000000000000000000000000000000000000..1e51091e352ec1a44e0a2cd1c3a85a8648e3d811
--- /dev/null
+++ b/gui/slick/interfaces/default/inc_bottom.mako
@@ -0,0 +1,76 @@
+<%!
+    import sickbeard
+    import datetime
+    from sickbeard import db
+    from sickbeard.common import Quality, SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST
+    import re
+%>
+<%
+    sbRoot = sickbeard.WEB_ROOT
+%>
+    </div> <!-- /content -->
+</div> <!-- /contentWrapper -->
+
+<footer>
+<div class="footer clearfix">
+<%
+    myDB = db.DBConnection()
+    today = str(datetime.date.today().toordinal())
+    status_quality = '(%s)' % ','.join([str(quality) for quality in Quality.SNATCHED + Quality.SNATCHED_PROPER])
+    status_download = '(%s)' % ','.join([str(quality) for quality in Quality.DOWNLOADED + [ARCHIVED]])
+
+    sql_statement = 'SELECT ' \
+    + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_snatched, ' % status_quality \
+    + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_downloaded, ' % status_download \
+    + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND ((airdate <= %s AND (status = %s OR status = %s)) ' % (today, str(SKIPPED), str(WANTED)) \
+    + ' OR (status IN %s) OR (status IN %s))) AS ep_total FROM tv_episodes tv_eps LIMIT 1' % (status_quality, status_download)
+
+    sql_result = myDB.select(sql_statement)
+
+    shows_total = len(sickbeard.showList)
+    shows_active = len([show for show in sickbeard.showList if show.paused == 0 and show.status == "Continuing"])
+
+    if sql_result:
+        ep_snatched = sql_result[0]['ep_snatched']
+        ep_downloaded = sql_result[0]['ep_downloaded']
+        ep_total = sql_result[0]['ep_total']
+    else:
+        ep_snatched = 0
+        ep_downloaded = 0
+        ep_total = 0
+
+    ep_percentage = '' if ep_total == 0 else '(<span class="footerhighlight">%s%%</span>)' % re.sub(r'(\d+)(\.\d)\d+', r'\1\2', str((float(ep_downloaded)/float(ep_total))*100))
+
+    try:
+        localRoot = sbRoot
+    except NotFound:
+        localRoot = ''
+
+    try:
+        localheader = header
+    except NotFound:
+        localheader = ''
+%>
+
+        <span class="footerhighlight">${shows_total}</span> Shows (<span class="footerhighlight">${shows_active}</span> Active)
+        | <span class="footerhighlight">${ep_downloaded}</span>
+
+        % if ep_snatched:
+        <span class="footerhighlight"><a href="${localRoot}/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">+${ep_snatched}</a></span> Snatched
+        % endif
+
+                &nbsp;/&nbsp;<span class="footerhighlight">${ep_total}</span> Episodes Downloaded ${ep_percentage}
+        | Daily Search: <span class="footerhighlight">${str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0]}</span>
+        | Backlog Search: <span class="footerhighlight">${str(sickbeard.backlogSearchScheduler.timeLeft()).split('.')[0]}</span>
+    </div>
+        <!--
+        <ul style="display: table; margin: 0 auto; font-size: 12px; list-style-type: none; padding: 0; padding-top: 10px;">
+            <li><a href="${sbRoot}/manage/manageSearches/forceVersionCheck"><img src="${sbRoot}/images/menu/update16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Force Version Check</a></li>
+            <li><a href="${sbRoot}/home/restart/?pid=${sbPID}" class="confirm"><img src="${sbRoot}/images/menu/restart16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Restart</a></li>
+            <li><a href="${sbRoot}/home/shutdown/?pid=${sbPID}" class="confirm"><img src="${sbRoot}/images/menu/shutdown16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Shutdown</a></li>
+        </ul>
+        -->
+</footer>
+
+</body>
+</html>
diff --git a/gui/slick/interfaces/default/inc_bottom.tmpl b/gui/slick/interfaces/default/inc_bottom.tmpl
deleted file mode 100644
index 551196b09ac036c841b57151b70962cc7d956b72..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/inc_bottom.tmpl
+++ /dev/null
@@ -1,81 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard import db, sbdatetime
-#from sickbeard.common import *
-
-    </div> <!-- /content -->
-</div> <!-- /contentWrapper -->
-
-<footer>
-    <div class="footer clearfix">
-        #set $myDB = $db.DBConnection()
-        #set $today = str($datetime.date.today().toordinal())
-        #set status_quality = '(%s)' % ','.join([str(quality) for quality in $Quality.SNATCHED + $Quality.SNATCHED_PROPER])
-        #set status_download = '(%s)' % ','.join([str(quality) for quality in $Quality.DOWNLOADED + [$ARCHIVED]])
-
-        #set $sql_statement = 'SELECT '\
-            + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_snatched, '\
-            % $status_quality\
-            + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_downloaded, '\
-            % $status_download\
-            + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 '\
-            + ' AND ((airdate <= %s AND (status = %s OR status = %s)) '\
-            % ($today, str($SKIPPED), str($WANTED))\
-            + ' OR (status IN %s) OR (status IN %s))) AS ep_total FROM tv_episodes tv_eps LIMIT 1'\
-            % ($status_quality, $status_download)
-
-        #set $sql_result = $myDB.select($sql_statement)
-
-        #set $shows_total = len($sickbeard.showList)
-        #set $shows_active = len([show for show in $sickbeard.showList if show.paused == 0 and show.status == "Continuing"])
-
-        #if $sql_result:
-            #set $ep_snatched = $sql_result[0]['ep_snatched']
-            #set $ep_downloaded = $sql_result[0]['ep_downloaded']
-            #set $ep_total = $sql_result[0]['ep_total']
-        #else
-            #set $ep_snatched = 0
-            #set $ep_downloaded = 0
-            #set $ep_total = 0
-        #end if
-        #set $ep_percentage = '' if $ep_total == 0 else '(<span class="footerhighlight">%s%%</span>)' % re.sub(r'(\d+)(\.\d)\d+', r'\1\2', str((float($ep_downloaded)/float($ep_total))*100))
-
-        #try
-            #set $localRoot = $sbRoot
-        #except NotFound
-            #set $localRoot = ''
-        #end try
-        #try
-            #set $localheader = $header
-        #except NotFound
-            #set $localheader = ''
-        #end try
-
-        <span class="footerhighlight">$shows_total</span> Shows (<span class="footerhighlight">$shows_active</span> Active)
-        | <span class="footerhighlight">$ep_downloaded</span>
-                <%= (
-                        '',
-                        ' (<span class="footerhighlight">+%s</span> Snatched)'\
-                        % (
-                            str(ep_snatched),
-                            '<a href="%s/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">%s</a>'\
-                            % (localRoot, str(ep_snatched))
-                        )['Episode Overview' != localheader]
-                    )[0 < ep_snatched]
-                %>
-                &nbsp;/&nbsp;<span class="footerhighlight">$ep_total</span> Episodes Downloaded $ep_percentage
-        | Daily Search: <span class="footerhighlight"><%=str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0]%></span>
-        | Backlog Search: <span class="footerhighlight"><%=str(sickbeard.backlogSearchScheduler.timeLeft()).split('.')[0]%></span>
-
-    </div>
-        <!--
-        <ul style="display: table; margin: 0 auto; font-size: 12px; list-style-type: none; padding: 0; padding-top: 10px;">
-            <li><a href="$sbRoot/manage/manageSearches/forceVersionCheck"><img src="$sbRoot/images/menu/update16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Force Version Check</a></li>
-            <li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm"><img src="$sbRoot/images/menu/restart16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Restart</a></li>
-            <li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm"><img src="$sbRoot/images/menu/shutdown16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Shutdown</a></li>
-        </ul>
-        -->
-</footer>
-
-</body>
-</html>
diff --git a/gui/slick/interfaces/default/inc_qualityChooser.mako b/gui/slick/interfaces/default/inc_qualityChooser.mako
new file mode 100644
index 0000000000000000000000000000000000000000..55f887773532055ae0f4490a9b795f99a6ff2127
--- /dev/null
+++ b/gui/slick/interfaces/default/inc_qualityChooser.mako
@@ -0,0 +1,60 @@
+<%!
+    import sickbeard
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
+%>
+
+<div class="field-pair">
+    <label for="qualityPreset">
+
+<%
+if not show is UNDEFINED:
+    __quality = int(show.quality)
+else:
+    __quality = int(sickbeard.QUALITY_DEFAULT)
+
+qualities = Quality.splitQuality(__quality)
+anyQualities = qualities[0]
+bestQualities = qualities[1]
+%>
+
+<% overall_quality = Quality.combineQualities(anyQualities, bestQualities) %>
+<span class="component-title">Preferred quality of episodes to be download</span>
+<span class="component-desc">
+<% selected = None %>
+<select id="qualityPreset" class="form-control form-control-inline input-sm">
+<option value="0">Custom</option>
+% for curPreset in sorted(qualityPresets):
+<option value="${curPreset}" ${('', 'selected="selected"')[curPreset == overall_quality]} ${('', 'style="padding-left: 15px;"')[qualityPresetStrings[curPreset].endswith("0p")]}>${qualityPresetStrings[curPreset]}</option>
+% endfor
+</select>
+</span>
+    </label>
+</div>
+
+<div id="customQualityWrapper">
+    <div id="customQuality">
+        <div class="component-group-desc">
+            <p><b>Preferred</b> qualities will replace an <b>Allowed</b> quality if found, initially or in the future, even if it is a lower quality</p>
+        </div>
+
+        <div style="padding-right: 40px; text-align: left; float: left;">
+            <h5>Allowed</h4>
+            <% anyQualityList = filter(lambda x: x > Quality.NONE, Quality.qualityStrings) %>
+            <select id="anyQualities" name="anyQualities" multiple="multiple" size="${len(anyQualityList)}" class="form-control form-control-inline input-sm">
+            % for curQuality in sorted(anyQualityList):
+                <option value="${curQuality}" ${('', 'selected="selected"')[curQuality in anyQualities]}>${Quality.qualityStrings[curQuality]}</option>
+            % endfor
+            </select>
+        </div>
+
+        <div style="text-align: left; float: left;">
+            <h5>Preferred</h4>
+            <% bestQualityList = filter(lambda x: x >= Quality.SDTV and x < Quality.UNKNOWN, Quality.qualityStrings) %>
+            <select id="bestQualities" name="bestQualities" multiple="multiple" size="${len(bestQualityList)}" class="form-control form-control-inline input-sm">
+            % for curQuality in sorted(bestQualityList):
+                <option value="${curQuality}" ${('', 'selected="selected"')[curQuality in bestQualities]}>${Quality.qualityStrings[curQuality]}</option>
+            % endfor
+            </select>
+        </div>
+    </div>
+</div>
diff --git a/gui/slick/interfaces/default/inc_qualityChooser.tmpl b/gui/slick/interfaces/default/inc_qualityChooser.tmpl
deleted file mode 100644
index 85dda6f4298fdde0109564de28e7b4ad9e029595..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/inc_qualityChooser.tmpl
+++ /dev/null
@@ -1,47 +0,0 @@
-#import sickbeard
-#from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
-
-<div class="field-pair">
-    <label for="qualityPreset">
-#set $overall_quality = $Quality.combineQualities($anyQualities, $bestQualities)
-<span class="component-title">Preferred quality of episodes to be download</span>
-<span class="component-desc">
-#set $selected = None
-<select id="qualityPreset" class="form-control form-control-inline input-sm">
-<option value="0">Custom</option>
-#for $curPreset in sorted($qualityPresets):
-<option value="$curPreset" #if $curPreset == $overall_quality then "selected=\"selected\"" else ""# #if $qualityPresetStrings[$curPreset].endswith("0p") then "style=\"padding-left: 15px;\"" else ""#>$qualityPresetStrings[$curPreset]</option>
-#end for
-</select>
-</span>
-
-    </label>
-</div>
-
-<div id="customQualityWrapper">
-    <div id="customQuality">
-        <div class="component-group-desc">
-            <p><b>Preferred</b> qualities will replace an <b>Allowed</b> quality if found, initially or in the future, even if it is a lower quality</p>
-        </div>
-
-        <div style="padding-right: 40px; text-align: left; float: left;">
-            <h5>Allowed</h4>
-            #set $anyQualityList = filter(lambda x: x > $Quality.NONE, $Quality.qualityStrings)
-            <select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)" class="form-control form-control-inline input-sm">
-            #for $curQuality in sorted($anyQualityList):
-                <option value="$curQuality" #if $curQuality in $anyQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option>
-            #end for
-            </select>
-        </div>
-
-        <div style="text-align: left; float: left;">
-            <h5>Preferred</h4>
-            #set $bestQualityList = filter(lambda x: x >= $Quality.SDTV and x < $Quality.UNKNOWN, $Quality.qualityStrings)
-            <select id="bestQualities" name="bestQualities" multiple="multiple" size="$len($bestQualityList)" class="form-control form-control-inline input-sm">
-            #for $curQuality in sorted($bestQualityList):
-                <option value="$curQuality" #if $curQuality in $bestQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option>
-            #end for
-            </select>
-        </div>
-    </div>
-</div>
diff --git a/gui/slick/interfaces/default/inc_rootDirs.tmpl b/gui/slick/interfaces/default/inc_rootDirs.mako
similarity index 55%
rename from gui/slick/interfaces/default/inc_rootDirs.tmpl
rename to gui/slick/interfaces/default/inc_rootDirs.mako
index 852b7dbe2fba24abc0afc3bd3cfb5dfc498d6cf6..ecdec42255be6864b5c5397236fde1cb664ad22a 100644
--- a/gui/slick/interfaces/default/inc_rootDirs.tmpl
+++ b/gui/slick/interfaces/default/inc_rootDirs.mako
@@ -1,21 +1,23 @@
-#import sickbeard
-<span id="sampleRootDir"></span>
+<%
+    import sickbeard
+
+    if sickbeard.ROOT_DIRS:
+        backend_pieces = sickbeard.ROOT_DIRS.split('|')
+        backend_default = 'rd-' + backend_pieces[0]
+        backend_dirs = backend_pieces[1:]
+    else:
+        backend_default = ''
+        backend_dirs = []
+%>
 
-#if $sickbeard.ROOT_DIRS:
-#set $backend_pieces = $sickbeard.ROOT_DIRS.split('|')
-#set $backend_default = 'rd-' + $backend_pieces[0]
-#set $backend_dirs = $backend_pieces[1:]
-#else:
-#set $backend_default = ''
-#set $backend_dirs = []
-#end if
+<span id="sampleRootDir"></span>
 
-<input type="hidden" id="whichDefaultRootDir" value="$backend_default" />
+<input type="hidden" id="whichDefaultRootDir" value="${backend_default}" />
 <div class="rootdir-selectbox">
     <select name="rootDir" id="rootDirs" size="6">
-    #for $cur_dir in $backend_dirs:
-        <option value="$cur_dir">$cur_dir</option>
-    #end for
+    % for cur_dir in backend_dirs:
+        <option value="${cur_dir}">${cur_dir}</option>
+    % endfor
     </select>
 </div>
 <div id="rootDirsControls" class="rootdir-controls">
diff --git a/gui/slick/interfaces/default/inc_top.mako b/gui/slick/interfaces/default/inc_top.mako
new file mode 100644
index 0000000000000000000000000000000000000000..be2a5cdb8f68ee006e1f518a9c598dd20d1625e0
--- /dev/null
+++ b/gui/slick/interfaces/default/inc_top.mako
@@ -0,0 +1,288 @@
+<%!
+    import sickbeard
+%>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="robots" content="noindex, nofollow">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width">
+
+        <!-- These values come from css/dark.css and css/light.css -->
+        % if sickbeard.THEME_NAME == "dark":
+        <meta name="theme-color" content="#15528F">
+        % elif sickbeard.THEME_NAME == "light":
+        <meta name="theme-color" content="#333333">
+        % endif
+
+        <title>SickRage - BRANCH:[${sickbeard.BRANCH}] - ${title}</title>
+
+        <!--[if lt IE 9]>
+            <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+            <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+        <![endif]-->
+
+        <link rel="shortcut icon" href="${sbRoot}/images/ico/favicon.ico">
+        <link rel="icon" sizes="16x16 32x32 64x64" href="${sbRoot}/images/ico/favicon.ico">
+        <link rel="icon" type="image/png" sizes="196x196" href="${sbRoot}/images/ico/favicon-196.png">
+        <link rel="icon" type="image/png" sizes="160x160" href="${sbRoot}/images/ico/favicon-160.png">
+        <link rel="icon" type="image/png" sizes="96x96" href="${sbRoot}/images/ico/favicon-96.png">
+        <link rel="icon" type="image/png" sizes="64x64" href="${sbRoot}/images/ico/favicon-64.png">
+        <link rel="icon" type="image/png" sizes="32x32" href="${sbRoot}/images/ico/favicon-32.png">
+        <link rel="icon" type="image/png" sizes="16x16" href="${sbRoot}/images/ico/favicon-16.png">
+        <link rel="apple-touch-icon" sizes="152x152" href="${sbRoot}/images/ico/favicon-152.png">
+        <link rel="apple-touch-icon" sizes="144x144" href="${sbRoot}/images/ico/favicon-144.png">
+        <link rel="apple-touch-icon" sizes="120x120" href="${sbRoot}/images/ico/favicon-120.png">
+        <link rel="apple-touch-icon" sizes="114x114" href="${sbRoot}/images/ico/favicon-114.png">
+        <link rel="apple-touch-icon" sizes="76x76" href="${sbRoot}/images/ico/favicon-76.png">
+        <link rel="apple-touch-icon" sizes="72x72" href="${sbRoot}/images/ico/favicon-72.png">
+        <link rel="apple-touch-icon" href="${sbRoot}/images/ico/favicon-57.png">
+        <meta name="msapplication-TileColor" content="#FFFFFF">
+        <meta name="msapplication-TileImage" content="${sbRoot}/images/ico/favicon-144.png">
+        <meta name="msapplication-config" content="${sbRoot}/css/browserconfig.xml">
+
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/lib/bootstrap.css?${sbPID}"/>
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/browser.css?${sbPID}" />
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/lib/jquery-ui-1.10.4.custom.css?${sbPID}" />
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/lib/jquery.qtip-2.2.1.min.css?${sbPID}"/>
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/style.css?${sbPID}"/>
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/${sickbeard.THEME_NAME}.css?${sbPID}" />
+        % if sbLogin:
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/lib/pnotify.custom.min.css?${sbPID}" />
+        <link rel="stylesheet" type="text/css" href="${sbRoot}/css/country-flags.css?${sbPID}"/>
+        % endif
+
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-hover-dropdown.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery-ui-1.10.4.custom.min.js?${sbPID}"></script>
+        % if sbLogin:
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.cookie.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.cookiejar.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.json-2.2.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.selectboxes.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.tablesorter-2.17.7.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.tablesorter.widgets-2.17.7.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.tablesorter.widget-columnSelector-2.17.7.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.qtip-2.2.1.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/pnotify.custom.min.js"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.form-3.35.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.ui.touch-punch-0.2.2.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/isotope.pkgd.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.confirm.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/script.js?${sbPID}"></script>
+
+
+        % if sickbeard.FUZZY_DATING:
+        <script type="text/javascript" src="${sbRoot}/js/moment/moment.min.js?${sbPID}"></script>
+        <script type="text/javascript" src="${sbRoot}/js/fuzzyMoment.js?${sbPID}"></script>
+        % endif
+        <script type="text/javascript" charset="utf-8">
+            sbRoot = '${sbRoot}'; // needed for browser.js & ajaxNotifications.js
+            //HTML for scrolltopcontrol, which is auto wrapped in DIV w/ ID="topcontrol"
+            top_image_html = '<img src="${sbRoot}/images/top.gif" width="31" height="11" alt="Jump to top" />';
+            themeSpinner = '${('', '-dark')[sickbeard.THEME_NAME == 'dark']}';
+            anonURL = '${sickbeard.ANON_REDIRECT}'
+        </script>
+        <script type="text/javascript" src="${sbRoot}/js/lib/jquery.scrolltopcontrol-1.1.js"></script>
+        <script type="text/javascript" src="${sbRoot}/js/browser.js"></script>
+        <script type="text/javascript" src="${sbRoot}/js/ajaxNotifications.js"></script>
+        <script type="text/javascript">
+            function initActions() {
+                $("#SubMenu a[href*='/home/restart/']").addClass('btn restart').html('<span class="submenu-icon-restart pull-left"></span> Restart');
+                $("#SubMenu a[href*='/home/shutdown/']").addClass('btn shutdown').html('<span class="submenu-icon-shutdown pull-left"></span> Shutdown');
+                $("#SubMenu a[href*='/home/logout/']").addClass('btn').html('<span class="ui-icon ui-icon-power pull-left"></span> Logout');
+                $("#SubMenu a:contains('Edit')").addClass('btn').html('<span class="ui-icon ui-icon-pencil pull-left"></span> Edit');
+                $("#SubMenu a:contains('Remove')").addClass('btn remove').html('<span class="ui-icon ui-icon-trash pull-left"></span> Remove');
+                $("#SubMenu a:contains('Clear History')").addClass('btn clearhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear History');
+                $("#SubMenu a:contains('Trim History')").addClass('btn trimhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Trim History');
+                $("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear Errors');
+                $("#SubMenu a[href$='/errorlogs/submit_errors/']").addClass('btn').html('<span class="ui-icon ui-icon-arrowreturnthick-1-n pull-left"></span> Submit Errors');
+                $("#SubMenu a:contains('Re-scan')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Re-scan');
+                $("#SubMenu a:contains('Backlog Overview')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Backlog Overview');
+                $("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update PLEX');
+                $("#SubMenu a:contains('Force')").addClass('btn').html('<span class="ui-icon ui-icon-transfer-e-w pull-left"></span> Force Full Update');
+                $("#SubMenu a:contains('Rename')").addClass('btn').html('<span class="ui-icon ui-icon-tag pull-left"></span> Preview Rename');
+                $("#SubMenu a[href$='/config/subtitles/']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Search Subtitles');
+                $("#SubMenu a[href*='/home/subtitleShow']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Download Subtitles');
+                $("#SubMenu a:contains('Anime')").addClass('btn').html('<span class="submenu-icon-anime pull-left"></span> Anime');
+                $("#SubMenu a:contains('Settings')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Settings');
+                $("#SubMenu a:contains('Provider')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Providers');
+                $("#SubMenu a:contains('Backup/Restore')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> Backup/Restore');
+                $("#SubMenu a:contains('General')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> General');
+                $("#SubMenu a:contains('Episode Status')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Episode Status Management');
+                $("#SubMenu a:contains('Missed Subtitle')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Missed Subtitles');
+                $("#SubMenu a[href$='/home/addShows/']").addClass('btn').html('<span class="ui-icon ui-icon-video pull-left"></span> Add Show');
+                $("#SubMenu a:contains('Processing')").addClass('btn').html('<span class="ui-icon ui-icon-folder-open pull-left"></span> Post-Processing');
+                $("#SubMenu a:contains('Manage Searches')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Manage Searches');
+                $("#SubMenu a:contains('Manage Torrents')").addClass('btn').html('<span class="submenu-icon-bittorrent pull-left"></span> Manage Torrents');
+                $("#SubMenu a[href$='/manage/failedDownloads/']").addClass('btn').html('<span class="submenu-icon-failed-download pull-left"></span> Failed Downloads');
+                $("#SubMenu a:contains('Notification')").addClass('btn').html('<span class="ui-icon ui-icon-note pull-left"></span> Notifications');
+                $("#SubMenu a:contains('Update show in KODI')").addClass('btn').html('<span class="submenu-icon-kodi pull-left"></span> Update show in KODI');
+                $("#SubMenu a[href$='/home/updateKODI/']").addClass('btn').html('<span class="submenu-icon-kodi pull-left"></span> Update KODI');
+                $("#SubMenu a:contains('Update show in Emby')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update show in Emby');
+                $("#SubMenu a[href$='/home/updateEMBY/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update Emby');
+                $("#SubMenu a:contains('Pause')").addClass('btn').html('<span class="ui-icon ui-icon-pause pull-left"></span> Pause');
+                $("#SubMenu a:contains('Resume')").addClass('btn').html('<span class="ui-icon ui-icon-play pull-left"></span> Resume');
+
+            };
+
+            $(document).ready(function() {
+
+                initActions();
+
+                $("#NAV${topmenu}").addClass("active");
+
+                $('.dropdown-toggle').dropdownHover();
+
+            });
+        </script>
+    <script type="text/javascript" src="${sbRoot}/js/confirmations.js?${sbPID}"></script>
+    % endif
+    </head>
+
+    <body>
+        <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
+            <div class="container-fluid">
+                <div class="navbar-header">
+                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
+                        <span class="sr-only">Toggle navigation</span>
+                        <span class="icon-bar"></span>
+                        <span class="icon-bar"></span>
+                        <span class="icon-bar"></span>
+                    </button>
+                    <a class="navbar-brand" href="${sbRoot}/home/" title="SickRage"><img alt="SickRage" src="${sbRoot}/images/sickrage.png" style="height: 50px;" class="img-responsive pull-left" /></a>
+                </div>
+
+            % if sbLogin:
+                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
+                    <ul class="nav navbar-nav navbar-right">
+                        <li id="NAVnews">
+                            <a href="${sbRoot}/news/">News</a>
+                        </li>
+                        <li id="NAVnews">
+                            <a href="${sbRoot}/IRC/">IRC</a>
+                        </li>
+                        <li id="NAVhome">
+                            <a href="${sbRoot}/home/">Shows</a>
+                        </li>
+
+                        <li id="NAVcomingEpisodes">
+                            <a href="${sbRoot}/comingEpisodes/">Coming Episodes</a>
+                        </li>
+
+                        <li id="NAVhistory">
+                            <a href="${sbRoot}/history/">History</a>
+                        </li>
+
+                        <li id="NAVmanage" class="dropdown">
+                            <a href="${sbRoot}/manage/" class="dropdown-toggle" data-toggle="dropdown">Manage <b class="caret"></b></a>
+                            <ul class="dropdown-menu">
+                                <li><a href="${sbRoot}/manage/"><i class="menu-icon-manage"></i>&nbsp;Mass Update</a></li>
+                                <li><a href="${sbRoot}/manage/backlogOverview/"><i class="menu-icon-backlog-view"></i>&nbsp;Backlog Overview</a></li>
+                                <li><a href="${sbRoot}/manage/manageSearches/"><i class="menu-icon-manage-searches"></i>&nbsp;Manage Searches</a></li>
+                                <li><a href="${sbRoot}/manage/episodeStatuses/"><i class="menu-icon-backlog"></i>&nbsp;Episode Status Management</a></li>
+                            % if sickbeard.USE_PLEX and sickbeard.PLEX_SERVER_HOST != "":
+                                <li><a href="${sbRoot}/home/updatePLEX/"><i class="menu-icon-backlog-view"></i>&nbsp;Update PLEX</a></li>
+                            % endif
+                            % if sickbeard.USE_KODI and sickbeard.KODI_HOST != "":
+                                <li><a href="${sbRoot}/home/updateKODI/"><i class="menu-icon-kodi"></i>&nbsp;Update KODI</a></li>
+                            % endif
+                            % if sickbeard.USE_EMBY and sickbeard.EMBY_HOST != "" and sickbeard.EMBY_APIKEY != "":
+                                <li><a href="${sbRoot}/home/updateEMBY/"><i class="menu-icon-backlog-view"></i>&nbsp;Update Emby</a></li>
+                            % endif
+                            % if sickbeard.USE_TORRENTS and sickbeard.TORRENT_METHOD != 'blackhole' and (sickbeard.ENABLE_HTTPS and sickbeard.TORRENT_HOST[:5] == 'https' or not sickbeard.ENABLE_HTTPS and sickbeard.TORRENT_HOST[:5] == 'http:'):
+                                <li><a href="${sbRoot}/manage/manageTorrents/"><i class="menu-icon-bittorrent"></i>&nbsp;Manage Torrents</a></li>
+                            % endif
+                            % if sickbeard.USE_FAILED_DOWNLOADS:
+                                <li><a href="${sbRoot}/manage/failedDownloads/"><i class="menu-icon-failed-download"></i>&nbsp;Failed Downloads</a></li>
+                            % endif
+                            % if sickbeard.USE_SUBTITLES:
+                                <li><a href="${sbRoot}/manage/subtitleMissed/"><i class="menu-icon-backlog"></i>&nbsp;Missed Subtitle Management</a></li>
+                            % endif
+                            </ul>
+                        </li>
+
+                        <li id="NAVerrorlogs" class="dropdown">
+                            <a href="${sbRoot}/errorlogs/" class="dropdown-toggle" data-toggle="dropdown">${logPageTitle} <b class="caret"></b></a>
+                            <ul class="dropdown-menu">
+                                <li><a href="${sbRoot}/errorlogs/"><i class="menu-icon-viewlog-errors"></i>&nbsp;View Log (Errors)</a></li>
+                                <li><a href="${sbRoot}/errorlogs/viewlog/"><i class="menu-icon-viewlog"></i>&nbsp;View Log</a></li>
+                            </ul>
+                        </li>
+
+                        <li id="NAVconfig" class="dropdown">
+                            <a href="${sbRoot}/config/" class="dropdown-toggle" data-toggle="dropdown"><img src="${sbRoot}/images/menu/system18.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">Config <b class="caret"></b></span></a>
+                            <ul class="dropdown-menu">
+                                <li><a href="${sbRoot}/config/"><i class="menu-icon-help"></i>&nbsp;Help &amp; Info</a></li>
+                                <li><a href="${sbRoot}/config/general/"><i class="menu-icon-config"></i>&nbsp;General</a></li>
+                                <li><a href="${sbRoot}/config/backuprestore/"><i class="menu-icon-config"></i>&nbsp;Backup &amp; Restore</a></li>
+                                <li><a href="${sbRoot}/config/search/"><i class="menu-icon-config"></i>&nbsp;Search Settings</a></li>
+                                <li><a href="${sbRoot}/config/providers/"><i class="menu-icon-config"></i>&nbsp;Search Providers</a></li>
+                                <li><a href="${sbRoot}/config/subtitles/"><i class="menu-icon-config"></i>&nbsp;Subtitles Settings</a></li>
+                                <li><a href="${sbRoot}/config/postProcessing/"><i class="menu-icon-config"></i>&nbsp;Post Processing</a></li>
+                                <li><a href="${sbRoot}/config/notifications/"><i class="menu-icon-config"></i>&nbsp;Notifications</a></li>
+                                <li><a href="${sbRoot}/config/anime/"><i class="menu-icon-config"></i>&nbsp;Anime</a></li>
+                            </ul>
+                        </li>
+
+                        <li class="dropdown">
+                            <a href="#" class="dropdown-toggle" data-toggle="dropdown"><img src="${sbRoot}/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
+                            <ul class="dropdown-menu">
+                                <li><a href="${sbRoot}/home/updateCheck?pid=${sbPID}"><i class="menu-icon-update"></i>&nbsp;Check For Updates</a></li>
+                                <li><a href="${sbRoot}/changes"><i class="menu-icon-help"></i>&nbsp;Changelog</a></li>
+                                <li><a href="${sbRoot}/home/restart/?pid=${sbPID}" class="confirm restart"><i class="menu-icon-restart"></i>&nbsp;Restart</a></li>
+                                <li><a href="${sbRoot}/home/shutdown/?pid=${sbPID}" class="confirm shutdown"><i class="menu-icon-shutdown"></i>&nbsp;Shutdown</a></li>
+                                <li><a href="${sbRoot}/logout" class="confirm logout"><i class="menu-icon-shutdown"></i>&nbsp;Logout</a></li>
+                                <li><a href="${sbRoot}/home/status/"><i class="menu-icon-help"></i>&nbsp;Server Status</a></li>
+                            </ul>
+                        </li>
+                        <li id="donate"><a href="https://github.com/SiCKRAGETV/SickRage/wiki/Donations" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href); return false;"><img src="${sbRoot}/images/donate.jpg" alt="[donate]" class="navbaricon hidden-xs" /></a></li>
+                    </ul>
+            % endif
+                </div><!-- /.navbar-collapse -->
+            </div><!-- /.container-fluid -->
+        </nav>
+
+        % if not submenu is UNDEFINED:
+        <div id="SubMenu">
+        <span>
+        <% first = True %>
+        % for menuItem in submenu:
+            % if 'requires' not in menuItem or menuItem['requires']:
+                  % if type(menuItem['path']) == dict:
+                      ${("</span><span>", "")[bool(first)]}<b>${menuItem['title']}</b>
+                      <%
+                          first = False
+                          inner_first = True
+                      %>
+                      % for cur_link in menuItem['path']:
+                          ${("&middot; ", "")[bool(inner_first)]}<a class="inner" href="${sbRoot}/${menuItem['path'][cur_link]}">${cur_link}</a>
+                          <% inner_first = False %>
+                      % endfor
+                  % else:
+                      <a href="${sbRoot}/${menuItem['path']}" ${("", "class=\"confirm\"")['confirm' in menuItem]}>${menuItem['title']}</a>
+                      <% first = False %>
+                  % endif
+            % endif
+        % endfor
+        </span>
+        </div>
+        % endif
+
+        % if sickbeard.BRANCH and sickbeard.BRANCH != 'master' and not sickbeard.DEVELOPER and sbLogin:
+        <div class="alert alert-danger upgrade-notification" role="alert">
+            <span>You're using the ${sickbeard.BRANCH} branch. Please use 'master' unless specifically asked</span>
+        </div>
+        % endif
+
+        % if sickbeard.NEWEST_VERSION_STRING and sbLogin:
+        <div class="alert alert-success upgrade-notification" role="alert">
+            <span>${sickbeard.NEWEST_VERSION_STRING}</span>
+        </div>
+        % endif
+
+<div id="contentWrapper">
+    <div id="content">
diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl
deleted file mode 100644
index 1da9bfba67b4f34140a377fe5e938dd46fa28c46..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/inc_top.tmpl
+++ /dev/null
@@ -1,294 +0,0 @@
-#import sickbeard
-#import urllib
-
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta name="robots" content="noindex, nofollow">    
-        <meta charset="utf-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width">
-
-        <!-- These values come from css/dark.css and css/light.css -->
-        #if $sickbeard.THEME_NAME == "dark":
-        <meta name="theme-color" content="#15528F">
-        #elif $sickbeard.THEME_NAME == "light":
-        <meta name="theme-color" content="#333333">
-        #end if
-
-        <title>SickRage - BRANCH:[$sickbeard.BRANCH] - $title</title>
-
-        <!--[if lt IE 9]>
-            <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
-            <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
-        <![endif]-->
-
-        <link rel="shortcut icon" href="$sbRoot/images/ico/favicon.ico">
-        <link rel="icon" sizes="16x16 32x32 64x64" href="$sbRoot/images/ico/favicon.ico">
-        <link rel="icon" type="image/png" sizes="196x196" href="$sbRoot/images/ico/favicon-196.png">
-        <link rel="icon" type="image/png" sizes="160x160" href="$sbRoot/images/ico/favicon-160.png">
-        <link rel="icon" type="image/png" sizes="96x96" href="$sbRoot/images/ico/favicon-96.png">
-        <link rel="icon" type="image/png" sizes="64x64" href="$sbRoot/images/ico/favicon-64.png">
-        <link rel="icon" type="image/png" sizes="32x32" href="$sbRoot/images/ico/favicon-32.png">
-        <link rel="icon" type="image/png" sizes="16x16" href="$sbRoot/images/ico/favicon-16.png">
-        <link rel="apple-touch-icon" sizes="152x152" href="$sbRoot/images/ico/favicon-152.png">
-        <link rel="apple-touch-icon" sizes="144x144" href="$sbRoot/images/ico/favicon-144.png">
-        <link rel="apple-touch-icon" sizes="120x120" href="$sbRoot/images/ico/favicon-120.png">
-        <link rel="apple-touch-icon" sizes="114x114" href="$sbRoot/images/ico/favicon-114.png">
-        <link rel="apple-touch-icon" sizes="76x76" href="$sbRoot/images/ico/favicon-76.png">
-        <link rel="apple-touch-icon" sizes="72x72" href="$sbRoot/images/ico/favicon-72.png">
-        <link rel="apple-touch-icon" href="$sbRoot/images/ico/favicon-57.png">
-        <meta name="msapplication-TileColor" content="#FFFFFF">
-        <meta name="msapplication-TileImage" content="$sbRoot/images/ico/favicon-144.png">
-        <meta name="msapplication-config" content="$sbRoot/css/browserconfig.xml">
-
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?$sbPID"/>
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/browser.css?$sbPID" />
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery-ui-1.10.4.custom.css?$sbPID" />
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery.qtip-2.2.1.min.css?$sbPID"/>
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?$sbPID"/>
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/${sickbeard.THEME_NAME}.css?$sbPID" />
-        #if $sbLogin:
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/pnotify.custom.min.css?$sbPID" />
-        <link rel="stylesheet" type="text/css" href="$sbRoot/css/country-flags.css?$sbPID"/>
-        #end if
-
-
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery-1.11.2.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/bootstrap.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/bootstrap-hover-dropdown.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery-ui-1.10.4.custom.min.js?$sbPID"></script>
-        #if $sbLogin:
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.cookie.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.cookiejar.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.json-2.2.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.selectboxes.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter-2.17.7.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter.widgets-2.17.7.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter.widget-columnSelector-2.17.7.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.qtip-2.2.1.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/pnotify.custom.min.js"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.form-3.35.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.ui.touch-punch-0.2.2.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/isotope.pkgd.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.confirm.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/script.js?$sbPID"></script>
-
-
-        #if $sickbeard.FUZZY_DATING:
-        <script type="text/javascript" src="$sbRoot/js/moment/moment.min.js?$sbPID"></script>
-        <script type="text/javascript" src="$sbRoot/js/fuzzyMoment.js?$sbPID"></script>
-        #end if
-        <script type="text/javascript" charset="utf-8">
-        <!--
-            sbRoot = '$sbRoot'; // needed for browser.js & ajaxNotifications.js
-            //HTML for scrolltopcontrol, which is auto wrapped in DIV w/ ID="topcontrol"
-            top_image_html = '<img src="$sbRoot/images/top.gif" width="31" height="11" alt="Jump to top" />';
-            themeSpinner = <%= '\'\'' if 'dark' != sickbeard.THEME_NAME else '\'-dark\'' %>;
-            anonURL = '<%= sickbeard.ANON_REDIRECT %>'
-        //-->
-        </script>
-        <script type="text/javascript" src="$sbRoot/js/lib/jquery.scrolltopcontrol-1.1.js"></script>
-        <script type="text/javascript" src="$sbRoot/js/browser.js"></script>
-        <script type="text/javascript" src="$sbRoot/js/ajaxNotifications.js"></script>
-        <script type="text/javascript">
-        <!--
-            function initActions() {
-                \$("#SubMenu a[href*='/home/restart/']").addClass('btn restart').html('<span class="submenu-icon-restart pull-left"></span> Restart');
-                \$("#SubMenu a[href*='/home/shutdown/']").addClass('btn shutdown').html('<span class="submenu-icon-shutdown pull-left"></span> Shutdown');
-                \$("#SubMenu a[href*='/home/logout/']").addClass('btn').html('<span class="ui-icon ui-icon-power pull-left"></span> Logout');
-                \$("#SubMenu a:contains('Edit')").addClass('btn').html('<span class="ui-icon ui-icon-pencil pull-left"></span> Edit');
-                \$("#SubMenu a:contains('Remove')").addClass('btn remove').html('<span class="ui-icon ui-icon-trash pull-left"></span> Remove');
-                \$("#SubMenu a:contains('Clear History')").addClass('btn clearhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear History');
-                \$("#SubMenu a:contains('Trim History')").addClass('btn trimhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Trim History');
-                \$("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear Errors');
-                #if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD:
-                \$("#SubMenu a[href$='/errorlogs/submit_errors/']").addClass('btn submiterrors').html('<span class="ui-icon ui-icon-arrowreturnthick-1-n pull-left"></span> Submit Errors');
-                #end if
-                \$("#SubMenu a:contains('Re-scan')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Re-scan');
-                \$("#SubMenu a:contains('Backlog Overview')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Backlog Overview');
-                \$("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update PLEX');
-                \$("#SubMenu a:contains('Force')").addClass('btn').html('<span class="ui-icon ui-icon-transfer-e-w pull-left"></span> Force Full Update');
-                \$("#SubMenu a:contains('Rename')").addClass('btn').html('<span class="ui-icon ui-icon-tag pull-left"></span> Preview Rename');
-                \$("#SubMenu a[href$='/config/subtitles/']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Search Subtitles');
-                \$("#SubMenu a[href*='/home/subtitleShow']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Download Subtitles');
-                \$("#SubMenu a:contains('Anime')").addClass('btn').html('<span class="submenu-icon-anime pull-left"></span> Anime');
-                \$("#SubMenu a:contains('Settings')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Settings');
-                \$("#SubMenu a:contains('Provider')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Providers');
-                \$("#SubMenu a:contains('Backup/Restore')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> Backup/Restore');
-                \$("#SubMenu a:contains('General')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> General');
-                \$("#SubMenu a:contains('Episode Status')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Episode Status Management');
-                \$("#SubMenu a:contains('Missed Subtitle')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Missed Subtitles');
-                \$("#SubMenu a[href$='/home/addShows/']").addClass('btn').html('<span class="ui-icon ui-icon-video pull-left"></span> Add Show');
-                \$("#SubMenu a:contains('Processing')").addClass('btn').html('<span class="ui-icon ui-icon-folder-open pull-left"></span> Post-Processing');
-                \$("#SubMenu a:contains('Manage Searches')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Manage Searches');
-                \$("#SubMenu a:contains('Manage Torrents')").addClass('btn').html('<span class="submenu-icon-bittorrent pull-left"></span> Manage Torrents');
-                \$("#SubMenu a[href$='/manage/failedDownloads/']").addClass('btn').html('<span class="submenu-icon-failed-download pull-left"></span> Failed Downloads');
-                \$("#SubMenu a:contains('Notification')").addClass('btn').html('<span class="ui-icon ui-icon-note pull-left"></span> Notifications');
-                \$("#SubMenu a:contains('Update show in KODI')").addClass('btn').html('<span class="submenu-icon-kodi pull-left"></span> Update show in KODI');
-                \$("#SubMenu a[href$='/home/updateKODI/']").addClass('btn').html('<span class="submenu-icon-kodi pull-left"></span> Update KODI');
-                \$("#SubMenu a:contains('Update show in Emby')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update show in Emby');
-                \$("#SubMenu a[href$='/home/updateEMBY/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update Emby');
-                \$("#SubMenu a:contains('Pause')").addClass('btn').html('<span class="ui-icon ui-icon-pause pull-left"></span> Pause');
-                \$("#SubMenu a:contains('Resume')").addClass('btn').html('<span class="ui-icon ui-icon-play pull-left"></span> Resume');
-
-            }
-
-            \$(document).ready(function() {
-
-                initActions();
-
-                \$("#NAV$topmenu").addClass("active");
-
-                \$('.dropdown-toggle').dropdownHover();
-
-            });
-        //-->
-        </script>
-    <script type="text/javascript" src="$sbRoot/js/confirmations.js?$sbPID"></script>
-    #end if
-    </head>
-
-    <body>
-        <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
-            <div class="container-fluid">
-                <div class="navbar-header">
-                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
-                        <span class="sr-only">Toggle navigation</span>
-                        <span class="icon-bar"></span>
-                        <span class="icon-bar"></span>
-                        <span class="icon-bar"></span>
-                    </button>
-                    <a class="navbar-brand" href="$sbRoot/home/" title="SickRage"><img alt="SickRage" src="$sbRoot/images/sickrage.png" style="height: 50px;" class="img-responsive pull-left" /></a>
-                </div>
-
-            #if $sbLogin:
-                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
-                    <ul class="nav navbar-nav navbar-right">
-                        <li id="NAVnews">
-                            <a href="$sbRoot/news/">News</a>
-                        </li>
-                        <li id="NAVnews">
-                            <a href="$sbRoot/IRC/">IRC</a>
-                        </li>
-                        <li id="NAVhome">
-                            <a href="$sbRoot/home/">Shows</a>
-                        </li>
-
-                        <li id="NAVcomingEpisodes">
-                            <a href="$sbRoot/comingEpisodes/">Coming Episodes</a>
-                        </li>
-
-                        <li id="NAVhistory">
-                            <a href="$sbRoot/history/">History</a>
-                        </li>
-
-                        <li id="NAVmanage" class="dropdown">
-                            <a href="$sbRoot/manage/" class="dropdown-toggle" data-toggle="dropdown">Manage <b class="caret"></b></a>
-                            <ul class="dropdown-menu">
-                                <li><a href="$sbRoot/manage/"><i class="menu-icon-manage"></i>&nbsp;Mass Update</a></li>
-                                <li><a href="$sbRoot/manage/backlogOverview/"><i class="menu-icon-backlog-view"></i>&nbsp;Backlog Overview</a></li>
-                                <li><a href="$sbRoot/manage/manageSearches/"><i class="menu-icon-manage-searches"></i>&nbsp;Manage Searches</a></li>
-                                <li><a href="$sbRoot/manage/episodeStatuses/"><i class="menu-icon-backlog"></i>&nbsp;Episode Status Management</a></li>
-                            #if $sickbeard.USE_PLEX and $sickbeard.PLEX_SERVER_HOST != "":
-                                <li><a href="$sbRoot/home/updatePLEX/"><i class="menu-icon-backlog-view"></i>&nbsp;Update PLEX</a></li>
-                            #end if
-                            #if $sickbeard.USE_KODI and $sickbeard.KODI_HOST != "":
-                                <li><a href="$sbRoot/home/updateKODI/"><i class="menu-icon-kodi"></i>&nbsp;Update KODI</a></li>
-                            #end if
-                            #if $sickbeard.USE_EMBY and $sickbeard.EMBY_HOST != "" and $sickbeard.EMBY_APIKEY != "":
-                                <li><a href="$sbRoot/home/updateEMBY/"><i class="menu-icon-backlog-view"></i>&nbsp;Update Emby</a></li>
-                            #end if
-                            #if $sickbeard.USE_TORRENTS and $sickbeard.TORRENT_METHOD != 'blackhole' \
-                            and ($sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'https' \
-                            or not $sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'http:'):
-                                <li><a href="$sbRoot/manage/manageTorrents/"><i class="menu-icon-bittorrent"></i>&nbsp;Manage Torrents</a></li>
-                            #end if
-                            #if $sickbeard.USE_FAILED_DOWNLOADS:
-                                <li><a href="$sbRoot/manage/failedDownloads/"><i class="menu-icon-failed-download"></i>&nbsp;Failed Downloads</a></li>
-                            #end if
-                            #if $sickbeard.USE_SUBTITLES:
-                                <li><a href="$sbRoot/manage/subtitleMissed/"><i class="menu-icon-backlog"></i>&nbsp;Missed Subtitle Management</a></li>
-                            #end if
-                            </ul>
-                        </li>
-
-                        <li id="NAVerrorlogs" class="dropdown">
-                            <a href="$sbRoot/errorlogs/" class="dropdown-toggle" data-toggle="dropdown">$logPageTitle <b class="caret"></b></a>
-                            <ul class="dropdown-menu">
-                                <li><a href="$sbRoot/errorlogs/"><i class="menu-icon-viewlog-errors"></i>&nbsp;View Log (Errors)</a></li>
-                                <li><a href="$sbRoot/errorlogs/viewlog/"><i class="menu-icon-viewlog"></i>&nbsp;View Log</a></li>
-                            </ul>
-                        </li>
-
-                        <li id="NAVconfig" class="dropdown">
-                            <a href="$sbRoot/config/" class="dropdown-toggle" data-toggle="dropdown"><img src="$sbRoot/images/menu/system18.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">Config <b class="caret"></b></span></a>
-                            <ul class="dropdown-menu">
-                                <li><a href="$sbRoot/config/"><i class="menu-icon-help"></i>&nbsp;Help &amp; Info</a></li>
-                                <li><a href="$sbRoot/config/general/"><i class="menu-icon-config"></i>&nbsp;General</a></li>
-                                <li><a href="$sbRoot/config/backuprestore/"><i class="menu-icon-config"></i>&nbsp;Backup &amp; Restore</a></li>
-                                <li><a href="$sbRoot/config/search/"><i class="menu-icon-config"></i>&nbsp;Search Settings</a></li>
-                                <li><a href="$sbRoot/config/providers/"><i class="menu-icon-config"></i>&nbsp;Search Providers</a></li>
-                                <li><a href="$sbRoot/config/subtitles/"><i class="menu-icon-config"></i>&nbsp;Subtitles Settings</a></li>
-                                <li><a href="$sbRoot/config/postProcessing/"><i class="menu-icon-config"></i>&nbsp;Post Processing</a></li>
-                                <li><a href="$sbRoot/config/notifications/"><i class="menu-icon-config"></i>&nbsp;Notifications</a></li>
-                                <li><a href="$sbRoot/config/anime/"><i class="menu-icon-config"></i>&nbsp;Anime</a></li>
-                            </ul>
-                        </li>
-
-                        <li class="dropdown">
-                            <a href="#" class="dropdown-toggle" data-toggle="dropdown"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
-                            <ul class="dropdown-menu">
-                                <li><a href="$sbRoot/home/updateCheck?pid=$sbPID"><i class="menu-icon-update"></i>&nbsp;Check For Updates</a></li>
-                                <li><a href="$sbRoot/changes"><i class="menu-icon-help"></i>&nbsp;Changelog</a></li>
-                                <li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart"><i class="menu-icon-restart"></i>&nbsp;Restart</a></li>
-                                <li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm shutdown"><i class="menu-icon-shutdown"></i>&nbsp;Shutdown</a></li>
-                                <li><a href="$sbRoot/logout" class="confirm logout"><i class="menu-icon-shutdown"></i>&nbsp;Logout</a></li>
-                                <li><a href="$sbRoot/home/status/"><i class="menu-icon-help"></i>&nbsp;Server Status</a></li>
-                            </ul>
-                        </li>
-                        <li id="donate"><a href="https://github.com/SiCKRAGETV/SickRage/wiki/Donations" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href); return false;"><img src="$sbRoot/images/donate.jpg" alt="[donate]" class="navbaricon hidden-xs" /></a></li>
-                    </ul>
-            #end if
-                </div><!-- /.navbar-collapse -->
-            </div><!-- /.container-fluid -->
-        </nav>
-
-        #if $varExists('submenu'):
-        <div id="SubMenu">
-        <span>
-        #set $first = True
-        #for $menuItem in $submenu:
-            #if 'requires' not in $menuItem or $menuItem.requires():
-                  #if type($menuItem.path) == dict:
-                      #if $first then "" else "</span><span>"#<b>$menuItem.title</b>
-                      #set $first = False
-                      #set $inner_first = True
-                      #for $cur_link in $menuItem.path:
-                          #if $inner_first then "" else "&middot; "#<a class="inner" href="$sbRoot/$menuItem.path[$cur_link]">$cur_link</a>
-                          #set $inner_first = False
-                      #end for
-                  #else
-                      #if $first then "" else ""#<a href="$sbRoot/$menuItem.path" #if 'confirm' in $menuItem then "class=\"confirm\"" else "" #>$menuItem.title</a>
-                      #set $first = False
-                  #end if
-            #end if
-        #end for
-        </span>
-        </div>
-        #end if
-
-          #if $sickbeard.BRANCH and $sickbeard.BRANCH != 'master' and not $sickbeard.DEVELOPER and $sbLogin
-        <div class="alert alert-danger upgrade-notification" role="alert">
-            <span>You're using the $sickbeard.BRANCH branch. Please use 'master' unless specifically asked</span>
-        </div>
-        #end if
-
-        #if $sickbeard.NEWEST_VERSION_STRING and $sbLogin
-        <div class="alert alert-success upgrade-notification" role="alert">
-            <span>$sickbeard.NEWEST_VERSION_STRING</span>
-        </div>
-        #end if
-
-<div id="contentWrapper">
-    <div id="content">
diff --git a/gui/slick/interfaces/default/login.tmpl b/gui/slick/interfaces/default/login.mako
similarity index 69%
rename from gui/slick/interfaces/default/login.tmpl
rename to gui/slick/interfaces/default/login.mako
index 4bf403944234c073cb42d57a14b77c600b51aa35..011c9baee8a314e92743a7013148fe3fa8499afc 100644
--- a/gui/slick/interfaces/default/login.tmpl
+++ b/gui/slick/interfaces/default/login.mako
@@ -1,21 +1,12 @@
-#import sickbeard
-
-#set global $title="Login"
-
-#set global $sbPath = ".."
-
-#set global $topmenu="login"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
+<%include file="/inc_top.mako"/>
 <div class="login">
     <form action="" method="post">
         <h1>SickRage</h1>
         <div class="ctrlHolder"><input class="inlay" name="username" type="text" placeholder="Username" autocomplete="off" /></div>
         <div class="ctrlHolder"><input class="inlay" name="password" type="password" placeholder="Password" autocomplete="off" /></div>
         <div class="ctrlHolder">
-            <label class="remember_me" title="for 30 days"><input class="inlay" id="remember_me" name="remember_me" type="checkbox" value="1" checked="checked" /> Remember me</label>
+            <label class="remember_me" title="for 30 days"><input class="inlay" id="remember_me" name="remember_me" type="checkbox" value="1"checked="checked" /> Remember me</label>
             <input class="button" name="submit" type="submit" value="Login" />
         </div>
     </form>
-</div>
\ No newline at end of file
+</div>
diff --git a/gui/slick/interfaces/default/manage.mako b/gui/slick/interfaces/default/manage.mako
new file mode 100644
index 0000000000000000000000000000000000000000..ceb4bbb8f8b9c8f373f8b1a59dc8c4b128a6f5d9
--- /dev/null
+++ b/gui/slick/interfaces/default/manage.mako
@@ -0,0 +1,194 @@
+<%!
+    import sickbeard
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/lib/bootbox.min.js?${sbPID}"></script>
+<script type="text/javascript" charset="utf-8">
+$.tablesorter.addParser({
+    id: 'showNames',
+    is: function(s) {
+        return false;
+    },
+    format: function(s) {
+        % if not sickbeard.SORT_ARTICLE:
+            return (s || '').replace(/^(The|A|An)\s/i,'');
+        % else:
+            return (s || '');
+        % endif
+    },
+    type: 'text'
+});
+$.tablesorter.addParser({
+    id: 'quality',
+    is: function(s) {
+        return false;
+    },
+    format: function(s) {
+        return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('best',0).replace('custom',7);
+    },
+    type: 'numeric'
+});
+
+$(document).ready(function()
+{
+    $("#massUpdateTable:has(tbody tr)").tablesorter({
+        sortList: [[1,0]],
+        textExtraction: {
+            2: function(node) { return $(node).find("span").text().toLowerCase(); },
+            3: function(node) { return $(node).find("img").attr("alt"); },
+            4: function(node) { return $(node).find("img").attr("alt"); },
+            5: function(node) { return $(node).find("img").attr("alt"); },
+            6: function(node) { return $(node).find("img").attr("alt"); },
+            7: function(node) { return $(node).find("img").attr("alt"); },
+            8: function(node) { return $(node).find("img").attr("alt"); },
+        },
+        widgets: ['zebra'],
+        headers: {
+            0: { sorter: false},
+            1: { sorter: 'showNames'},
+            2: { sorter: 'quality'},
+            3: { sorter: 'sports'},
+            4: { sorter: 'scene'},
+            5: { sorter: 'anime'},
+            6: { sorter: 'flatfold'},
+            7: { sorter: 'paused'},
+            8: { sorter: 'subtitle'},
+            9: { sorter: 'default_ep_status'},
+           10: { sorter: 'status'},
+           11: { sorter: false},
+           12: { sorter: false},
+           13: { sorter: false},
+           14: { sorter: false},
+           15: { sorter: false},
+           16: { sorter: false},
+           17: { sorter: false}
+        }
+    });
+});
+</script>
+<script type="text/javascript" src="${sbRoot}/js/massUpdate.js?${sbPID}"></script>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+<form name="massUpdateForm" method="post" action="massUpdate">
+
+<table id="massUpdateTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
+    <thead>
+        <tr>
+            <th class="col-checkbox">Edit<br/><input type="checkbox" class="bulkCheck" id="editCheck" /></th>
+            <th class="nowrap" style="text-align: left;">Show Name</th>
+            <th class="col-legend">Quality</th>
+            <th class="col-legend">Sports</th>
+            <th class="col-legend">Scene</th>
+            <th class="col-legend">Anime</th>
+            <th class="col-legend">Flat Folders</th>
+            <th class="col-legend">Paused</th>
+            <th class="col-legend">Subtitle</th>
+            <th class="col-legend">Default Ep<br>Status</th>
+            <th class="col-legend">Status</th>
+            <th width="1%">Update<br/><input type="checkbox" class="bulkCheck" id="updateCheck" /></th>
+            <th width="1%">Rescan<br/><input type="checkbox" class="bulkCheck" id="refreshCheck" /></th>
+            <th width="1%">Rename<br/><input type="checkbox" class="bulkCheck" id="renameCheck" /></th>
+        % if sickbeard.USE_SUBTITLES:
+            <th width="1%">Search Subtitle<br/><input type="checkbox" class="bulkCheck" id="subtitleCheck" /></th>
+        % endif
+            <!-- <th>Force Metadata Regen <input type="checkbox" class="bulkCheck" id="metadataCheck" /></th>//-->
+            <th width="1%">Delete<br/><input type="checkbox" class="bulkCheck" id="deleteCheck" /></th>
+            <th width="1%">Remove<br/><input type="checkbox" class="bulkCheck" id="removeCheck" /></th>
+        </tr>
+    </thead>
+
+    <tfoot>
+        <tr>
+            <td rowspan="1" colspan="2" class="align-center alt"><input class="btn pull-left" type="button" value="Edit Selected" id="submitMassEdit" /></td>
+            <td rowspan="1" colspan="${(14, 15)[bool(sickbeard.USE_SUBTITLES)]}" class="align-right alt"><input class="btn pull-right" type="button" value="Submit" id="submitMassUpdate" /></td>
+        </tr>
+    </tfoot>
+
+    <tbody>
+        <% myShowList = sickbeard.showList %>
+        <% myShowList.sort(lambda x, y: cmp(x.name, y.name)) %>
+
+        % for curShow in myShowList:
+        <% curEp = curShow.nextaired %>
+        <% curUpdate_disabled = "" %>
+        <% curRefresh_disabled = "" %>
+        <% curRename_disabled = "" %>
+        <% curSubtitle_disabled = "" %>
+        <% curDelete_disabled = "" %>
+        <% curRemove_disabled = "" %>
+
+        % if sickbeard.showQueueScheduler.action.isBeingUpdated(curShow) or sickbeard.showQueueScheduler.action.isInUpdateQueue(curShow):
+            <% curUpdate_disabled = "disabled=\"disabled\" " %>
+        % endif
+
+        <% curUpdate = "<input type=\"checkbox\" class=\"updateCheck\" id=\"update-"+str(curShow.indexerid)+"\" "+curUpdate_disabled+"/>" %>
+
+        % if sickbeard.showQueueScheduler.action.isBeingRefreshed(curShow) or sickbeard.showQueueScheduler.action.isInRefreshQueue(curShow):
+            <% curRefresh_disabled = "disabled=\"disabled\" " %>
+        % endif
+
+        <% curRefresh = "<input type=\"checkbox\" class=\"refreshCheck\" id=\"refresh-"+str(curShow.indexerid)+"\" "+curRefresh_disabled+"/>" %>
+
+        % if sickbeard.showQueueScheduler.action.isBeingRenamed(curShow) or sickbeard.showQueueScheduler.action.isInRenameQueue(curShow):
+            <% curRename = "disabled=\"disabled\" " %>
+        % endif
+
+        <% curRename = "<input type=\"checkbox\" class=\"renameCheck\" id=\"rename-"+str(curShow.indexerid)+"\" "+curRename_disabled+"/>" %>
+
+        % if not curShow.subtitles or sickbeard.showQueueScheduler.action.isBeingSubtitled(curShow) or sickbeard.showQueueScheduler.action.isInSubtitleQueue(curShow):
+            <% curSubtitle_disabled = "disabled=\"disabled\" " %>
+        % endif
+
+        <% curSubtitle = "<input type=\"checkbox\" class=\"subtitleCheck\" id=\"subtitle-"+str(curShow.indexerid)+"\" "+curSubtitle_disabled+"/>" %>
+
+        % if sickbeard.showQueueScheduler.action.isBeingRenamed(curShow) or sickbeard.showQueueScheduler.action.isInRenameQueue(curShow) or sickbeard.showQueueScheduler.action.isInRefreshQueue(curShow):
+            <% curDelete = "disabled=\"disabled\" " %>
+        % endif
+
+        <% curDelete = "<input type=\"checkbox\" class=\"deleteCheck\" id=\"delete-"+str(curShow.indexerid)+"\" "+curDelete_disabled+"/>" %>
+
+        % if sickbeard.showQueueScheduler.action.isBeingRenamed(curShow) or sickbeard.showQueueScheduler.action.isInRenameQueue(curShow) or sickbeard.showQueueScheduler.action.isInRefreshQueue(curShow):
+            <% curRemove = "disabled=\"disabled\" " %>
+        % endif
+
+        <% curRemove = "<input type=\"checkbox\" class=\"removeCheck\" id=\"remove-"+str(curShow.indexerid)+"\" "+curRemove_disabled+"/>" %>
+        <tr>
+            <td align="center"><input type="checkbox" class="editCheck" id="edit-${curShow.indexerid}" /></td>
+            <td class="tvShow"><a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}">${curShow.name}</a></td>
+        % if curShow.quality in qualityPresets:
+            <td align="center"><span class="quality ${qualityPresetStrings[curShow.quality]}">${qualityPresetStrings[curShow.quality]}</span></td>
+        % else:
+            <td align="center"><span class="quality Custom">Custom</span></td>
+        % endif
+            <td align="center"><img src="${sbRoot}/images/${('no16.png" alt="N"', 'yes16.png" alt="Y"')[int(curShow.is_sports) == 1]} width="16" height="16" /></td>
+            <td align="center"><img src="${sbRoot}/images/${('no16.png" alt="N"', 'yes16.png" alt="Y"')[int(curShow.is_scene) == 1]} width="16" height="16" /></td>
+            <td align="center"><img src="${sbRoot}/images/${('no16.png" alt="N"', 'yes16.png" alt="Y"')[int(curShow.is_anime) == 1]} width="16" height="16" /></td>
+            <td align="center"><img src="${sbRoot}/images/${('no16.png" alt="N"', 'yes16.png" alt="Y"')[int(curShow.flatten_folders) == 1]} width="16" height="16" /></td>
+            <td align="center"><img src="${sbRoot}/images/${('no16.png" alt="N"', 'yes16.png" alt="Y"')[int(curShow.paused) == 1]} width="16" height="16" /></td>
+            <td align="center"><img src="${sbRoot}/images/${('no16.png" alt="N"', 'yes16.png" alt="Y"')[int(curShow.subtitles) == 1]} width="16" height="16" /></td>
+            <td align="center">${statusStrings[curShow.default_ep_status]}</td>
+            <td align="center">${curShow.status}</td>
+            <td align="center">${curUpdate}</td>
+            <td align="center">${curRefresh}</td>
+            <td align="center">${curRename}</td>
+        % if sickbeard.USE_SUBTITLES:
+            <td align="center">${curSubtitle}</td>
+        % endif
+            <td align="center">${curDelete}</td>
+            <td align="center">${curRemove}</td>
+        </tr>
+
+        % endfor
+
+    </tbody>
+
+</table>
+
+</form>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage.tmpl b/gui/slick/interfaces/default/manage.tmpl
deleted file mode 100644
index 2efba08e85e19593627c5dcc239fe1b90b886fa7..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage.tmpl
+++ /dev/null
@@ -1,202 +0,0 @@
-#import sickbeard
-#from sickbeard.common import *
-#set global $title="Mass Update"
-#set global $header="Mass Update"
-
-#set global $sbPath="../.."
-
-#set global $topmenu="manage"
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/lib/bootbox.min.js?$sbPID"></script>
-<script type="text/javascript" charset="utf-8">
-<!--
-\$.tablesorter.addParser({
-    id: 'showNames',
-    is: function(s) {
-        return false;
-    },
-    format: function(s) {
-        #if not $sickbeard.SORT_ARTICLE:
-            return (s || '').replace(/^(The|A|An)\s/i,'');
-        #else:
-            return (s || '');
-        #end if
-    },
-    type: 'text'
-});
-\$.tablesorter.addParser({
-    id: 'quality',
-    is: function(s) {
-        return false;
-    },
-    format: function(s) {
-        return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('best',0).replace('custom',7);
-    },
-    type: 'numeric'
-});
-
-\$(document).ready(function()
-{
-    \$("#massUpdateTable:has(tbody tr)").tablesorter({
-        sortList: [[1,0]],
-        textExtraction: {
-            2: function(node) { return \$(node).find("span").text().toLowerCase(); },
-            3: function(node) { return \$(node).find("img").attr("alt"); },
-            4: function(node) { return \$(node).find("img").attr("alt"); },
-            5: function(node) { return \$(node).find("img").attr("alt"); },
-            6: function(node) { return \$(node).find("img").attr("alt"); },
-            7: function(node) { return \$(node).find("img").attr("alt"); },
-            8: function(node) { return \$(node).find("img").attr("alt"); },
-        },
-        widgets: ['zebra'],
-        headers: {
-            0: { sorter: false},
-            1: { sorter: 'showNames'},
-            2: { sorter: 'quality'},
-            3: { sorter: 'sports'},
-            4: { sorter: 'scene'},
-            5: { sorter: 'anime'},
-            6: { sorter: 'flatfold'},
-            7: { sorter: 'paused'},
-            8: { sorter: 'subtitle'},
-            9: { sorter: 'default_ep_status'},
-           10: { sorter: 'status'},
-           11: { sorter: false},
-           12: { sorter: false},
-           13: { sorter: false},
-           14: { sorter: false},
-           15: { sorter: false},
-           16: { sorter: false},
-           17: { sorter: false}
-        }
-    });
-});
-//-->
-</script>
-<script type="text/javascript" src="$sbRoot/js/massUpdate.js?$sbPID"></script>
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-<form name="massUpdateForm" method="post" action="massUpdate">
-
-<table id="massUpdateTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
-    <thead>
-        <tr>
-            <th class="col-checkbox">Edit<br/><input type="checkbox" class="bulkCheck" id="editCheck" /></th>
-            <th class="nowrap" style="text-align: left;">Show Name</th>
-            <th class="col-legend">Quality</th>
-            <th class="col-legend">Sports</th>
-            <th class="col-legend">Scene</th>
-            <th class="col-legend">Anime</th>
-            <th class="col-legend">Flat Folders</th>
-            <th class="col-legend">Paused</th>
-            <th class="col-legend">Subtitle</th>
-            <th class="col-legend">Default Ep<br>Status</th>
-            <th class="col-legend">Status</th>
-            <th width="1%">Update<br/><input type="checkbox" class="bulkCheck" id="updateCheck" /></th>
-            <th width="1%">Rescan<br/><input type="checkbox" class="bulkCheck" id="refreshCheck" /></th>
-            <th width="1%">Rename<br/><input type="checkbox" class="bulkCheck" id="renameCheck" /></th>
-        #if $sickbeard.USE_SUBTITLES:
-            <th width="1%">Search Subtitle<br/><input type="checkbox" class="bulkCheck" id="subtitleCheck" /></th>
-        #end if
-            <!-- <th>Force Metadata Regen <input type="checkbox" class="bulkCheck" id="metadataCheck" /></th>//-->
-            <th width="1%">Delete<br/><input type="checkbox" class="bulkCheck" id="deleteCheck" /></th>
-            <th width="1%">Remove<br/><input type="checkbox" class="bulkCheck" id="removeCheck" /></th>
-        </tr>
-    </thead>
-
-    <tfoot>
-        <tr>
-            <td rowspan="1" colspan="2" class="align-center alt"><input class="btn pull-left" type="button" value="Edit Selected" id="submitMassEdit" /></td>
-            <td rowspan="1" colspan="#if $sickbeard.USE_SUBTITLES then 15 else 14#" class="align-right alt"><input class="btn pull-right" type="button" value="Submit" id="submitMassUpdate" /></td>
-        </tr>
-    </tfoot>
-
-    <tbody>
-        #set $myShowList = $sickbeard.showList
-        $myShowList.sort(lambda x, y: cmp(x.name, y.name))
-
-        #for $curShow in $myShowList:
-            #set $curEp = $curShow.nextaired
-            #set $curUpdate_disabled = ""
-            #set $curRefresh_disabled = ""
-            #set $curRename_disabled = ""
-            #set $curSubtitle_disabled = ""
-            #set $curDelete_disabled = ""
-            #set $curRemove_disabled = ""
-
-        #if $sickbeard.showQueueScheduler.action.isBeingUpdated($curShow) or $sickbeard.showQueueScheduler.action.isInUpdateQueue($curShow):
-            #set $curUpdate_disabled = "disabled=\"disabled\" "
-        #end if
-
-        #set $curUpdate = "<input type=\"checkbox\" class=\"updateCheck\" id=\"update-"+str($curShow.indexerid)+"\" "+$curUpdate_disabled+"/>"
-
-        #if $sickbeard.showQueueScheduler.action.isBeingRefreshed($curShow) or $sickbeard.showQueueScheduler.action.isInRefreshQueue($curShow):
-            #set $curRefresh_disabled = "disabled=\"disabled\" "
-        #end if
-
-        #set $curRefresh = "<input type=\"checkbox\" class=\"refreshCheck\" id=\"refresh-"+str($curShow.indexerid)+"\" "+$curRefresh_disabled+"/>"
-
-        #if $sickbeard.showQueueScheduler.action.isBeingRenamed($curShow) or $sickbeard.showQueueScheduler.action.isInRenameQueue($curShow):
-            #set $curRename = "disabled=\"disabled\" "
-        #end if
-
-        #set $curRename = "<input type=\"checkbox\" class=\"renameCheck\" id=\"rename-"+str($curShow.indexerid)+"\" "+$curRename_disabled+"/>"
-
-        #if not $curShow.subtitles or $sickbeard.showQueueScheduler.action.isBeingSubtitled($curShow) or $sickbeard.showQueueScheduler.action.isInSubtitleQueue($curShow):
-            #set $curSubtitle_disabled = "disabled=\"disabled\" "
-        #end if
-
-        #set $curSubtitle = "<input type=\"checkbox\" class=\"subtitleCheck\" id=\"subtitle-"+str($curShow.indexerid)+"\" "+$curSubtitle_disabled+"/>"
-
-        #if $sickbeard.showQueueScheduler.action.isBeingRenamed($curShow) or $sickbeard.showQueueScheduler.action.isInRenameQueue($curShow) or $sickbeard.showQueueScheduler.action.isInRefreshQueue($curShow):
-            #set $curDelete = "disabled=\"disabled\" "
-        #end if
-
-        #set $curDelete = "<input type=\"checkbox\" class=\"deleteCheck\" id=\"delete-"+str($curShow.indexerid)+"\" "+$curDelete_disabled+"/>"
-
-        #if $sickbeard.showQueueScheduler.action.isBeingRenamed($curShow) or $sickbeard.showQueueScheduler.action.isInRenameQueue($curShow) or $sickbeard.showQueueScheduler.action.isInRefreshQueue($curShow):
-            #set $curRemove = "disabled=\"disabled\" "
-        #end if
-
-        #set $curRemove = "<input type=\"checkbox\" class=\"removeCheck\" id=\"remove-"+str($curShow.indexerid)+"\" "+$curRemove_disabled+"/>"
-
-        <tr>
-            <td align="center"><input type="checkbox" class="editCheck" id="edit-$curShow.indexerid" /></td>
-            <td class="tvShow"><a href="$sbRoot/home/displayShow?show=$curShow.indexerid">$curShow.name</a></td>
-        #if $curShow.quality in $qualityPresets:
-            <td align="center"><span class="quality $qualityPresetStrings[$curShow.quality]">$qualityPresetStrings[$curShow.quality]</span></td>
-        #else:
-            <td align="center"><span class="quality Custom">Custom</span></td>
-        #end if
-            <td align="center"><img src="$sbRoot/images/#if int($curShow.is_sports) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
-            <td align="center"><img src="$sbRoot/images/#if int($curShow.is_scene) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
-            <td align="center"><img src="$sbRoot/images/#if int($curShow.is_anime) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
-            <td align="center"><img src="$sbRoot/images/#if int($curShow.flatten_folders) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
-            <td align="center"><img src="$sbRoot/images/#if int($curShow.paused) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
-            <td align="center"><img src="$sbRoot/images/#if int($curShow.subtitles) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
-            <td align="center">$statusStrings[$curShow.default_ep_status]</td>
-            <td align="center">$curShow.status</td>
-            <td align="center">$curUpdate</td>
-            <td align="center">$curRefresh</td>
-            <td align="center">$curRename</td>
-        #if $sickbeard.USE_SUBTITLES:
-            <td align="center">$curSubtitle</td>
-        #end if
-            <td align="center">$curDelete</td>
-            <td align="center">$curRemove</td>
-        </tr>
-
-        #end for
-
-    </tbody>
-
-</table>
-
-</form>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/manage_backlogOverview.mako b/gui/slick/interfaces/default/manage_backlogOverview.mako
new file mode 100644
index 0000000000000000000000000000000000000000..5ff215cc29eb06f5b6345967feac8c8501a2d485
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_backlogOverview.mako
@@ -0,0 +1,107 @@
+<%
+    import sickbeard
+    import datetime
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Overview, Quality, qualityPresets, qualityPresetStrings
+    from sickbeard import sbdatetime, network_timezones
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript">
+$(document).ready(function(){
+    $('#pickShow').change(function(){
+        var id = $(this).val();
+        if (id) {
+            $('html,body').animate({scrollTop: $('#show-' + id).offset().top -25},'slow');
+        }
+    });
+
+    <% fuzzydate = 'airdate' %>
+    % if sickbeard.FUZZY_DATING:
+    fuzzyMoment({
+        containerClass : '.${fuzzydate}',
+        dateHasTime : false,
+        dateFormat : '${sickbeard.DATE_PRESET}',
+        timeFormat : '${sickbeard.TIME_PRESET}',
+        trimZero : ${('False', 'True')[bool(sickbeard.TRIM_ZERO)]}
+    });
+    % endif
+
+});
+</script>
+
+<div id="content960">
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<% totalWanted = 0 %>
+<% totalQual = 0 %>
+
+% for curShow in sickbeard.showList:
+    <% totalWanted = totalWanted + showCounts[curShow.indexerid][Overview.WANTED] %>
+    <% totalQual = totalQual + showCounts[curShow.indexerid][Overview.QUAL] %>
+% endfor
+
+<div class="h2footer pull-right">
+    <span class="listing-key wanted">Wanted: <b>${totalWanted}</b></span>
+    <span class="listing-key qual">Low Quality: <b>${totalQual}</b></span>
+</div><br/>
+
+<div class="float-left">
+Jump to Show
+    <select id="pickShow" class="form-control form-control-inline input-sm">
+    % for curShow in sorted(sickbeard.showList, key=lambda x: x.name):
+        % if showCounts[curShow.indexerid][Overview.QUAL] + showCounts[curShow.indexerid][Overview.WANTED] != 0:
+        <option value="${curShow.indexerid}">${curShow.name}</option>
+        % endif
+    % endfor
+    </select>
+</div>
+
+<table class="sickbeardTable" cellspacing="0" border="0" cellpadding="0">
+% for curShow in sorted(sickbeard.showList, key=lambda x: x.name):
+
+    % if showCounts[curShow.indexerid][Overview.QUAL] + showCounts[curShow.indexerid][Overview.WANTED] == 0:
+        <% continue %>
+    % endif
+    <tr class="seasonheader" id="show-${curShow.indexerid}">
+        <td colspan="3" class="align-left">
+            <br/><h2><a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}">${curShow.name}</a></h2>
+            <div class="pull-right">
+                <span class="listing-key wanted">Wanted: <b>${showCounts[curShow.indexerid][Overview.WANTED]}</b></span>
+                <span class="listing-key qual">Low Quality: <b>${showCounts[curShow.indexerid][Overview.QUAL]}</b></span>
+                <a class="btn btn-inline forceBacklog" href="${sbRoot}/manage/backlogShow?indexer_id=${curShow.indexerid}"><i class="icon-play-circle icon-white"></i> Force Backlog</a>
+            </div>
+        </td>
+    </tr>
+
+    <tr class="seasoncols"><th>Episode</th><th>Name</th><th class="nowrap">Airdate</th></tr>
+
+    % for curResult in showSQLResults[curShow.indexerid]:
+        <% whichStr = str(curResult['season']) + 'x' + str(curResult['episode']) %>
+        % try:
+            <% overview = showCats[curShow.indexerid][whichStr] %>
+        % except Exception:
+            <% continue %>
+        % endtry
+
+        % if overview not in (Overview.QUAL, Overview.WANTED):
+            <% continue %>
+        % endif
+
+        <tr class="seasonstyle ${Overview.overviewStrings[showCats[curShow.indexerid][whichStr]]}">
+            <td class="tableleft" align="center">${whichStr}</td>
+            <td class="tableright" align="center" class="nowrap">
+                <div class="${fuzzydate}">${curResult["name"]}</div>
+            </td>
+            <td><div>${(sbdatetime.sbdatetime.sbfdate(sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(curResult['airdate'], curShow.airs, curShow.network))), 'never')[int(curResult['airdate']) == 1]}</div></td>
+        </tr>
+    % endfor
+% endfor
+
+</table>
+</div>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_backlogOverview.tmpl b/gui/slick/interfaces/default/manage_backlogOverview.tmpl
deleted file mode 100644
index 7d8f76f09d7ed379d56a86a199dda8f533ff794a..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_backlogOverview.tmpl
+++ /dev/null
@@ -1,116 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard.common import *
-#from sickbeard import sbdatetime, network_timezones
-#set global $title = 'Backlog Overview'
-#set global $header = 'Backlog Overview'
-
-#set global $sbPath = '..'
-
-#set global $topmenu = 'manage'#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
-
-<script type="text/javascript">
-<!--
-\$(document).ready(function()
-{
-    \$('#pickShow').change(function(){
-        var id = \$(this).val();
-        if (id) {
-            \$('html,body').animate({scrollTop: \$('#show-' + id).offset().top -25},'slow');
-        }
-    });
-
-    #set $fuzzydate = 'airdate'
-    #if $sickbeard.FUZZY_DATING:
-    fuzzyMoment({
-        containerClass : '.${fuzzydate}',
-        dateHasTime : false,
-        dateFormat : '${sickbeard.DATE_PRESET}',
-        timeFormat : '${sickbeard.TIME_PRESET}',
-        trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#
-    });
-    #end if
-
-});
-//-->
-</script>
-
-<div id="content960">
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-#set $totalWanted = 0
-#set $totalQual = 0
-
-#for $curShow in $sickbeard.showList:
-#set $totalWanted = $totalWanted + $showCounts[$curShow.indexerid][$Overview.WANTED]
-#set $totalQual = $totalQual + $showCounts[$curShow.indexerid][$Overview.QUAL]
-#end for
-
-<div class="h2footer pull-right">
-    <span class="listing-key wanted">Wanted: <b>$totalWanted</b></span>
-    <span class="listing-key qual">Low Quality: <b>$totalQual</b></span>
-</div><br/>
-
-<div class="float-left">
-Jump to Show
-    <select id="pickShow" class="form-control form-control-inline input-sm">
-    #for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
-    #if $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED] != 0:
-    <option value="$curShow.indexerid">$curShow.name</option>
-    #end if
-    #end for
-</select>
-</div>
-
-<table class="sickbeardTable" cellspacing="0" border="0" cellpadding="0">
-
-#for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
-
-#if $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED] == 0:
-#continue
-#end if
-
-    <tr class="seasonheader" id="show-$curShow.indexerid">
-        <td colspan="3" class="align-left">
-            <br/><h2><a href="$sbRoot/home/displayShow?show=$curShow.indexerid">$curShow.name</a></h2>
-            <div class="pull-right">
-                <span class="listing-key wanted">Wanted: <b>$showCounts[$curShow.indexerid][$Overview.WANTED]</b></span>
-                <span class="listing-key qual">Low Quality: <b>$showCounts[$curShow.indexerid][$Overview.QUAL]</b></span>
-                <a class="btn btn-inline forceBacklog" href="$sbRoot/manage/backlogShow?indexer_id=$curShow.indexerid"><i class="icon-play-circle icon-white"></i> Force Backlog</a>
-            </div>
-        </td>
-    </tr>
-
-    <tr class="seasoncols"><th>Episode</th><th>Name</th><th class="nowrap">Airdate</th></tr>
-
-#for $curResult in $showSQLResults[$curShow.indexerid]:
-    #set $whichStr = $str($curResult['season']) + 'x' + $str($curResult['episode'])
-    #try:
-        #set $overview = $showCats[$curShow.indexerid][$whichStr]
-    #except Exception
-        #continue
-    #end try
-
-    #if $overview not in ($Overview.QUAL, $Overview.WANTED):
-        #continue
-    #end if
-
-    <tr class="seasonstyle $Overview.overviewStrings[$showCats[$curShow.indexerid][$whichStr]]">
-        <td class="tableleft" align="center">$whichStr</td>
-        <td>$curResult["name"]</td>
-        <td class="tableright" align="center" class="nowrap"><div class="${fuzzydate}">#if int($curResult['airdate']) == 1 then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($curResult['airdate'],$curShow.airs,$curShow.network)))#</div></td>
-    </tr>
-#end for
-
-#end for
-
-</table>
-</div>
-
-#include $os.path.join($sickbeard.PROG_DIR,'gui/slick/interfaces/default/inc_bottom.tmpl')
diff --git a/gui/slick/interfaces/default/manage_episodeStatuses.mako b/gui/slick/interfaces/default/manage_episodeStatuses.mako
new file mode 100644
index 0000000000000000000000000000000000000000..2b0d025f9f3d0946be50560c0d7d638e4762709c
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_episodeStatuses.mako
@@ -0,0 +1,87 @@
+<%!
+    from sickbeard import common
+%>
+<%include file="/inc_top.mako"/>
+
+<div id="content960">
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+% if not whichStatus or (whichStatus and not ep_counts):
+
+% if whichStatus:
+<h2>None of your episodes have status ${common.statusStrings[int(whichStatus)]}</h2>
+<br />
+% endif
+
+<form action="${sbRoot}/manage/episodeStatuses" method="get">
+Manage episodes with status <select name="whichStatus" class="form-control form-control-inline input-sm">
+% for curStatus in [common.SKIPPED, common.SNATCHED, common.WANTED, common.ARCHIVED, common.IGNORED]:
+<option value="${curStatus}">${common.statusStrings[curStatus]}</option>
+% endfor
+</select>
+<input class="btn btn-inline" type="submit" value="Manage" />
+</form>
+
+% else:
+
+<script type="text/javascript" src="${sbRoot}/js/manageEpisodeStatuses.js?${sbPID}"></script>
+
+<form action="${sbRoot}/manage/changeEpisodeStatuses" method="post">
+<input type="hidden" id="oldStatus" name="oldStatus" value="${whichStatus}" />
+
+<h2>Shows containing ${common.statusStrings[int(whichStatus)]} episodes</h2>
+
+<br />
+
+<%
+    if int(whichStatus) in [common.ARCHIVED, common.IGNORED, common.SNATCHED]:
+        row_class = "good"
+    else:
+        row_class = common.Overview.overviewStrings[int(whichStatus)]
+%>
+
+<input type="hidden" id="row_class" value="${row_class}" />
+
+Set checked shows/episodes to <select name="newStatus" class="form-control form-control-inline input-sm">
+<%
+    statusList = [common.SKIPPED, common.WANTED, common.ARCHIVED, common.IGNORED]
+    if int(whichStatus) in statusList:
+        statusList.remove(int(whichStatus))
+
+    if int(whichStatus) in [common.SNATCHED, common.SNATCHED_PROPER, common.SNATCHED_BEST]:
+        statusList.append(common.FAILED)
+%>
+
+% for curStatus in statusList:
+<option value="${curStatus}">${common.statusStrings[curStatus]}</option>
+% endfor
+
+</select>
+
+<input class="btn btn-inline" type="submit" value="Go" />
+
+<div>
+    <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
+    <button type="button" class="btn btn-xs unselectAllShows">Clear all</a></button>
+</div>
+<br />
+
+<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
+    % for cur_indexer_id in sorted_show_ids:
+    <tr id="${cur_indexer_id}">
+        <th><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th>
+        <th colspan="2" style="width: 100%; text-align: left;"><a class="whitelink" href="${sbRoot}/home/displayShow?show=${cur_indexer_id}">${show_names[cur_indexer_id]}</a> (${ep_counts[cur_indexer_id]}) <input type="button" class="pull-right get_more_eps btn" id="${cur_indexer_id}" value="Expand" /></th>
+    </tr>
+    % endfor
+</table>
+</form>
+
+% endif
+</div>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_episodeStatuses.tmpl b/gui/slick/interfaces/default/manage_episodeStatuses.tmpl
deleted file mode 100644
index 90a4e2dc87ceed5e795989d4c34c7ff8f4307c3b..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_episodeStatuses.tmpl
+++ /dev/null
@@ -1,89 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard import common
-#set global $title="Episode Overview"
-#set global $header="Episode Overview"
-
-#set global $sbPath=".."
-
-#set global $topmenu="manage"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<div id="content960">
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-#if not $whichStatus or ($whichStatus and not $ep_counts):
-
-#if $whichStatus:
-<h2>None of your episodes have status $common.statusStrings[$int($whichStatus)]</h2>
-<br />
-#end if
-
-<form action="$sbRoot/manage/episodeStatuses" method="get">
-Manage episodes with status <select name="whichStatus" class="form-control form-control-inline input-sm">
-#for $curStatus in [$common.SKIPPED, $common.SNATCHED, $common.WANTED, $common.ARCHIVED, $common.IGNORED]:
-<option value="$curStatus">$common.statusStrings[$curStatus]</option>
-#end for
-</select>
-<input class="btn btn-inline" type="submit" value="Manage" />
-</form>
-
-#else
-
-<script type="text/javascript" src="$sbRoot/js/manageEpisodeStatuses.js?$sbPID"></script>
-
-<form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
-<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus" />
-
-<h2>Shows containing $common.statusStrings[$int($whichStatus)] episodes</h2>
-
-<br />
-
-#if $whichStatus in ($common.ARCHIVED, $common.IGNORED, $common.SNATCHED):
-#set $row_class = "good"
-#else
-#set $row_class = $common.Overview.overviewStrings[$whichStatus]
-#end if
-<input type="hidden" id="row_class" value="$row_class" />
-
-Set checked shows/episodes to <select name="newStatus" class="form-control form-control-inline input-sm">
-#set $statusList = [$common.SKIPPED, $common.WANTED, $common.ARCHIVED, $common.IGNORED]
-#if $int($whichStatus) in $statusList
-$statusList.remove($int($whichStatus))
-#end if
-
-#if $int($whichStatus) in [$common.SNATCHED, $common.SNATCHED_PROPER]
-$statusList.append($common.FAILED)
-#end if
-
-#for $curStatus in $statusList:
-<option value="$curStatus">$common.statusStrings[$curStatus]</option>
-#end for
-</select>
-<input class="btn btn-inline" type="submit" value="Go" />
-
-<div>
-    <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
-    <button type="button" class="btn btn-xs unselectAllShows">Clear all</a></button>
-</div>
-<br />
-
-<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
-#for $cur_indexer_id in $sorted_show_ids:
- <tr id="$cur_indexer_id">
-  <th><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all" checked="checked" /></th>
-  <th colspan="2" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th>
- </tr>
-#end for
-</table>
-</form>
-
-#end if
-</div>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/manage_failedDownloads.mako b/gui/slick/interfaces/default/manage_failedDownloads.mako
new file mode 100644
index 0000000000000000000000000000000000000000..6adde96bab4941a3cbb4730a22747625c2995bee
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_failedDownloads.mako
@@ -0,0 +1,83 @@
+<%!
+    import sickbeard
+    import os.path
+    import datetime
+    import re
+    from sickbeard import providers
+    from sickbeard.providers import generic
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings, Overview
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript">
+$(document).ready(function(){
+    $("#failedTable:has(tbody tr)").tablesorter({
+        widgets: ['zebra'],
+        sortList: [[0,0]],
+        headers: { 3: { sorter: false } }
+    });
+    $('#limit').change(function(){
+        url = '${sbRoot}/manage/failedDownloads/?limit='+$(this).val()
+        window.location.href = url
+    });
+});
+</script>
+<script type="text/javascript" src="${sbRoot}/js/failedDownloads.js?${sbPID}"></script>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div class="h2footer pull-right"><b>Limit:</b>
+    <select name="limit" id="limit" class="form-control form-control-inline input-sm">
+        <option value="100" ${('', 'selected="selected"')[limit == '100']}>100</option>
+        <option value="250" ${('', 'selected="selected"')[limit == '250']}>250</option>
+        <option value="500" ${('', 'selected="selected"')[limit == '500']}>500</option>
+        <option value="0" ${('', 'selected="selected"')[limit == '0']}>All</option>
+    </select>
+</div>
+
+<table id="failedTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
+  <thead>
+    <tr>
+      <th class="nowrap" width="75%" style="text-align: left;">Release</th>
+      <th width="10%">Size</th>
+      <th width="14%">Provider</th>
+      <th width="1%">Remove<br />
+          <input type="checkbox" class="bulkCheck" id="removeCheck" />
+      </th>
+    </tr>
+  </thead>
+  <tfoot>
+    <tr>
+      <td rowspan="1" colspan="4"><input type="button" class="btn pull-right" value="Submit" id="submitMassRemove"></td>
+    </tr>
+  </tfoot>
+  <tbody>
+% for hItem in failedResults:
+<% curRemove  = "<input type=\"checkbox\" class=\"removeCheck\" id=\"remove-"+hItem["release"]+"\" />" %>
+  <tr>
+    <td class="nowrap">${hItem["release"]}</td>
+    <td align="center">
+    % if hItem["size"] != -1:
+        ${hItem["size"]}
+    % else:
+        ?
+    % endif
+    </td>
+    <td align="center">
+    <% provider = providers.getProviderClass(generic.GenericProvider.makeID(hItem["provider"])) %>
+    % if provider != None:
+        <img src="${sbRoot}/images/providers/${provider.imageName()}" width="16" height="16" alt="${provider.name}" title="${provider.name}"/>
+    % else:
+        <img src="${sbRoot}/images/providers/missing.png" width="16" height="16" alt="missing provider" title="missing provider"/>
+    % endif
+    </td>
+    <td align="center">${curRemove}</td>
+  </tr>
+% endfor
+  </tbody>
+</table>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_failedDownloads.tmpl b/gui/slick/interfaces/default/manage_failedDownloads.tmpl
deleted file mode 100644
index d7070514babd941fe26a6c5be0903af93fb1b540..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_failedDownloads.tmpl
+++ /dev/null
@@ -1,91 +0,0 @@
-#import sickbeard
-#import os.path
-#import datetime
-#import re
-#from sickbeard import providers
-#from sickbeard.providers import generic
-#from sickbeard.common import *
-#set global $header="Failed Downloads"
-#set global $title="Failed Downloads"
-
-#set global $sbPath=".."
-
-#set global $topmenu="manage"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript">
-<!--
-\$(document).ready(function()
-{
-    \$("#failedTable:has(tbody tr)").tablesorter({
-        widgets: ['zebra'],
-        sortList: [[0,0]],
-        headers: { 3: { sorter: false } }
-    });
-    \$('#limit').change(function(){
-        url = '$sbRoot/manage/failedDownloads/?limit='+\$(this).val()
-        window.location.href = url
-    });
-});
-//-->
-</script>
-<script type="text/javascript" src="$sbRoot/js/failedDownloads.js?$sbPID"></script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<div class="h2footer pull-right"><b>Limit:</b>
-    <select name="limit" id="limit" class="form-control form-control-inline input-sm">
-        <option value="100" #if $limit == "100" then "selected=\"selected\"" else ""#>100</option>
-        <option value="250" #if $limit == "250" then "selected=\"selected\"" else ""#>250</option>
-        <option value="500" #if $limit == "500" then "selected=\"selected\"" else ""#>500</option>
-        <option value="0" #if $limit == "0" then "selected=\"selected\"" else ""#>All</option>
-    </select>
-</div>
-
-<table id="failedTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
-  <thead>
-    <tr>
-      <th class="nowrap" width="75%" style="text-align: left;">Release</th>
-      <th width="10%">Size</th>
-      <th width="14%">Provider</th>
-      <th width="1%">Remove<br />
-        <input type="checkbox" class="bulkCheck" id="removeCheck" />
-      </th>
-    </tr>
-  </thead>
-  <tfoot>
-    <tr>
-      <td rowspan="1" colspan="4"><input type="button" class="btn pull-right" value="Submit" id="submitMassRemove"></td>
-    </tr>
-  </tfoot>
-  <tbody>
-#for $hItem in $failedResults:
-#set $curRemove  = "<input type=\"checkbox\" class=\"removeCheck\" id=\"remove-"+$hItem["release"]+"\" />"
-  <tr>
-    <td class="nowrap">$hItem["release"]</td>
-    <td align="center">
-    #if $hItem["size"] != -1
-        $hItem["size"]
-    #else
-        ?
-    #end if
-    </td>
-       <td align="center">
-    #set $provider = $providers.getProviderClass($generic.GenericProvider.makeID($hItem["provider"]))
-    #if $provider != None:
-        <img src="$sbRoot/images/providers/<%=provider.imageName()%>" width="16" height="16" alt="$provider.name" title="$provider.name"/>
-    #else:
-        <img src="$sbRoot/images/providers/missing.png" width="16" height="16" alt="missing provider" title="missing provider"/>
-    #end if
-    </td>
-    <td align="center">$curRemove</td>
-  </tr>
-#end for
-  </tbody>
-</table>
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/manage_manageSearches.mako b/gui/slick/interfaces/default/manage_manageSearches.mako
new file mode 100644
index 0000000000000000000000000000000000000000..aed40d90a160f2d2a0794a1e17c0b9b72d8ae94f
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_manageSearches.mako
@@ -0,0 +1,53 @@
+<%!
+    import sickbeard
+    import datetime
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script>
+<div id="content800">
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div id="summary2" class="align-left">
+<h3>Backlog Search:</h3>
+<a class="btn" href="${sbRoot}/manage/manageSearches/forceBacklog"><i class="icon-exclamation-sign"></i> Force</a>
+<a class="btn" href="${sbRoot}/manage/manageSearches/pauseBacklog?paused=${('1', '0')[bool(backlogPaused)]}"><i class="icon-${('paused', 'play')[bool(backlogPaused)]}"></i> ${('pause', 'Unpause')[bool(backlogPaused)]}</a>
+% if not backlogRunning:
+    Not in progress<br />
+% else:
+    ${('', 'Paused:')[bool(backlogPaused)]}
+    Currently running<br />
+% endif
+<br />
+
+<h3>Daily Search:</h3>
+<a class="btn" href="${sbRoot}/manage/manageSearches/forceSearch"><i class="icon-exclamation-sign"></i> Force</a>
+% if not dailySearchStatus:
+    Not in progress<br />
+% else:
+    In Progress<br />
+% endif
+<br />
+
+<h3>Find Propers Search:</h3>
+<a class="btn ${('disabled', '')[bool(sickbeard.DOWNLOAD_PROPERS)]}" href="${sbRoot}/manage/manageSearches/forceFindPropers"><i class="icon-exclamation-sign"></i> Force</a>
+% if not findPropersStatus:
+    Not in progress<br />
+% else:
+    In Progress<br />
+% endif
+<br />
+
+<h3>Search Queue:</h3>
+Backlog: <i>${queueLength['backlog']} pending items</i></br>
+Daily: <i>${queueLength['daily']} pending items</i></br>
+Manual: <i>${queueLength['manual']} pending items</i></br>
+Failed: <i>${queueLength['failed']} pending items</i></br>
+</div>
+</div>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_manageSearches.tmpl b/gui/slick/interfaces/default/manage_manageSearches.tmpl
deleted file mode 100644
index 03f421370f626f4833f9666fb13531fcd8678cf4..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_manageSearches.tmpl
+++ /dev/null
@@ -1,57 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard.common import *
-#set global $title="Manage Searches"
-#set global $header="Manage Searches"
-#set global $sbPath=".."
-
-#set global $topmenu="manage"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
-<div id="content800">
-   #if $varExists('header')
-       <h1 class="header">$header</h1>
-   #else
-       <h1 class="title">$title</h1>
-   #end if
-
-<div id="summary2" class="align-left">
-<h3>Backlog Search:</h3>
-<a class="btn" href="$sbRoot/manage/manageSearches/forceBacklog"><i class="icon-exclamation-sign"></i> Force</a>
-<a class="btn" href="$sbRoot/manage/manageSearches/pauseBacklog?paused=#if $backlogPaused then "0" else "1"#"><i class="#if $backlogPaused then "icon-play" else "icon-pause"#"></i> #if $backlogPaused then "Unpause" else "Pause"#</a>
-#if not $backlogRunning:
-Not in progress<br />
-#else:
-#if $backlogPaused then "Paused: " else ""#
-Currently running<br />
-#end if
-<br />
-
-<h3>Daily Search:</h3>
-<a class="btn" href="$sbRoot/manage/manageSearches/forceSearch"><i class="icon-exclamation-sign"></i> Force</a>
-#if not $dailySearchStatus:
-Not in progress<br />
-#else:
-In Progress<br />
-#end if
-<br />
-
-<h3>Find Propers Search:</h3>
-<a class="#if not $sickbeard.DOWNLOAD_PROPERS then 'btn disabled' else 'btn' #" href="$sbRoot/manage/manageSearches/forceFindPropers"><i class="icon-exclamation-sign"></i> Force</a>
-#if not $findPropersStatus:
-Not in progress<br />
-#else:
-In Progress<br />
-#end if
-<br />
-
-<h3>Search Queue:</h3>
-Backlog: <i>$queueLength['backlog'] pending items</i></br>
-Daily: <i>$queueLength['daily'] pending items</i></br>
-Manual: <i>$queueLength['manual'] pending items</i></br>
-Failed: <i>$queueLength['failed'] pending items</i></br>
-</div>
-</div>
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/manage_massEdit.mako b/gui/slick/interfaces/default/manage_massEdit.mako
new file mode 100644
index 0000000000000000000000000000000000000000..fc6250eb8762d459ec78bfb88920798d4a51d5a3
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_massEdit.mako
@@ -0,0 +1,196 @@
+<%!
+    import sickbeard
+    from sickbeard import common
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings
+    from sickbeard import exceptions
+%>
+% if quality_value != None:
+    <% initial_quality = int(quality_value) %>
+% else:
+    <% initial_quality = common.SD %>
+% endif
+<% anyQualities, bestQualities = common.Quality.splitQuality(initial_quality) %>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/massEdit.js?${sbPID}"></script>
+
+<form action="massEditSubmit" method="post">
+<input type="hidden" name="toEdit" value="${showList}" />
+
+<div class="optionWrapper">
+    <span class="selectTitle">Root Directories <span class="separator">*</span></span><br />
+    % for cur_dir in root_dir_list:
+        <% cur_index = root_dir_list.index(cur_dir) %>
+        <div>
+            <input class="btn edit_root_dir" type="button" class="edit_root_dir" id="edit_root_dir_${cur_index}" value="Edit" />
+            <input class="btn delete_root_dir" type="button" class="delete_root_dir" id="delete_root_dir_${cur_index}" value="Delete" />
+            ${cur_dir} => <span id="display_new_root_dir_${cur_index}">${cur_dir}</span>
+        </div>
+        <input type="hidden" name="orig_root_dir_${cur_index}" value="${cur_dir}" />
+        <input type="text" style="display: none" name="new_root_dir_${cur_index}" id="new_root_dir_${cur_index}" class="new_root_dir" value="${cur_dir}" />
+    % endfor
+</div>
+
+<div class="optionWrapper">
+<span class="selectTitle">Quality</span>
+    <div class="selectChoices">
+        <select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <% selected = None %>
+            <option value="0" ${('', 'selected="selected"')[quality_value != None and quality_value not in common.qualityPresets]}>Custom</option>
+            % for curPreset in sorted(common.qualityPresets):
+            <option value="${curPreset}" ${('', 'selected="selected"')[quality_value == curPreset]}>${common.qualityPresetStrings[curPreset]}</option>
+            % endfor
+        </select>
+    </div><br />
+
+    <div id="customQuality">
+        <div class="manageCustom pull-left">
+        <h4>Inital</h4>
+            <% anyQualityList = filter(lambda x: x > common.Quality.NONE, common.Quality.qualityStrings) %>
+            <select id="anyQualities" name="anyQualities" multiple="multiple" size="${len(anyQualityList)}">
+            % for curQuality in sorted(anyQualityList):
+            <option value="${curQuality}" ${('', 'selected="selected"')[curQuality in anyQualities]}>${common.Quality.qualityStrings[curQuality]}</option>
+            % endfor
+            </select>
+        </div>
+        <div class="manageCustom pull-left">
+        <h4>Archive</h4>
+            <% bestQualityList = filter(lambda x: x >= common.Quality.SDTV, common.Quality.qualityStrings) %>
+            <select id="bestQualities" name="bestQualities" multiple="multiple" size="len(${bestQualityList})">
+            % for curQuality in sorted(bestQualityList):
+            <option value="${curQuality}" ${('', 'selected="selected"')[curQuality in bestQualities]}>${common.Quality.qualityStrings[curQuality]}</option>
+            % endfor
+            </select>
+        </div>
+    <br />
+    </div>
+</div>
+
+% if anyQualities + bestQualities:
+<% isSelected = ' selected="selected"' %>
+<% isEnabled = isSelected %>
+<% isDisabled = isSelected %>
+% if archive_firstmatch_value:
+    <% isDisabled = '' %>
+% else:
+    <% isEnabled = '' %>
+% endif
+<div class="optionWrapper clearfix">
+<span class="selectTitle">Archive on first match</span>
+    <div class="selectChoices">
+        <select id="edit_archive_firstmatch" name="archive_firstmatch" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${isEnabled}>enable</option>
+            <option value="disable" ${isDisabled}>disable</option>
+        </select>
+    </div>
+</div>
+% endif
+
+<div class="optionWrapper clearfix">
+<span class="selectTitle">Flatten Folders <span class="separator">*</span></span>
+    <div class="selectChoices">
+        <select id="edit_flatten_folders" name="flatten_folders" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(flatten_folders_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(flatten_folders_value)]}>disable</option>
+        </select>
+    </div>
+</div>
+
+<div class="optionWrapper">
+    <span class="selectTitle">Paused</span>
+    <div class="selectChoices">
+        <select id="edit_paused" name="paused" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(paused_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(paused_value)]}>disable</option>
+        </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+    <span class="selectTitle">Default Episode Status:</span>
+    <div class="selectChoices">
+      <select id="edit_default_ep_status" name="default_ep_status" class="form-control form-control-inline input-sm">
+          <option value="keep">&lt; keep &gt;</option>
+          % for curStatus in [WANTED, SKIPPED, ARCHIVED, IGNORED]:
+          <option value="${curStatus}" ${('', 'selected="selected"')[curStatus == default_ep_status_value]}>${statusStrings[curStatus]}</option>
+          % endfor
+      </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+    <span class="selectTitle">Scene Numbering</span>
+    <div class="selectChoices">
+        <select id="edit_scene" name="scene" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(scene_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(scene_value)]}>disable</option>
+        </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+    <span class="selectTitle">Anime</span>
+    <div class="selectChoices">
+        <select id="edit_anime" name="anime" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(anime_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(anime_value)]}>disable</option>
+        </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+    <span class="selectTitle">Sports</span>
+    <div class="selectChoices">
+        <select id="edit_sports" name="sports" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(sports_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(sports_value)]}>disable</option>
+        </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+    <span class="selectTitle">Air-By-Date</span>
+    <div class="selectChoices">
+        <select id="edit_air_by_date" name="air_by_date" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(air_by_date_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(air_by_date_value)]}>disable</option>
+        </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+<span class="selectTitle">Subtitles<span class="separator"></span></span>
+    <div class="selectChoices">
+        <select id="edit_subtitles" name="subtitles" class="form-control form-control-inline input-sm">
+            <option value="keep">&lt; keep &gt;</option>
+            <option value="enable" ${('', 'selected="selected"')[bool(subtitles_value)]}>enable</option>
+            <option value="disable" ${('', 'selected="selected"')[not bool(subtitles_value)]}>disable</option>
+        </select>
+    </div><br />
+</div>
+
+<div class="optionWrapper">
+    <br /><span class="separator" style="font-size: 1.2em; font-weight: 700;">*</span>
+    Changing these settings will cause the selected shows to be refreshed.
+</div>
+
+<div class="optionWrapper" style="text-align: center;">
+    <input type="submit" value="Submit" class="btn" /><br />
+</div>
+
+</form>
+<br />
+
+<script type="text/javascript" charset="utf-8">
+    jQuery('#location').fileBrowser({ title: 'Select Show Location' });
+</script>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_massEdit.tmpl b/gui/slick/interfaces/default/manage_massEdit.tmpl
deleted file mode 100644
index 69579b743a8631c623b09ef662931eec67d84358..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_massEdit.tmpl
+++ /dev/null
@@ -1,200 +0,0 @@
-#import sickbeard
-#from sickbeard import common
-#from sickbeard.common import *
-#from sickbeard import exceptions
-#set global $title="Mass Edit"
-#set global $header="Mass Edit"
-
-#set global $sbPath=".."
-
-#set global $topmenu="manage"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-#if $quality_value != None:
-#set $initial_quality = int($quality_value)
-#else:
-#set $initial_quality = $common.SD
-#end if
-#set $anyQualities, $bestQualities = $common.Quality.splitQuality($initial_quality)
-<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/massEdit.js?$sbPID"></script>
-
-<form action="massEditSubmit" method="post">
-<input type="hidden" name="toEdit" value="$showList" />
-
-<div class="optionWrapper">
-<span class="selectTitle">Root Directories <span class="separator">*</span></span><br />
-        #for $cur_dir in $root_dir_list:
-        #set $cur_index = $root_dir_list.index($cur_dir)
-        <div>
-            <input class="btn edit_root_dir" type="button" class="edit_root_dir" id="edit_root_dir_$cur_index" value="Edit" />
-            <input class="btn delete_root_dir" type="button" class="delete_root_dir" id="delete_root_dir_$cur_index" value="Delete" />
-            $cur_dir => <span id="display_new_root_dir_$cur_index">$cur_dir</span>
-        </div>
-        <input type="hidden" name="orig_root_dir_$cur_index" value="$cur_dir" />
-        <input type="text" style="display: none" name="new_root_dir_$cur_index" id="new_root_dir_$cur_index" class="new_root_dir" value="$cur_dir" />
-        #end for
-
-</div>
-
-<div class="optionWrapper">
-<span class="selectTitle">Quality</span>
-    <div class="selectChoices">
-        <select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            #set $selected = None
-            <option value="0"  #if $quality_value != None and $quality_value not in $common.qualityPresets then "selected=\"selected\"" else ""#>Custom</option>
-            #for $curPreset in sorted($common.qualityPresets):
-            <option value="$curPreset" #if $quality_value == $curPreset then "selected=\"selected\"" else ""#>$common.qualityPresetStrings[$curPreset]</option>
-            #end for
-        </select>
-    </div><br />
-
-    <div id="customQuality">
-        <div class="manageCustom pull-left">
-        <h4>Inital</h4>
-            #set $anyQualityList = filter(lambda x: x > $common.Quality.NONE, $common.Quality.qualityStrings)
-            <select id="anyQualities" name="anyQualities" multiple="multiple" size="len($anyQualityList)">
-            #for $curQuality in sorted($anyQualityList):
-            <option value="$curQuality" #if $curQuality in $anyQualities then "selected=\"selected\"" else ""#>$common.Quality.qualityStrings[$curQuality]</option>
-            #end for
-            </select>
-        </div>
-        <div class="manageCustom pull-left">
-        <h4>Archive</h4>
-            #set $bestQualityList = filter(lambda x: x >= $common.Quality.SDTV, $common.Quality.qualityStrings)
-            <select id="bestQualities" name="bestQualities" multiple="multiple" size="len($bestQualityList)">
-            #for $curQuality in sorted($bestQualityList):
-            <option value="$curQuality" #if $curQuality in $bestQualities then "selected=\"selected\"" else ""#>$common.Quality.qualityStrings[$curQuality]</option>
-            #end for
-            </select>
-        </div>
-    <br />
-    </div>
-</div>
-
-#if $anyQualities + $bestQualities:
-#set $isSelected = ' selected="selected"'
-#set $isEnabled = $isSelected
-#set $isDisabled = $isSelected
-#if $archive_firstmatch_value##set $isDisabled = ''##else##set $isEnabled = ''##end if#
-<div class="optionWrapper clearfix">
-<span class="selectTitle">Archive on first match</span>
-    <div class="selectChoices">
-        <select id="edit_archive_firstmatch" name="archive_firstmatch" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable"${isEnabled}>enable</option>
-            <option value="disable"${isDisabled}>disable</option>
-        </select>
-    </div>
-</div>
-#end if
-
-<div class="optionWrapper clearfix">
-<span class="selectTitle">Flatten Folders <span class="separator">*</span></span>
-    <div class="selectChoices">
-        <select id="edit_flatten_folders" name="flatten_folders" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $flatten_folders_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $flatten_folders_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div>
-</div>
-
-<div class="optionWrapper">
-    <span class="selectTitle">Paused</span>
-    <div class="selectChoices">
-        <select id="edit_paused" name="paused" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $paused_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $paused_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-    <span class="selectTitle">Default Episode Status:</span>
-    <div class="selectChoices">
-      <select id="edit_default_ep_status" name="default_ep_status" class="form-control form-control-inline input-sm">
-          <option value="keep">&lt; keep &gt;</option>
-          #for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED]:
-          <option value="$curStatus" #if $curStatus == $default_ep_status_value then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
-          #end for
-      </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-    <span class="selectTitle">Scene Numbering</span>
-    <div class="selectChoices">
-        <select id="edit_scene" name="scene" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $scene_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $scene_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-    <span class="selectTitle">Anime</span>
-    <div class="selectChoices">
-        <select id="edit_anime" name="anime" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $anime_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $anime_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-    <span class="selectTitle">Sports</span>
-    <div class="selectChoices">
-        <select id="edit_sports" name="sports" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $sports_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $sports_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-    <span class="selectTitle">Air-By-Date</span>
-    <div class="selectChoices">
-        <select id="edit_air_by_date" name="air_by_date" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $air_by_date_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $air_by_date_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-<span class="selectTitle">Subtitles<span class="separator"></span></span>
-    <div class="selectChoices">
-        <select id="edit_subtitles" name="subtitles" class="form-control form-control-inline input-sm">
-            <option value="keep">&lt; keep &gt;</option>
-            <option value="enable" #if $subtitles_value then "selected=\"selected\"" else ""#>enable</option>
-            <option value="disable" #if $subtitles_value == False then "selected=\"selected\"" else ""#>disable</option>
-        </select>
-    </div><br />
-</div>
-
-<div class="optionWrapper">
-    <br /><span class="separator" style="font-size: 1.2em; font-weight: 700;">*</span>
-    Changing these settings will cause the selected shows to be refreshed.
-</div>
-
-<div class="optionWrapper" style="text-align: center;">
-    <input type="submit" value="Submit" class="btn" /><br />
-</div>
-
-</form>
-<br />
-
-<script type="text/javascript" charset="utf-8">
-<!--
-    jQuery('#location').fileBrowser({ title: 'Select Show Location' });
-//-->
-</script>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/manage_subtitleMissed.mako b/gui/slick/interfaces/default/manage_subtitleMissed.mako
new file mode 100644
index 0000000000000000000000000000000000000000..3874943f0a80e2f2828b4a501d55b4f38e8f693c
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_subtitleMissed.mako
@@ -0,0 +1,62 @@
+<%!
+    from sickbeard import subtitles
+    import datetime
+    import sickbeard
+    from sickbeard import common
+%>
+<%include file="/inc_top.mako"/>
+<div id="content960">
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+% if whichSubs:
+<% subsLanguage = subtitles.fromietf(whichSubs).name if not whichSubs == 'all' else 'All' %>
+% endif
+% if not whichSubs or (whichSubs and not ep_counts):
+
+% if whichSubs:
+<h2>All of your episodes have ${subsLanguage} subtitles.</h2>
+<br />
+% endif
+
+<form action="${sbRoot}/manage/subtitleMissed" method="get">
+Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm">
+<option value="all">All</option>
+<% sub_langs = [subtitles.fromietf(x) for x in subtitles.wantedLanguages()] %>
+% for sub_lang in sub_langs:
+<option value="${sub_lang.opensubtitles}">${sub_lang.name}</option>
+% endfor
+</select>
+subtitles
+<input class="btn" type="submit" value="Manage" />
+</form>
+
+% else:
+
+<script type="text/javascript" src="${sbRoot}/js/manageSubtitleMissed.js?${sbPID}"></script>
+<input type="hidden" id="selectSubLang" name="selectSubLang" value="${whichSubs}" />
+
+<form action="${sbRoot}/manage/downloadSubtitleMissed" method="post">
+<h2>Episodes without ${subsLanguage} subtitles.</h2>
+<br />
+Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" />
+<div>
+    <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
+    <button type="button" class="btn btn-xs unselectAllShows">Clear all</a></button>
+</div>
+<br />
+<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
+% for cur_indexer_id in sorted_show_ids:
+ <tr id="${cur_indexer_id}">
+  <th><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th>
+  <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="${sbRoot}/home/displayShow?show=${cur_indexer_id}">${show_names[cur_indexer_id]}</a> (${ep_counts[cur_indexer_id]}) <input type="button" class="pull-right get_more_eps btn" id="${cur_indexer_id}" value="Expand" /></th>
+ </tr>
+% endfor
+</table>
+</form>
+
+% endif
+</div>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_subtitleMissed.tmpl b/gui/slick/interfaces/default/manage_subtitleMissed.tmpl
deleted file mode 100644
index 50fa41400fe3af5a8ea54ccaf0f0beed1caafed2..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_subtitleMissed.tmpl
+++ /dev/null
@@ -1,66 +0,0 @@
-#from sickbeard import subtitles
-#import datetime
-#import sickbeard
-#from sickbeard import common
-#set global $title="Episode Overview"
-#set global $header="Episode Overview"
-
-#set global $sbPath=".."
-
-#set global $topmenu="manage"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-<div id="content960">
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-#if $whichSubs:
-#set subsLanguage = $subtitles.fromietf($whichSubs).name if not $whichSubs == 'all' else 'All'
-#end if
-#if not $whichSubs or ($whichSubs and not $ep_counts):
-
-#if $whichSubs:
-<h2>All of your episodes have $subsLanguage subtitles.</h2>
-<br />
-#end if
-
-<form action="$sbRoot/manage/subtitleMissed" method="get">
-Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm">
-<option value="all">All</option>
-#for $sub_lang in [$subtitles.fromietf(x) for x in $subtitles.wantedLanguages]:
-<option value="$sub_lang.opensubtitles">$sub_lang.name</option>
-#end for
-</select>
-subtitles
-<input class="btn" type="submit" value="Manage" />
-</form>
-
-#else
-
-<script type="text/javascript" src="$sbRoot/js/manageSubtitleMissed.js?$sbPID"></script>
-<input type="hidden" id="selectSubLang" name="selectSubLang" value="$whichSubs" />
-
-<form action="$sbRoot/manage/downloadSubtitleMissed" method="post">
-<h2>Episodes without $subsLanguage subtitles.</h2>
-<br />
-Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" />
-<div>
-    <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
-    <button type="button" class="btn btn-xs unselectAllShows">Clear all</a></button>
-</div>
-<br />
-<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
-#for $cur_indexer_id in $sorted_show_ids:
- <tr id="$cur_indexer_id">
-  <th><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all" checked="checked" /></th>
-  <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th>
- </tr>
-#end for
-</table>
-</form>
-
-#end if
-</div>
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/manage_torrents.mako b/gui/slick/interfaces/default/manage_torrents.mako
new file mode 100644
index 0000000000000000000000000000000000000000..e48b4f133a9139282827bc8415e8c27b6124b734
--- /dev/null
+++ b/gui/slick/interfaces/default/manage_torrents.mako
@@ -0,0 +1,17 @@
+<%!
+    import sickbeard
+    import datetime
+    from sickbeard.common import *
+%>
+<%include file="/inc_top.mako"/>
+<script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else;
+    <h1 class="title">${title}</h1>
+% endif
+
+${info_download_station}
+<iframe id="extFrame" src="${webui_url}" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/manage_torrents.tmpl b/gui/slick/interfaces/default/manage_torrents.tmpl
deleted file mode 100644
index 50f2d08143c490c6feb5f0e3dc294983727c669b..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/manage_torrents.tmpl
+++ /dev/null
@@ -1,22 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard.common import *
-#set global $title="Manage Torrents"
-#set global $header="Manage Torrents"
-#set global $sbPath=".."
-
-#set global $topmenu="manage"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
-   #if $varExists('header')
-       <h1 class="header">$header</h1>
-   #else
-       <h1 class="title">$title</h1>
-   #end if
-
-$info_download_station
-<iframe id="extFrame" src="$webui_url" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe>
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/markdown.mako b/gui/slick/interfaces/default/markdown.mako
new file mode 100644
index 0000000000000000000000000000000000000000..0e462d0bf55be0363644500f78905c0ccab50a80
--- /dev/null
+++ b/gui/slick/interfaces/default/markdown.mako
@@ -0,0 +1,3 @@
+<%include file="/inc_top.mako"/>
+${data}
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/markdown.tmpl b/gui/slick/interfaces/default/markdown.tmpl
deleted file mode 100644
index 31ea924e75e18dd770e983d4f7a5eb0320d5d9af..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/markdown.tmpl
+++ /dev/null
@@ -1,9 +0,0 @@
-#import sickbeard
-#from sickbeard.common import *
-
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-$data
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/restart.mako b/gui/slick/interfaces/default/restart.mako
new file mode 100644
index 0000000000000000000000000000000000000000..a09eabf15c2e7efa71bf0c709d4f50ac620a3ebd
--- /dev/null
+++ b/gui/slick/interfaces/default/restart.mako
@@ -0,0 +1,3 @@
+<%include file="/inc_top.mako"/>
+<%include file="/restart_bare.mako"/>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/restart.tmpl b/gui/slick/interfaces/default/restart.tmpl
deleted file mode 100644
index eb5832c6b34534f7944039f96d2dc3a08b0845c1..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/restart.tmpl
+++ /dev/null
@@ -1,17 +0,0 @@
-#import sickbeard
-#import datetime
-#from sickbeard.common import *
-#from sickbeard import db
-
-#set global $title="Home"
-#set global $header="Restarting SickRage"
-
-#set global $sbPath = ".."
-
-#set global $topmenu="home"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/restart_bare.tmpl")
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/restart_bare.mako b/gui/slick/interfaces/default/restart_bare.mako
new file mode 100644
index 0000000000000000000000000000000000000000..f7a202b5adbaafdcb2e0c8052c8de3834f2430d5
--- /dev/null
+++ b/gui/slick/interfaces/default/restart_bare.mako
@@ -0,0 +1,50 @@
+<%
+    try:
+        curSBHost = sbHost
+        curSBHttpPort = sbHttpPort
+        curSBHttpsEnabled = sbHttpsEnabled
+        curSBHandleReverseProxy = sbHandleReverseProxy
+        themeSpinner = sbThemeName
+    except NameMapper.NotFound:
+        curSBHost = "localhost"
+        curSBHttpPort = sickbeard.WEB_PORT
+        curSBHttpsEnabled = "False"
+        curSBHandleReverseProxy = "False"
+        themeSpinner = sickbeard.THEME_NAME
+%>
+<script type="text/javascript" charset="utf-8">
+sbRoot = "${sbRoot}";
+sbHttpPort = "${curSBHttpPort}";
+sbHttpsEnabled = "${curSBHttpsEnabled}";
+sbHandleReverseProxy = "${curSBHandleReverseProxy}";
+sbHost = "${curSBHost}";
+sbDefaultPage = "${sbDefaultPage}";
+</script>
+
+<script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script>
+<script type="text/javascript" src="${sbRoot}/js/restart.js?${sbPID}&${sbDefaultPage}"></script>
+
+<% themeSpinner = ('', '-dark')['dark' == themeSpinner] %>
+<h2>Performing Restart</h2>
+<br />
+<div id="shut_down_message">
+Waiting for SickRage to shut down:
+<img src="${sbRoot}/images/loading16${themeSpinner}.gif" height="16" width="16" id="shut_down_loading" />
+<img src="${sbRoot}/images/yes16.png" height="16" width="16" id="shut_down_success" style="display: none;" />
+</div>
+
+<div id="restart_message" style="display: none;">
+Waiting for SickRage to start again:
+<img src="${sbRoot}/images/loading16${themeSpinner}.gif" height="16" width="16" id="restart_loading" />
+<img src="${sbRoot}/images/yes16.png" height="16" width="16" id="restart_success" style="display: none;" />
+<img src="${sbRoot}/images/no16.png" height="16" width="16" id="restart_failure" style="display: none;" />
+</div>
+
+<div id="refresh_message" style="display: none;">
+Loading the default page:
+<img src="${sbRoot}/images/loading16${themeSpinner}.gif" height="16" width="16" id="refresh_loading" />
+</div>
+
+<div id="restart_fail_message" style="display: none;">
+Error: The restart has timed out, perhaps something prevented SickRage from starting again?
+</div>
diff --git a/gui/slick/interfaces/default/restart_bare.tmpl b/gui/slick/interfaces/default/restart_bare.tmpl
deleted file mode 100644
index ac795e04f8a8a035131f5f61158a745fbd255e42..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/restart_bare.tmpl
+++ /dev/null
@@ -1,52 +0,0 @@
-<script type="text/javascript" charset="utf-8">
-<!--
-#try:
-    #set curSBHost = $sbHost
-    #set curSBHttpPort = $sbHttpPort
-    #set curSBHttpsEnabled = $sbHttpsEnabled
-    #set curSBHandleReverseProxy = $sbHandleReverseProxy
-    #set themeSpinner = $sbThemeName
-#except NameMapper.NotFound:
-    #set curSBHost = "localhost"
-    #set curSBHttpPort = $sickbeard.WEB_PORT
-    #set curSBHttpsEnabled = "False"
-    #set curSBHandleReverseProxy = "False"
-    #set themeSpinner = $sickbeard.THEME_NAME
-#end try
-sbRoot = "$sbRoot";
-sbHttpPort = "$curSBHttpPort";
-sbHttpsEnabled = "$curSBHttpsEnabled";
-sbHandleReverseProxy = "$curSBHandleReverseProxy";
-sbHost = "$curSBHost";
-sbDefaultPage = "$sbDefaultPage";
-//-->
-</script>
-
-<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.11.2.min.js?$sbPID"></script>
-<script type="text/javascript" src="$sbRoot/js/restart.js?$sbPID&$sbDefaultPage"></script>
-
-#set themeSpinner = '-dark' if 'dark' == themeSpinner else ''
-<h2>Performing Restart</h2>
-<br />
-<div id="shut_down_message">
-Waiting for SickRage to shut down:
-<img src="$sbRoot/images/loading16${themeSpinner}.gif" height="16" width="16" id="shut_down_loading" />
-<img src="$sbRoot/images/yes16.png" height="16" width="16" id="shut_down_success" style="display: none;" />
-</div>
-
-<div id="restart_message" style="display: none;">
-Waiting for SickRage to start again:
-<img src="$sbRoot/images/loading16${themeSpinner}.gif" height="16" width="16" id="restart_loading" />
-<img src="$sbRoot/images/yes16.png" height="16" width="16" id="restart_success" style="display: none;" />
-<img src="$sbRoot/images/no16.png" height="16" width="16" id="restart_failure" style="display: none;" />
-</div>
-
-<div id="refresh_message" style="display: none;">
-Loading the default page:
-<img src="$sbRoot/images/loading16${themeSpinner}.gif" height="16" width="16" id="refresh_loading" />
-</div>
-
-<div id="restart_fail_message" style="display: none;">
-Error: The restart has timed out, perhaps something prevented SickRage from starting again?
-</div>
-
diff --git a/gui/slick/interfaces/default/status.mako b/gui/slick/interfaces/default/status.mako
new file mode 100644
index 0000000000000000000000000000000000000000..8f8382dcab4b8d7a14979ce20fa82ba24723ab22
--- /dev/null
+++ b/gui/slick/interfaces/default/status.mako
@@ -0,0 +1,223 @@
+<%!
+    import sickbeard
+    from sickbeard import helpers
+    from sickbeard.show_queue import ShowQueueActions
+%>
+<%include file="/inc_top.mako"/>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<%
+    schedulerList = {
+        'Daily Search': 'dailySearchScheduler',
+        'Backlog': 'backlogSearchScheduler',
+        'Show Update': 'showUpdateScheduler',
+        'Version Check': 'versionCheckScheduler',
+        'Show Queue': 'showQueueScheduler',
+        'Search Queue': 'searchQueueScheduler',
+        'Proper Finder': 'properFinderScheduler',
+        'Post Process': 'autoPostProcesserScheduler',
+        'Subtitles Finder': 'subtitlesFinderScheduler',
+        'Trakt Checker': 'traktCheckerScheduler',
+        'Trakt Rolling': 'traktRollingScheduler',
+    }
+%>
+
+<script type="text/javascript">
+    $(document).ready(function() {
+        $("#schedulerStatusTable").tablesorter({
+            widgets: ['saveSort', 'zebra']
+        });
+    });
+    $(document).ready(function() {
+        $("#queueStatusTable").tablesorter({
+            widgets: ['saveSort', 'zebra'],
+            sortList: [[3,0], [4,0], [2,1]]
+        });
+    });
+</script>
+
+<div id="config-content">
+    <h2 class="header">Scheduler</h2>
+    <table id="schedulerStatusTable" class="tablesorter" width="100%">
+        <thead>
+            <tr>
+                <th>Scheduler</th>
+                <th>Alive</th>
+                <th>Enable</th>
+                <th>Active</th>
+                <th>Start Time</th>
+                <th>Cycle Time</th>
+                <th>Next Run</th>
+                <th>Last Run</th>
+                <th>Silent</th>
+            </tr>
+        </thead>
+        <tbody>
+            % for schedulerName, scheduler in schedulerList.iteritems():
+               <% service = getattr(sickbeard, scheduler) %>
+           <tr>
+               <td>${schedulerName}</td>
+               % if service.isAlive():
+               <td style="background-color:green">${service.isAlive()}</td>
+               % else:
+               <td style="background-color:red">${service.isAlive()}</td>
+               % endif
+               % if scheduler == 'backlogSearchScheduler':
+                   <% searchQueue = getattr(sickbeard, 'searchQueueScheduler') %>
+                   <% BLSpaused = searchQueue.action.is_backlog_paused() %>
+                   <% del searchQueue %>
+                   % if BLSpaused:
+               <td>Paused</td>
+                   % else:
+               <td>${service.enable}</td>
+                   % endif
+               % else:
+               <td>${service.enable}</td>
+               % endif
+               % if scheduler == 'backlogSearchScheduler':
+                   <% searchQueue = getattr(sickbeard, 'searchQueueScheduler') %>
+                   <% BLSinProgress = searchQueue.action.is_backlog_in_progress() %>
+                   <% del searchQueue %>
+                   % if BLSinProgress:
+               <td>True</td>
+                   % else:
+                       % try:
+                       <% amActive = service.action.amActive %>
+               <td>${amActive}</td>
+                       % except Exception:
+               <td>N/A</td>
+                       % endtry
+                   % endif
+               % else:
+                   % try:
+                   <% amActive = service.action.amActive %>
+               <td>${amActive}</td>
+                   % except Exception:
+               <td>N/A</td>
+                   % endtry
+               % endif
+               <td align="right">${service.start_time}</td>
+               <% cycleTime = (service.cycleTime.microseconds + (service.cycleTime.seconds + service.cycleTime.days * 24 * 3600) * 10**6) / 10**6 %>
+               <td align="right">${helpers.pretty_time_delta(cycleTime)}</td>
+               % if service.enable:
+                   <% timeLeft = (service.timeLeft().microseconds + (service.timeLeft().seconds + service.timeLeft().days * 24 * 3600) * 10**6) / 10**6 %>
+               <td align="right">${helpers.pretty_time_delta(timeLeft)}</td>
+               % else:
+               <td></td>
+               % endif
+               <td>${service.lastRun.strftime("%Y-%m-%d %H:%M:%S")}</td>
+               <td>${service.silent}</td>
+           </tr>
+           <% del service %>
+           % endfor
+       </tbody>
+    </table>
+    <h2 class="header">Show Queue</h2>
+    <table id="queueStatusTable" class="tablesorter" width="100%">
+        <thead>
+            <tr>
+                <th>Show id</th>
+                <th>Show name</th>
+                <th>In Progress</th>
+                <th>Priority</th>
+                <th>Added</th>
+                <th>Queue type</th>
+            </tr>
+        </thead>
+        <tbody>
+            % if sickbeard.showQueueScheduler.action.currentItem is not None:
+                <tr>
+                    % try:
+                        <% showindexerid = sickbeard.showQueueScheduler.action.currentItem.show.indexerid %>
+                        <td>${showindexerid}</td>
+                    % except Exception:
+                        <td></td>
+                    % endtry
+                    % try:
+                        <% showname = sickbeard.showQueueScheduler.action.currentItem.show.name %>
+                        <td>${showname}</td>
+                    % except Exception:
+                        % if sickbeard.showQueueScheduler.action.currentItem.action_id == ShowQueueActions.ADD:
+                            <td>${sickbeard.showQueueScheduler.action.currentItem.showDir}</td>
+                        % else:
+                            <td></td>
+                        % endif
+                    % endtry
+                    <td>${sickbeard.showQueueScheduler.action.currentItem.inProgress}</td>
+                    % if sickbeard.showQueueScheduler.action.currentItem.priority == 10:
+                        <td>LOW</td>
+                    % elif sickbeard.showQueueScheduler.action.currentItem.priority == 20:
+                        <td>NORMAL</td>
+                    % elif sickbeard.showQueueScheduler.action.currentItem.priority == 30:
+                        <td>HIGH</td>
+                    % else:
+                        <td>sickbeard.showQueueScheduler.action.currentItem.priority</td>
+                    % endif
+                    <td>${sickbeard.showQueueScheduler.action.currentItem.added.strftime("%Y-%m-%d %H:%M:%S")}</td>
+                    <td>${ShowQueueActions.names[sickbeard.showQueueScheduler.action.currentItem.action_id]}</td>
+                </tr>
+            % endif
+            % for item in sickbeard.showQueueScheduler.action.queue:
+                <tr>
+                    % try:
+                        <% showindexerid = item.show.indexerid %>
+                        <td>${showindexerid}</td>
+                    % except Exception:
+                        <td></td>
+                    % endtry
+                    % try:
+                        <% showname = item.show.name %>
+                        <td>${showname}</td>
+                    % except Exception:
+                        % if item.action_id == ShowQueueActions.ADD:
+                            <td>${item.showDir}</td>
+                        % else:
+                            <td></td>
+                        % endif
+                    % endtry
+                    <td>${item.inProgress}</td>
+                    % if item.priority == 10:
+                        <td>LOW</td>
+                    % elif item.priority == 20:
+                        <td>NORMAL</td>
+                    % elif item.priority == 30:
+                        <td>HIGH</td>
+                    % else:
+                        <td>${item.priority}</td>
+                    % endif
+                    <td>${item.added.strftime("%Y-%m-%d %H:%M:%S")}</td>
+                    <td>${ShowQueueActions.names[item.action_id]}</td>
+                </tr>
+            % endfor
+        </tbody>
+    </table>
+    <h2 class="header">Disk Space</h2>
+    <table id="DFStatusTable" class="tablesorter" width="50%">
+        <thead>
+            <tr>
+                <th>Type</th>
+                <th>Location</th>
+                <th>Free space</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>TV Download Directory</td>
+                <td>${sickbeard.TV_DOWNLOAD_DIR}</td>
+                <td>${tvdirFree} MB</td>
+            </tr>
+            <tr>
+                <td rowspan=${len(rootDir)}>Media Root Directories</td>
+            % for cur_dir in rootDir:
+                <td>${cur_dir}</td>
+                <td>${rootDir[cur_dir]} MB</td>
+            </tr>
+            % endfor
+        </tbody>
+    </table>
+</div>
diff --git a/gui/slick/interfaces/default/status.tmpl b/gui/slick/interfaces/default/status.tmpl
deleted file mode 100644
index c02a0482662ca8041547c6f302747d82502e0c00..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/status.tmpl
+++ /dev/null
@@ -1,200 +0,0 @@
-#import sickbeard
-#from sickbeard import helpers
-#from sickbeard.show_queue import ShowQueueActions
-
-#set global $title="Status"
-#set global $header="Status"
-#set global $sbPath=".."
-
-#set global $topmenu="config"#
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-#set schedulerList = {'Daily Search': 'dailySearchScheduler',
-                      'Backlog': 'backlogSearchScheduler',
-                      'Show Update': 'showUpdateScheduler',
-                      'Version Check': 'versionCheckScheduler',
-                      'Show Queue': 'showQueueScheduler',
-                      'Search Queue': 'searchQueueScheduler',
-                      'Proper Finder': 'properFinderScheduler',
-                      'Post Process': 'autoPostProcesserScheduler',
-                      'Subtitles Finder': 'subtitlesFinderScheduler',
-                      'Trakt Checker': 'traktCheckerScheduler',
-                      'Trakt Rolling': 'traktRollingScheduler',
-}
-
-<script type="text/javascript">
-    \$(document).ready(function() {
-        \$("#schedulerStatusTable").tablesorter({
-            widgets: ['saveSort', 'zebra']
-        });
-    });
-    \$(document).ready(function() {
-        \$("#queueStatusTable").tablesorter({
-            widgets: ['saveSort', 'zebra'],
-            sortList: [[3,0], [4,0], [2,1]]
-        });
-    });
-</script>
-
-<div id="config-content">
-    <h2 class="header">Scheduler</h2>
-    <table id="schedulerStatusTable" class="tablesorter" width="100%">
-        <thead>
-            <tr>
-                <th>Scheduler</th>
-                <th>Alive</th>
-                <th>Enable</th>
-                <th>Active</th>
-                <th>Start Time</th>
-                <th>Cycle Time</th>
-                <th>Next Run</th>
-                <th>Last Run</th>
-                <th>Silent</th>
-            </tr>
-        </thead>
-        <tbody>
-            #for $schedulerName, $scheduler in $schedulerList.iteritems()
-                #set service = getattr($sickbeard, $scheduler)
-            <tr>
-                <td>$schedulerName</td>
-                #if $service.isAlive()
-                <td style="background-color:green">$service.isAlive()</td>
-                #else
-                <td style="background-color:red">$service.isAlive()</td>
-                #end if
-                #if $scheduler == 'backlogSearchScheduler'
-                    #set searchQueue = getattr($sickbeard, 'searchQueueScheduler')
-                    #set $BLSpaused = $searchQueue.action.is_backlog_paused()
-                    #del searchQueue
-                    #if $BLSpaused
-                <td>Paused</td>
-                    #else
-                <td>$service.enable</td>
-                    #end if
-                #else
-                <td>$service.enable</td>
-                #end if
-                #if $scheduler == 'backlogSearchScheduler'
-                    #set searchQueue = getattr($sickbeard, 'searchQueueScheduler')
-                    #set $BLSinProgress = $searchQueue.action.is_backlog_in_progress()
-                    #del searchQueue
-                    #if $BLSinProgress
-                <td>True</td>
-                    #else
-                        #try
-                        #set amActive = $service.action.amActive
-                <td>$amActive</td>
-                        #except Exception
-                <td>N/A</td>
-                        #end try
-                    #end if
-                #else
-                    #try
-                    #set amActive = $service.action.amActive
-                <td>$amActive</td>
-                    #except Exception
-                <td>N/A</td>
-                    #end try
-                #end if
-                <td align="right">$service.start_time</td>
-                #set $cycleTime = ($service.cycleTime.microseconds + ($service.cycleTime.seconds + $service.cycleTime.days * 24 * 3600) * 10**6) / 10**6
-                <td align="right">$helpers.pretty_time_delta($cycleTime)</td>
-                #if $service.enable
-                    #set $timeLeft = ($service.timeLeft().microseconds + ($service.timeLeft().seconds + $service.timeLeft().days * 24 * 3600) * 10**6) / 10**6
-                <td align="right">$helpers.pretty_time_delta($timeLeft)</td>
-                #else
-                <td></td>
-                #end if
-                <td>$service.lastRun.strftime("%Y-%m-%d %H:%M:%S")</td>
-                <td>$service.silent</td>
-            </tr>
-            #del service
-            #end for
-        </tbody>
-    </table>
-    <h2 class="header">Show Queue</h2>
-    <table id="queueStatusTable" class="tablesorter" width="100%">
-        <thead>
-            <tr>
-                <th>Show id</th>
-                <th>Show name</th>
-                <th>In Progress</th>
-                <th>Priority</th>
-                <th>Added</th>
-                <th>Queue type</th>
-            </tr>
-        </thead>
-        <tbody>
-            #if $sickbeard.showQueueScheduler.action.currentItem is not None
-            <tr>
-                #try
-                #set showindexerid = $sickbeard.showQueueScheduler.action.currentItem.show.indexerid
-                <td>$showindexerid</td>
-                #except Exception
-                <td></td>
-                #end try
-                #try
-                #set showname = $sickbeard.showQueueScheduler.action.currentItem.show.name
-                <td>$showname</td>
-                #except Exception
-                    #if $sickbeard.showQueueScheduler.action.currentItem.action_id == $ShowQueueActions.ADD
-                    <td>$sickbeard.showQueueScheduler.action.currentItem.showDir</td>
-                    #else
-                <td></td>
-                    #end if
-                #end try
-                <td>$sickbeard.showQueueScheduler.action.currentItem.inProgress</td>
-                #if $sickbeard.showQueueScheduler.action.currentItem.priority == 10
-                <td>LOW</td>
-                #elif $sickbeard.showQueueScheduler.action.currentItem.priority == 20
-                <td>NORMAL</td>
-                #elif $sickbeard.showQueueScheduler.action.currentItem.priority == 30
-                <td>HIGH</td>
-                #else
-                <td>$sickbeard.showQueueScheduler.action.currentItem.priority</td>
-                #end if
-                <td>$sickbeard.showQueueScheduler.action.currentItem.added.strftime("%Y-%m-%d %H:%M:%S")</td>
-                <td>$ShowQueueActions.names[$sickbeard.showQueueScheduler.action.currentItem.action_id]</td>
-            </tr>
-            #end if
-            #for item in $sickbeard.showQueueScheduler.action.queue
-            <tr>
-                #try
-                #set showindexerid = $item.show.indexerid
-                <td>$showindexerid</td>
-                #except Exception
-                <td></td>
-                #end try
-                #try
-                #set showname = $item.show.name
-                <td>$showname</td>
-                #except Exception
-                    #if $item.action_id == $ShowQueueActions.ADD
-                    <td>$item.showDir</td>
-                    #else
-                <td></td>
-                    #end if
-                #end try
-                <td>$item.inProgress</td>
-                #if $item.priority == 10
-                <td>LOW</td>
-                #elif $item.priority == 20
-                <td>NORMAL</td>
-                #elif $item.priority == 30
-                <td>HIGH</td>
-                #else
-                <td>$item.priority</td>
-                #end if
-                <td>$item.added.strftime("%Y-%m-%d %H:%M:%S")</td>
-                <td>$ShowQueueActions.names[$item.action_id]</td>
-            </tr>
-            #end for
-        </tbody>
-    </table>
-</div>
\ No newline at end of file
diff --git a/gui/slick/interfaces/default/testRename.mako b/gui/slick/interfaces/default/testRename.mako
new file mode 100644
index 0000000000000000000000000000000000000000..51e280d1c670b0bd2599ba1baac957ef6c6dfabe
--- /dev/null
+++ b/gui/slick/interfaces/default/testRename.mako
@@ -0,0 +1,101 @@
+<%!
+    import sickbeard
+    import calendar
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
+    from sickbeard import db, sbdatetime, network_timezones
+    import datetime
+    import re
+%>
+<%include file="/inc_top.mako"/>
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<input type="hidden" id="showID" value="${show.indexerid}" />
+
+<script type="text/javascript" src="${sbRoot}/js/testRename.js"></script>
+
+<h3>Preview of the proposed name changes</h3>
+<blockquote>
+% if int(show.air_by_date) == 1 and sickbeard.NAMING_CUSTOM_ABD:
+    ${sickbeard.NAMING_ABD_PATTERN}
+% elif int(show.sports) == 1 and sickbeard.NAMING_CUSTOM_SPORTS:
+    ${sickbeard.NAMING_SPORTS_PATTERN}
+% else:
+    ${sickbeard.NAMING_PATTERN}
+% endif
+</blockquote>
+
+<% curSeason = -1 %>
+<% odd = False%>
+
+<table id="SelectAllTable" class="sickbeardTable" cellspacing="1" border="0" cellpadding="0">
+    <thead>
+        <tr class="seasonheader" id="season-all">
+            <td colspan="4">
+                <h2>All Seasons</h2>
+            </td>
+        </tr>
+        <tr class="seasoncols" id="selectall">
+            <th class="col-checkbox"><input type="checkbox" class="seriesCheck" id="SelectAll" /></th>
+            <th align="left" valign="top" class="nowrap">Select All</th>
+            <th width="100%" class="col-name" style="visibility:hidden;"></th>
+        </tr>
+    </thead>
+</table>
+
+<br/>
+<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=${show.indexerid}" class="btn btn-danger">Cancel Rename</a>
+
+<table id="testRenameTable" class="sickbeardTable" cellspacing="1" border="0" cellpadding="0">
+
+% for cur_ep_obj in ep_obj_list:
+<%
+    curLoc = cur_ep_obj.location[len(cur_ep_obj.show.location)+1:]
+    curExt = curLoc.split('.')[-1]
+    newLoc = cur_ep_obj.proper_path() + '.' + curExt
+%>
+% if int(cur_ep_obj.season) != curSeason:
+    <thead>
+        <tr class="seasonheader" id="season-${cur_ep_obj.season}">
+            <td colspan="4">
+                 <br/>
+                <h2>${('Season '+str(cur_ep_obj.season), 'Specials')[int(cur_ep_obj.season) == 0]}</h2>
+            </td>
+        </tr>
+        <tr class="seasoncols" id="season-${cur_ep_obj.season}-cols">
+            <th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="${cur_ep_obj.season}" /></th>
+            <th class="nowrap">Episode</th>
+            <th class="col-name">Old Location</th>
+            <th class="col-name">New Location</th>
+        </tr>
+    </thead>
+<% curSeason = int(cur_ep_obj.season) %>
+% endif
+    <tbody>
+<%
+odd = not odd
+epStr = str(cur_ep_obj.season) + "x" + str(cur_ep_obj.episode)
+epList = sorted([cur_ep_obj.episode] + [x.episode for x in cur_ep_obj.relatedEps])
+if len(epList) > 1:
+    epList = [min(epList), max(epList)]
+%>
+        <tr class="season-${curSeason} ${('wanted', 'good')[curLoc == newLoc]} seasonstyle">
+            <td class="col-checkbox">
+            % if curLoc != newLoc:
+                <input type="checkbox" class="epCheck" id="${str(cur_ep_obj.season) + 'x' + str(cur_ep_obj.episode)}" name="${str(cur_ep_obj.season) + "x" + str(cur_ep_obj.episode)}" />
+            % endif
+            </td>
+            <td align="center" valign="top" class="nowrap">${"-".join(map(str, epList))}</td>
+            <td width="50%" class="col-name">${curLoc}</td>
+            <td width="50%" class="col-name">${newLoc}</td>
+        </tr>
+    </tbody>
+
+% endfor
+</table><br />
+<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=${show.indexerid}" class="btn btn-danger">Cancel Rename</a>
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/testRename.tmpl b/gui/slick/interfaces/default/testRename.tmpl
deleted file mode 100644
index 953f5efb0a8009826631240bda85a678954b473b..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/testRename.tmpl
+++ /dev/null
@@ -1,107 +0,0 @@
-#import sickbeard
-#from sickbeard import common
-#from sickbeard import exceptions
-#set global $title="Test Rename"
-#set global $header = '<a href="' + $sbRoot + '/home/displayShow?show=%d">%s</a>' % ($show.indexerid, $show.name)
-#set global $sbPath=".."
-
-#set global $topmenu="home"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<input type="hidden" id="showID" value="$show.indexerid" />
-
-<script type="text/javascript" src="$sbRoot/js/testRename.js"></script>
-
-<h3>Preview of the proposed name changes</h3>
-<blockquote>
-#if int($show.air_by_date) == 1 and $sickbeard.NAMING_CUSTOM_ABD:
-    $sickbeard.NAMING_ABD_PATTERN
-#elif int($show.sports) == 1 and $sickbeard.NAMING_CUSTOM_SPORTS:
-    $sickbeard.NAMING_SPORTS_PATTERN
-#else
-    $sickbeard.NAMING_PATTERN
-#end if
-</blockquote>
-
-#set $curSeason = -1
-#set $odd = False
-
-
-<table id="SelectAllTable" class="sickbeardTable" cellspacing="1" border="0" cellpadding="0">
-    <thead>
-        <tr class="seasonheader" id="season-all">
-            <td colspan="4">
-                <h2>All Seasons</h2>
-            </td>
-        </tr>
-        <tr class="seasoncols" id="selectall">
-            <th class="col-checkbox"><input type="checkbox" class="seriesCheck" id="SelectAll" /></th>
-            <th align="left" valign="top" class="nowrap">Select All</th>
-            <th width="100%" class="col-name" style="visibility:hidden;"></th>
-        </tr>
-    </thead>
-</table>
-
-<br/>
-<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
-
-<table id="testRenameTable" class="sickbeardTable" cellspacing="1" border="0" cellpadding="0">
-
-#for $cur_ep_obj in $ep_obj_list:
-#set $curLoc = $cur_ep_obj.location[len($cur_ep_obj.show.location)+1:]
-#set $curExt = $curLoc.split('.')[-1]
-#set $newLoc = $cur_ep_obj.proper_path() + '.' + $curExt
-
-#if int($cur_ep_obj.season) != $curSeason:
-    <thead>
-        <tr class="seasonheader" id="season-$cur_ep_obj.season">
-            <td colspan="4">
-                 <br/>
-                <h2>#if int($cur_ep_obj.season) == 0 then "Specials" else "Season "+str($cur_ep_obj.season)#</h2>
-            </td>
-        </tr>
-        <tr class="seasoncols" id="season-$cur_ep_obj.season-cols">
-            <th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="$cur_ep_obj.season" /></th>
-            <th class="nowrap">Episode</th>
-            <th class="col-name">Old Location</th>
-            <th class="col-name">New Location</th>
-        </tr>
-    </thead>
-#set $curSeason = int($cur_ep_obj.season)
-#end if
-    <tbody>
-#set $odd = not $odd
-#set $epStr = str($cur_ep_obj.season) + "x" + str($cur_ep_obj.episode)
-#set $epList = sorted([cur_ep_obj.episode] + [x.episode for x in cur_ep_obj.relatedEps])
-#if len($epList) > 1:
-    #set $epList = [$min($epList), $max($epList)]
-#end if
-        <tr class="season-$curSeason
-            #if $curLoc == $newLoc:
-                good
-            #else
-                wanted
-            #end if
-        seasonstyle">
-            <td class="col-checkbox">
-            #if $curLoc != $newLoc:
-                <input type="checkbox" class="epCheck" id="<%=str(cur_ep_obj.season) + 'x' + str(cur_ep_obj.episode)%>" name="<%=str(cur_ep_obj.season) + "x" + str(cur_ep_obj.episode) %>" />
-            #end if
-            </td>
-            <td align="center" valign="top" class="nowrap"><%= "-".join(map(str, epList)) %></td>
-            <td width="50%" class="col-name">$curLoc</td>
-            <td width="50%" class="col-name">$newLoc</td>
-        </tr>
-    </tbody>
-
-#end for
-</table><br />
-<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/interfaces/default/trendingShows.mako b/gui/slick/interfaces/default/trendingShows.mako
new file mode 100644
index 0000000000000000000000000000000000000000..46401a085a8fb1749748fc43d2d8095687d9ac37
--- /dev/null
+++ b/gui/slick/interfaces/default/trendingShows.mako
@@ -0,0 +1,102 @@
+<%!
+    import sickbeard
+    import datetime
+    import re
+    from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
+    from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
+    from sickbeard import sbdatetime
+    from sickbeard.helpers import anon_url
+%>
+
+<script type="text/javascript" charset="utf-8">
+$(document).ready(function(){
+    // initialise combos for dirty page refreshes
+    $('#showsort').val('original');
+    $('#showsortdirection').val('asc');
+
+    var $container = [$('#container')];
+    jQuery.each($container, function (j) {
+        this.isotope({
+            itemSelector: '.trakt_show',
+            sortBy: 'original-order',
+            layoutMode: 'fitRows',
+            getSortData: {
+                name: function( itemElem ) {
+                    var name = $( itemElem ).attr('data-name') || '';
+% if not sickbeard.SORT_ARTICLE:
+                    name = name.replace(/^(The|A|An)\s/i, '');
+% endif
+                    return name.toLowerCase();
+                },
+                rating: '[data-rating] parseInt',
+                votes: '[data-votes] parseInt',
+            }
+        });
+    });
+
+    $('#showsort').on( 'change', function() {
+        var sortCriteria;
+        switch (this.value) {
+            case 'original':
+                sortCriteria = 'original-order'
+                break;
+            case 'rating':
+                /* randomise, else the rating_votes can already
+                 * have sorted leaving this with nothing to do.
+                 */
+                $('#container').isotope({sortBy: 'random'});
+                sortCriteria = 'rating';
+                break;
+            case 'rating_votes':
+                sortCriteria = ['rating', 'votes'];
+                break;
+            case 'votes':
+                sortCriteria = 'votes';
+                break;
+            default:
+                sortCriteria = 'name'
+                break;
+        }
+        $('#container').isotope({sortBy: sortCriteria});
+    });
+
+    $('#showsortdirection').on( 'change', function() {
+        $('#container').isotope({sortAscending: ('asc' == this.value)});
+    });
+});
+</script>
+
+<div id="container">
+% if not trending_shows:
+    <div class="trakt_show" style="width:100%; margin-top:20px">
+        <p class="red-text">Trakt API did not return any results, please check your config.
+    </div>
+% else:
+% for cur_show in trending_shows:
+    <% show_url = 'http://www.trakt.tv/shows/%s' % cur_show['show']['ids']['slug'] %>
+
+    <div class="trakt_show" data-name="${cur_show['show']['title']}" data-rating="${cur_show['show']['rating']}" data-votes="${cur_show['show']['votes']}">
+        <div class="traktContainer">
+            <div class="trakt-image">
+                <a class="trakt-image" href="${anon_url(show_url)}" target="_blank"><img alt="" class="trakt-image" src="${cur_show['show']['images']['poster']['thumb']}" /></a>
+            </div>
+
+            <div class="show-title">
+                ${(cur_show['show']['title'], '<span>&nbsp;</span>')['' == cur_show['show']['title']]}
+            </div>
+
+        <div class="clearfix">
+            <p>${int(cur_show['show']['rating']*10)}% <img src="${sbRoot}/images/heart.png"></p>
+            <i>${cur_show['show']['votes']} votes</i>
+            <div class="traktShowTitleIcons">
+                <a href="${sbRoot}/home/addShows/addTraktShow?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}&amp;showName=${cur_show['show']['title']}" class="btn btn-xs">Add Show</a>
+% if blacklist:
+                <a href="${sbRoot}/home/addShows/addShowToBlacklist?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}" class="btn btn-xs">Remove Show</a>
+% endif
+            </div>
+        </div>
+        </div>
+    </div>
+% endfor
+% endif
+</div>
diff --git a/gui/slick/interfaces/default/trendingShows.tmpl b/gui/slick/interfaces/default/trendingShows.tmpl
deleted file mode 100644
index 3d046b990767d2bd33b56822e60a4e93eb0ed3eb..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/trendingShows.tmpl
+++ /dev/null
@@ -1,104 +0,0 @@
-#import sickbeard
-#import datetime
-#import re
-#import os.path
-#from sickbeard.common import *
-#from sickbeard import sbdatetime
-#from sickbeard.helpers import anon_url
-
-<script type="text/javascript" charset="utf-8">
-<!--
-
-\$(document).ready(function(){
-    // initialise combos for dirty page refreshes
-    \$('#showsort').val('original');
-    \$('#showsortdirection').val('asc');
-
-    var \$container = [\$('#container')];
-    jQuery.each(\$container, function (j) {
-        this.isotope({
-            itemSelector: '.trakt_show',
-            sortBy: 'original-order',
-            layoutMode: 'fitRows',
-            getSortData: {
-                name: function( itemElem ) {
-                    var name = \$( itemElem ).attr('data-name') || '';
-#if not $sickbeard.SORT_ARTICLE:
-                    name = name.replace(/^(The|A|An)\s/i, '');
-#end if
-                    return name.toLowerCase();
-                },
-                rating: '[data-rating] parseInt',
-                votes: '[data-votes] parseInt',
-            }
-        });
-    });
-
-    \$('#showsort').on( 'change', function() {
-        var sortCriteria;
-        switch (this.value) {
-            case 'original':
-                sortCriteria = 'original-order'
-                break;
-            case 'rating':
-                /* randomise, else the rating_votes can already
-                 * have sorted leaving this with nothing to do.
-                 */
-                \$('#container').isotope({sortBy: 'random'});
-                sortCriteria = 'rating';
-                break;
-            case 'rating_votes':
-                sortCriteria = ['rating', 'votes'];
-                break;
-            case 'votes':
-                sortCriteria = 'votes';
-                break;
-            default:
-                sortCriteria = 'name'
-                break;
-        }
-        \$('#container').isotope({sortBy: sortCriteria});
-    });
-
-    \$('#showsortdirection').on( 'change', function() {
-        \$('#container').isotope({sortAscending: ('asc' == this.value)});
-    });
-});
-
-//-->
-</script>
-
-<div id="container">
-#if not $trending_shows
-    <div class="trakt_show" style="width:100%; margin-top:20px">
-        <p class="red-text">Trakt API did not return any results, please check your config.
-    </div>
-#else
-#for $cur_show in $trending_shows:
-    #set $show_url = 'http://www.trakt.tv/shows/%s' % $cur_show['show']['ids']['slug']
-
-    <div class="trakt_show" data-name="$cur_show['show']['title']" data-rating="$cur_show['show']['rating']" data-votes="$cur_show['show']['votes']">
-        <div class="traktContainer">
-            <div class="trakt-image">
-                <a class="trakt-image" href="<%= anon_url(show_url) %>" target="_blank"><img alt="" class="trakt-image" src="$cur_show['show']['images']['poster']['thumb']" /></a>
-            </div>
-
-            <div class="show-title">
-                <%= (cur_show['show']['title'], '<span>&nbsp;</span>')[ '' == cur_show['show']['title']] %>
-            </div>
-
-        <div class="clearfix">
-            <p><%= int(cur_show['show']['rating']*10) %>% <img src="$sbRoot/images/heart.png"></p>
-            <i>$cur_show['show']['votes'] votes</i>
-            <div class="traktShowTitleIcons">
-                <a href="$sbRoot/home/addShows/addTraktShow?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}&amp;showName=${cur_show['show']['title']}" class="btn btn-xs">Add Show</a>
-#if $blacklist
-                <a href="$sbRoot/home/addShows/addShowToBlacklist?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}" class="btn btn-xs">Remove Show</a>
-#end if
-            </div>
-        </div>
-        </div>
-    </div>
-#end for
-#end if
-</div>
diff --git a/gui/slick/interfaces/default/viewlogs.mako b/gui/slick/interfaces/default/viewlogs.mako
new file mode 100644
index 0000000000000000000000000000000000000000..b4f107846b164753297f8f22b51714a989cf719c
--- /dev/null
+++ b/gui/slick/interfaces/default/viewlogs.mako
@@ -0,0 +1,92 @@
+<%include file="/inc_top.mako"/>
+<%!
+    import sickbeard
+    from sickbeard import classes
+    from sickbeard.logger import reverseNames
+%>
+<script type="text/javascript" charset="utf-8">
+$(document).ready(
+
+function(){
+    $('#minLevel,#logFilter,#logSearch').change(function(){
+        if ( $('#logSearch').val().length > 0 ) {
+            $('#logSearch').prop('disabled', true);
+            $('#logFilter option[value=\\<NONE\\>]').prop('selected', true);
+            $('#minLevel option[value=5]').prop('selected', true);
+        }
+        $('#minLevel').prop('disabled', true);
+        $('#logFilter').prop('disabled', true);
+        $('#logSearch').prop('disabled', true);
+        document.body.style.cursor='wait'
+        url = '${sbRoot}/errorlogs/viewlog/?minLevel='+$('select[name=minLevel]').val()+'&logFilter='+$('select[name=logFilter]').val()+'&logSearch='+$('#logSearch').val()
+        window.location.href = url
+
+    });
+
+    $(window).load(function(){
+
+        if ( $('#logSearch').val().length == 0 ) {
+            $('#minLevel').prop('disabled', false);
+            $('#logFilter').prop('disabled', false);
+            $('#logSearch').prop('disabled', false);
+        } else {
+            $('#minLevel').prop('disabled', true);
+            $('#logFilter').prop('disabled', true);
+            $('#logSearch').prop('disabled', false);
+        }
+
+         document.body.style.cursor='default';
+    });
+
+    $('#logSearch').keyup(function() {
+        if ( $('#logSearch').val().length == 0 ) {
+            $('#logFilter option[value=\\<NONE\\>]').prop('selected', true);
+            $('#minLevel option[value=20]').prop('selected', true);
+            $('#minLevel').prop('disabled', false);
+            $('#logFilter').prop('disabled', false);
+            url = '${sbRoot}/errorlogs/viewlog/?minLevel='+$('select[name=minLevel]').val()+'&logFilter='+$('select[name=logFilter]').val()+'&logSearch='+$('#logSearch').val()
+            window.location.href = url
+        } else {
+            $('#minLevel').prop('disabled', true);
+            $('#logFilter').prop('disabled', true);
+        }
+    });
+});
+</script>
+
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div class="h2footer pull-right">Minimum logging level to display: <select name="minLevel" id="minLevel" class="form-control form-control-inline input-sm">
+<% levels = reverseNames.keys() %>
+<% levels.sort(lambda x,y: cmp(reverseNames[x], reverseNames[y])) %>
+% for level in levels:
+    % if not sickbeard.DEBUG and (level == 'DEBUG' or level == 'DB'):
+       <% continue %>
+    % endif
+<option value="${reverseNames[level]}" ${('', 'selected="selected"')[minLevel == reverseNames[level]]}>${level.title()}</option>
+% endfor
+</select>
+
+Filter log by: <select name="logFilter" id="logFilter" class="form-control form-control-inline input-sm">
+% for logNameFilter in sorted(logNameFilters):
+    <option value="${logNameFilter}" ${('', 'selected="selected"')[logFilter == logNameFilter]}>${logNameFilters[logNameFilter]}</option>
+% endfor
+</select>
+Search log by:
+<input type="text" name="logSearch" placeholder="clear to reset" id="logSearch" value="${('', logSearch)[bool(logSearch)]}" class="form-control form-control-inline input-sm" />
+</div>
+<br />
+<div class="align-left"><pre>
+${logLines}
+</pre>
+</div>
+<br />
+<script type="text/javascript" charset="utf-8">
+window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes
+</script>
+
+<%include file="/inc_bottom.mako"/>
diff --git a/gui/slick/interfaces/default/viewlogs.tmpl b/gui/slick/interfaces/default/viewlogs.tmpl
deleted file mode 100644
index e84edbfac5fb1caedd069bd19fa65d49e0cae879..0000000000000000000000000000000000000000
--- a/gui/slick/interfaces/default/viewlogs.tmpl
+++ /dev/null
@@ -1,105 +0,0 @@
-#import sickbeard
-#from sickbeard import classes
-#from sickbeard.common import *
-#from sickbeard.logger import reverseNames
-#set global $header="Log File"
-#set global $title="Logs"
-
-#set global $sbPath = ".."
-
-#set global $topmenu="errorlogs"#
-#import os.path
-#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
-
-<script type="text/javascript" charset="utf-8">
-<!--
-\$(document).ready(
-
-function(){
-    \$('#minLevel,#logFilter,#logSearch').change(function(){
-        if ( \$('#logSearch').val().length > 0 ) {
-            \$('#logSearch').prop('disabled', true);
-            \$('#logFilter option[value=\\<NONE\\>]').prop('selected', true);
-            \$('#minLevel option[value=5]').prop('selected', true);
-        }
-        \$('#minLevel').prop('disabled', true);
-        \$('#logFilter').prop('disabled', true);
-        \$('#logSearch').prop('disabled', true);
-        document.body.style.cursor='wait'
-        url = '$sbRoot/errorlogs/viewlog/?minLevel='+\$('select[name=minLevel]').val()+'&logFilter='+\$('select[name=logFilter]').val()+'&logSearch='+\$('#logSearch').val()
-        window.location.href = url
-
-    });
-
-    \$(window).load(function(){
-
-        if ( \$('#logSearch').val().length == 0 ) {
-            \$('#minLevel').prop('disabled', false);
-            \$('#logFilter').prop('disabled', false);
-            \$('#logSearch').prop('disabled', false);
-        } else {
-            \$('#minLevel').prop('disabled', true);
-            \$('#logFilter').prop('disabled', true);
-            \$('#logSearch').prop('disabled', false);
-        }
-
-         document.body.style.cursor='default';
-    });
-
-    \$('#logSearch').keyup(function() {
-        if ( \$('#logSearch').val().length == 0 ) {
-            \$('#logFilter option[value=\\<NONE\\>]').prop('selected', true);
-            \$('#minLevel option[value=20]').prop('selected', true);
-            \$('#minLevel').prop('disabled', false);
-            \$('#logFilter').prop('disabled', false);
-            url = '$sbRoot/errorlogs/viewlog/?minLevel='+\$('select[name=minLevel]').val()+'&logFilter='+\$('select[name=logFilter]').val()+'&logSearch='+\$('#logSearch').val()
-            window.location.href = url
-        } else {
-            \$('#minLevel').prop('disabled', true);
-            \$('#logFilter').prop('disabled', true);
-        }
-    });
-});
-//-->
-</script>
-
-#if $varExists('header')
-    <h1 class="header">$header</h1>
-#else
-    <h1 class="title">$title</h1>
-#end if
-
-<div class="h2footer pull-right">Minimum logging level to display: <select name="minLevel" id="minLevel" class="form-control form-control-inline input-sm">
-#set $levels = $reverseNames.keys()
-$levels.sort(lambda x,y: cmp($reverseNames[$x], $reverseNames[$y]))
-#for $level in $levels:
-    #if not $sickbeard.DEBUG and ($level == 'DEBUG' or $level == 'DB')
-        #continue
-    #end if
-<option value="$reverseNames[$level]" #if $minLevel == $reverseNames[$level] then "selected=\"selected\"" else ""#>$level.title()</option>
-#end for
-</select>
-
-Filter log by: <select name="logFilter" id="logFilter" class="form-control form-control-inline input-sm">
-#for $logNameFilter in sorted($logNameFilters)
-<option value="$logNameFilter" #if $logFilter == $logNameFilter then "selected=\"selected\"" else ""#>$logNameFilters[$logNameFilter]</option>
-#end for
-</select>
-Search log by:
-<input type="text" name="logSearch" placeholder="clear to reset" id="logSearch" value="#if $logSearch then $logSearch else ""#" class="form-control form-control-inline input-sm" />
-</div>
-<br />
-<div class="align-left"><pre>
-#filter WebSafe
-$logLines
-#end filter
-</pre>
-</div>
-<br />
-<script type="text/javascript" charset="utf-8">
-<!--
-window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes
-//-->
-</script>
-
-#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
diff --git a/gui/slick/js/configSearch.js b/gui/slick/js/configSearch.js
index 1840bd68d60d86c72343173fdca9a51afde7f8bb..838d224d78544b2aa973da1dbfb99544b6f105b1 100644
--- a/gui/slick/js/configSearch.js
+++ b/gui/slick/js/configSearch.js
@@ -132,6 +132,16 @@ $(document).ready(function(){
                 $('#torrent_username').prop('value', '');
                 $('#host_desc_torrent').text('URL to your Deluge client (e.g. http://localhost:8112)');
                 //$('#directory_title').text(client + directory);
+            } else if ('deluged' == selectedProvider){
+                client = 'Deluge';
+                $(torrent_verify_cert_option).hide();
+                $(torrent_verify_deluge).hide();
+                $(torrent_verify_rtorrent).hide();
+                $(label_warning_deluge).show();
+                $(label_anime_warning_deluge).show();
+                $('#torrent_username_option').show();
+                $('#host_desc_torrent').text('IP or Hostname of your Deluge Daemon (e.g. scgi://localhost:58846)');
+                //$('#directory_title').text(client + directory);
             } else if ('download_station' == selectedProvider){
                 client = 'Synology DS';
                 $(torrent_label_option).hide();
diff --git a/lib/hachoir_core/bits.py b/lib/hachoir_core/bits.py
index d5b31a018b5e3d1bb61e9a6be8874f11414352d1..97e84af81efaa47d6c154f5f08a5c99efc94ba62 100644
--- a/lib/hachoir_core/bits.py
+++ b/lib/hachoir_core/bits.py
@@ -3,7 +3,7 @@ Utilities to convert integers and binary strings to binary (number), binary
 string, number, hexadecimal, etc.
 """
 
-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.compatibility import reversed
 from itertools import chain, repeat
 from struct import calcsize, unpack, error as struct_error
@@ -30,6 +30,28 @@ def swap32(value):
          | ((value & 0x00FF0000L) >> 8) \
          | ((value & 0xFF000000L) >> 24)
 
+def arrswapmid(data):
+    r"""
+    Convert an array of characters from middle-endian to big-endian and vice-versa.
+
+    >>> arrswapmid("badcfehg")
+    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
+    """
+    assert len(data)%2 == 0
+    ret = ['']*len(data)
+    ret[1::2] = data[0::2]
+    ret[0::2] = data[1::2]
+    return ret
+
+def strswapmid(data):
+    r"""
+    Convert raw data from middle-endian to big-endian and vice-versa.
+
+    >>> strswapmid("badcfehg")
+    'abcdefgh'
+    """
+    return ''.join(arrswapmid(data))
+
 def bin2long(text, endian):
     """
     Convert binary number written in a string into an integer.
@@ -45,9 +67,10 @@ def bin2long(text, endian):
     assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
     bits = [ (ord(character)-ord("0")) \
         for character in text if character in "01" ]
-    assert len(bits) != 0
     if endian is not BIG_ENDIAN:
-        bits = reversed(bits)
+        bits = bits[::-1]
+    size = len(bits)
+    assert 0 < size
     value = 0
     for bit in bits:
         value *= 2
@@ -142,7 +165,7 @@ def long2raw(value, endian, size=None):
     '\x19\x12\x00\x00'
     """
     assert (not size and 0 < value) or (0 <= value)
-    assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
+    assert endian in (LITTLE_ENDIAN, BIG_ENDIAN, MIDDLE_ENDIAN)
     text = []
     while (value != 0 or text == ""):
         byte = value % 256
@@ -153,13 +176,15 @@ def long2raw(value, endian, size=None):
     else:
         need = 0
     if need:
-        if endian is BIG_ENDIAN:
-            text = chain(repeat("\0", need), reversed(text))
-        else:
+        if endian is LITTLE_ENDIAN:
             text = chain(text, repeat("\0", need))
+        else:
+            text = chain(repeat("\0", need), reversed(text))
     else:
-        if endian is BIG_ENDIAN:
+        if endian is not LITTLE_ENDIAN:
             text = reversed(text)
+    if endian is MIDDLE_ENDIAN:
+        text = arrswapmid(text)
     return "".join(text)
 
 def long2bin(size, value, endian, classic_mode=False):
@@ -257,6 +282,8 @@ def str2long(data, endian):
     True
     >>> str2long("\xff\xff\xff\xff\xff\xff\xff\xff", BIG_ENDIAN) == (2**64-1)
     True
+    >>> str2long("\x0b\x0a\x0d\x0c", MIDDLE_ENDIAN) == 0x0a0b0c0d
+    True
     """
     assert 1 <= len(data) <= 32   # arbitrary limit: 256 bits
     try:
@@ -264,14 +291,15 @@ def str2long(data, endian):
     except KeyError:
         pass
 
-    assert endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+    assert endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
     shift = 0
     value = 0
     if endian is BIG_ENDIAN:
         data = reversed(data)
+    elif endian is MIDDLE_ENDIAN:
+        data = reversed(strswapmid(data))
     for character in data:
         byte = ord(character)
         value += (byte << shift)
         shift += 8
     return value
-
diff --git a/lib/hachoir_core/endian.py b/lib/hachoir_core/endian.py
index 5f6ae88b415490224f552699171c8f63e62efa1b..3568010ab82d9806599083a032228254cd87ce6b 100644
--- a/lib/hachoir_core/endian.py
+++ b/lib/hachoir_core/endian.py
@@ -6,10 +6,11 @@ from hachoir_core.i18n import _
 
 BIG_ENDIAN = "ABCD"
 LITTLE_ENDIAN = "DCBA"
+MIDDLE_ENDIAN = "BADC"
 NETWORK_ENDIAN = BIG_ENDIAN
 
 endian_name = {
     BIG_ENDIAN: _("Big endian"),
     LITTLE_ENDIAN: _("Little endian"),
+    MIDDLE_ENDIAN: _("Middle endian"),
 }
-
diff --git a/lib/hachoir_core/field/basic_field_set.py b/lib/hachoir_core/field/basic_field_set.py
index c044124b8185da4368b4c860b2db31ab3f91063a..74dc057168cfcc08f8cae93d80f7047b1b6f4154 100644
--- a/lib/hachoir_core/field/basic_field_set.py
+++ b/lib/hachoir_core/field/basic_field_set.py
@@ -1,6 +1,6 @@
 from hachoir_core.field import Field, FieldError
 from hachoir_core.stream import InputStream
-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.event_handler import EventHandler
 
 class ParserError(FieldError):
@@ -60,7 +60,7 @@ class BasicFieldSet(Field):
             self._global_event_handler = None
 
         # Sanity checks (post-conditions)
-        assert self.endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+        assert self.endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
         if (self._size is not None) and (self._size <= 0):
             raise ParserError("Invalid parser '%s' size: %s" % (self.path, self._size))
 
diff --git a/lib/hachoir_core/field/character.py b/lib/hachoir_core/field/character.py
index 42bc4b1e7ca4f9560758a0c06ef2d90dab2cee65..566c43329c700537590cd9aeba26aeffcf9735b0 100644
--- a/lib/hachoir_core/field/character.py
+++ b/lib/hachoir_core/field/character.py
@@ -24,4 +24,3 @@ class Character(Bits):
 
     def createDisplay(self):
         return makePrintable(self.value, "ASCII", quote="'", to_unicode=True)
-
diff --git a/lib/hachoir_core/field/enum.py b/lib/hachoir_core/field/enum.py
index cc04a29e909c95fe4e65bbfaf4eef21660ed1d99..61873504fa21761b8402ebf87551ad7e40abdf68 100644
--- a/lib/hachoir_core/field/enum.py
+++ b/lib/hachoir_core/field/enum.py
@@ -1,7 +1,7 @@
 def Enum(field, enum, key_func=None):
     """
     Enum is an adapter to another field: it will just change its display
-    attribute. It uses a dictionnary to associate a value to another.
+    attribute. It uses a dictionary to associate a value to another.
 
     key_func is an optional function with prototype "def func(key)->key"
     which is called to transform key.
@@ -23,4 +23,3 @@ def Enum(field, enum, key_func=None):
     field.createDisplay = createDisplay
     field.getEnum = lambda: enum
     return field
-
diff --git a/lib/hachoir_core/field/field.py b/lib/hachoir_core/field/field.py
index e4c949af7348d6ec813f6a7bc6ac3d399198eed1..cc59e9ced4c6a62542a911aba3b26d5f3b9d86f3 100644
--- a/lib/hachoir_core/field/field.py
+++ b/lib/hachoir_core/field/field.py
@@ -70,6 +70,8 @@ class Field(Logger):
         assert issubclass(parent.__class__, Field)
         assert (size is None) or (0 <= size)
         self._parent = parent
+        if not name:
+            raise ValueError("empty field name")
         self._name = name
         self._address = parent.nextFieldAddress()
         self._size = size
@@ -166,7 +168,7 @@ class Field(Logger):
             return '/'
         names = []
         field = self
-        while field:
+        while field is not None:
             names.append(field._name)
             field = field._parent
         names[-1] = ''
diff --git a/lib/hachoir_core/field/float.py b/lib/hachoir_core/field/float.py
index 27f9756300d7c093e91dd4a5d14243eb3429e7a3..025b57df76e08c9f99b7f59a729229cb846c5f81 100644
--- a/lib/hachoir_core/field/float.py
+++ b/lib/hachoir_core/field/float.py
@@ -85,15 +85,15 @@ def floatFactory(name, format, mantissa_bits, exponent_bits, doc):
     cls.__name__ = name
     return cls
 
-# 32-bit float (standart: IEEE 754/854)
+# 32-bit float (standard: IEEE 754/854)
 Float32 = floatFactory("Float32", "f", 23, 8,
     "Floating point number: format IEEE 754 int 32 bit")
 
-# 64-bit float (standart: IEEE 754/854)
+# 64-bit float (standard: IEEE 754/854)
 Float64 = floatFactory("Float64", "d", 52, 11,
     "Floating point number: format IEEE 754 in 64 bit")
 
-# 80-bit float (standart: IEEE 754/854)
+# 80-bit float (standard: IEEE 754/854)
 Float80 = floatFactory("Float80", None, 64, 15,
     "Floating point number: format IEEE 754 in 80 bit")
 
diff --git a/lib/hachoir_core/field/generic_field_set.py b/lib/hachoir_core/field/generic_field_set.py
index a3b5eb7d59df16ba159c8764129f6d2242e1bb9d..4817c2fc47f9fe22c6ed85f1d8369c50c131579f 100644
--- a/lib/hachoir_core/field/generic_field_set.py
+++ b/lib/hachoir_core/field/generic_field_set.py
@@ -2,7 +2,7 @@ from hachoir_core.field import (MissingField, BasicFieldSet, Field, ParserError,
     createRawField, createNullField, createPaddingField, FakeArray)
 from hachoir_core.dict import Dict, UniqKeyError
 from hachoir_core.error import HACHOIR_ERRORS
-from hachoir_core.tools import lowerBound
+from hachoir_core.tools import lowerBound, makeUnicode
 import hachoir_core.config as config
 
 class GenericFieldSet(BasicFieldSet):
@@ -12,8 +12,8 @@ class GenericFieldSet(BasicFieldSet):
     document).
 
     Class attributes:
-    - endian: Bytes order (L{BIG_ENDIAN} or L{LITTLE_ENDIAN}). Optional if the
-      field set has a parent ;
+    - endian: Bytes order (L{BIG_ENDIAN}, L{LITTLE_ENDIAN} or L{MIDDLE_ENDIAN}).
+      Optional if the field set has a parent ;
     - static_size: (optional) Size of FieldSet in bits. This attribute should
       be used in parser of constant size.
 
@@ -310,7 +310,7 @@ class GenericFieldSet(BasicFieldSet):
         """
         if self._size is None or not self.autofix:
             return False
-        self.warning(unicode(exception))
+        self.warning(makeUnicode(exception))
         return self._fixLastField()
 
     def _feedUntil(self, field_name):
diff --git a/lib/hachoir_core/field/integer.py b/lib/hachoir_core/field/integer.py
index 763f1d2f0981d077b79990cd9380b00e23b0c5aa..1f98322b4b82016521130d64c953d842159b4b5e 100644
--- a/lib/hachoir_core/field/integer.py
+++ b/lib/hachoir_core/field/integer.py
@@ -11,8 +11,8 @@ class GenericInteger(Bits):
     Generic integer class used to generate other classes.
     """
     def __init__(self, parent, name, signed, size, description=None):
-        if not (8 <= size <= 256):
-            raise FieldError("Invalid integer size (%s): have to be in 8..256" % size)
+        if not (8 <= size <= 16384):
+            raise FieldError("Invalid integer size (%s): have to be in 8..16384" % size)
         Bits.__init__(self, parent, name, size, description)
         self.signed = signed
 
diff --git a/lib/hachoir_core/field/parser.py b/lib/hachoir_core/field/parser.py
index e294e0246576d56ecbdf96d808c82393d6feeccc..8c16bf13953ceefd20197667ee491c7e3c072750 100644
--- a/lib/hachoir_core/field/parser.py
+++ b/lib/hachoir_core/field/parser.py
@@ -1,4 +1,4 @@
-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.field import GenericFieldSet
 from hachoir_core.log import Logger
 import hachoir_core.config as config
@@ -7,7 +7,7 @@ class Parser(GenericFieldSet):
     """
     A parser is the root of all other fields. It create first level of fields
     and have special attributes and methods:
-    - endian: Byte order (L{BIG_ENDIAN} or L{LITTLE_ENDIAN}) of input data ;
+    - endian: Byte order (L{BIG_ENDIAN}, L{LITTLE_ENDIAN} or L{MIDDLE_ENDIAN}) of input data ;
     - stream: Data input stream (set in L{__init__()}) ;
     - size: Field set size will be size of input stream.
     """
@@ -21,7 +21,7 @@ class Parser(GenericFieldSet):
         """
         # Check arguments
         assert hasattr(self, "endian") \
-            and self.endian in (BIG_ENDIAN, LITTLE_ENDIAN)
+            and self.endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
 
         # Call parent constructor
         GenericFieldSet.__init__(self, None, "root", stream, description, stream.askSize(self))
diff --git a/lib/hachoir_core/field/seekable_field_set.py b/lib/hachoir_core/field/seekable_field_set.py
index 9bc3fbbc131e0262e4d11c3a2fc180e64af9b2ff..c3a3b448edc5dbc7fce0cb1a7b418c1c32f3bb4a 100644
--- a/lib/hachoir_core/field/seekable_field_set.py
+++ b/lib/hachoir_core/field/seekable_field_set.py
@@ -1,182 +1,82 @@
-from hachoir_core.field import Field, BasicFieldSet, FakeArray, MissingField, ParserError
-from hachoir_core.tools import makeUnicode
+from hachoir_core.field import BasicFieldSet, GenericFieldSet, ParserError, createRawField
 from hachoir_core.error import HACHOIR_ERRORS
-from itertools import repeat
-import hachoir_core.config as config
-
-class RootSeekableFieldSet(BasicFieldSet):
-    def __init__(self, parent, name, stream, description, size):
-        BasicFieldSet.__init__(self, parent, name, stream, description, size)
-        self._generator = self.createFields()
-        self._offset = 0
-        self._current_size = 0
-        if size:
-            self._current_max_size = size
-        else:
-            self._current_max_size = 0
-        self._field_dict = {}
-        self._field_array = []
-
-    def _feedOne(self):
-        assert self._generator
-        field = self._generator.next()
-        self._addField(field)
-        return field
-
-    def array(self, key):
-        return FakeArray(self, key)
-
-    def getFieldByAddress(self, address, feed=True):
-        for field in self._field_array:
-            if field.address <= address < field.address + field.size:
-                return field
-        for field in self._readFields():
-            if field.address <= address < field.address + field.size:
-                return field
-        return None
-
-    def _stopFeed(self):
-        self._size = self._current_max_size
-        self._generator = None
-    done = property(lambda self: not bool(self._generator))
-
-    def _getSize(self):
-        if self._size is None:
-            self._feedAll()
-        return self._size
-    size = property(_getSize)
-
-    def _getField(self, key, const):
-        field = Field._getField(self, key, const)
-        if field is not None:
-            return field
-        if key in self._field_dict:
-            return self._field_dict[key]
-        if self._generator and not const:
-            try:
-                while True:
-                    field = self._feedOne()
-                    if field.name == key:
-                        return field
-            except StopIteration:
-                self._stopFeed()
-            except HACHOIR_ERRORS, err:
-                self.error("Error: %s" % makeUnicode(err))
-                self._stopFeed()
-        return None
-
-    def getField(self, key, const=True):
-        if isinstance(key, (int, long)):
-            if key < 0:
-                raise KeyError("Key must be positive!")
-            if not const:
-                self.readFirstFields(key+1)
-            if len(self._field_array) <= key:
-                raise MissingField(self, key)
-            return self._field_array[key]
-        return Field.getField(self, key, const)
-
-    def _addField(self, field):
-        if field._name.endswith("[]"):
-            self.setUniqueFieldName(field)
-        if config.debug:
-            self.info("[+] DBG: _addField(%s)" % field.name)
-
-        if field._address != self._offset:
-            self.warning("Set field %s address to %s (was %s)" % (
-                field.path, self._offset//8, field._address//8))
-            field._address = self._offset
-        assert field.name not in self._field_dict
-
-        self._checkFieldSize(field)
-
-        self._field_dict[field.name] = field
-        self._field_array.append(field)
-        self._current_size += field.size
-        self._offset += field.size
-        self._current_max_size = max(self._current_max_size, field.address + field.size)
-
-    def _checkAddress(self, address):
-        if self._size is not None:
-            max_addr = self._size
-        else:
-            # FIXME: Use parent size
-            max_addr = self.stream.size
-        return address < max_addr
-
-    def _checkFieldSize(self, field):
-        size = field.size
-        addr = field.address
-        if not self._checkAddress(addr+size-1):
-            raise ParserError("Unable to add %s: field is too large" % field.name)
 
+# getgaps(int, int, [listof (int, int)]) -> generator of (int, int)
+# Gets all the gaps not covered by a block in `blocks` from `start` for `length` units.
+def getgaps(start, length, blocks):
+    '''
+    Example:
+    >>> list(getgaps(0, 20, [(15,3), (6,2), (6,2), (1,2), (2,3), (11,2), (9,5)]))
+    [(0, 1), (5, 1), (8, 1), (14, 1), (18, 2)]
+    '''
+    # done this way to avoid mutating the original
+    blocks = sorted(blocks, key=lambda b: b[0])
+    end = start+length
+    for s, l in blocks:
+        if s > start:
+            yield (start, s-start)
+            start = s
+        if s+l > start:
+            start = s+l
+    if start < end:
+        yield (start, end-start)
+
+class RootSeekableFieldSet(GenericFieldSet):
     def seekBit(self, address, relative=True):
         if not relative:
             address -= self.absolute_address
         if address < 0:
             raise ParserError("Seek below field set start (%s.%s)" % divmod(address, 8))
-        if not self._checkAddress(address):
-            raise ParserError("Seek above field set end (%s.%s)" % divmod(address, 8))
-        self._offset = address
+        self._current_size = address
         return None
 
     def seekByte(self, address, relative=True):
         return self.seekBit(address*8, relative)
 
-    def readMoreFields(self, number):
-        return self._readMoreFields(xrange(number))
-
-    def _feedAll(self):
-        return self._readMoreFields(repeat(1))
-
-    def _readFields(self):
-        while True:
-            added = self._readMoreFields(xrange(1))
-            if not added:
-                break
-            yield self._field_array[-1]
-
-    def _readMoreFields(self, index_generator):
-        added = 0
-        if self._generator:
-            try:
-                for index in index_generator:
-                    self._feedOne()
-                    added += 1
-            except StopIteration:
-                self._stopFeed()
-            except HACHOIR_ERRORS, err:
-                self.error("Error: %s" % makeUnicode(err))
-                self._stopFeed()
-        return added
-
-    current_length = property(lambda self: len(self._field_array))
-    current_size = property(lambda self: self._offset)
-
-    def __iter__(self):
-        for field in self._field_array:
-            yield field
-        if self._generator:
-            try:
-                while True:
-                    yield self._feedOne()
-            except StopIteration:
-                self._stopFeed()
-                raise StopIteration
-
-    def __len__(self):
-        if self._generator:
-            self._feedAll()
-        return len(self._field_array)
-
-    def nextFieldAddress(self):
-        return self._offset
+    def _fixLastField(self):
+        """
+        Try to fix last field when we know current field set size.
+        Returns new added field if any, or None.
+        """
+        assert self._size is not None
+
+        # Stop parser
+        message = ["stop parser"]
+        self._field_generator = None
+
+        # If last field is too big, delete it
+        while self._size < self._current_size:
+            field = self._deleteField(len(self._fields)-1)
+            message.append("delete field %s" % field.path)
+        assert self._current_size <= self._size
+
+        blocks = [(x.absolute_address, x.size) for x in self._fields]
+        fields = []
+        self._size = max(self._size, max(a+b for a,b in blocks) - self.absolute_address)
+        for start, length in getgaps(self.absolute_address, self._size, blocks):
+            self.seekBit(start, relative=False)
+            field = createRawField(self, length, "unparsed[]")
+            self.setUniqueFieldName(field)
+            self._fields.append(field.name, field)
+            fields.append(field)
+            message.append("found unparsed segment: start %s, length %s" % (start, length))
+        self.seekBit(self._size + self.absolute_address, relative=False)
+        message = ", ".join(message)
+        if fields:
+            self.warning("[Autofix] Fix parser error: " + message)
+        return fields
+
+    def _stopFeeding(self):
+        new_field = None
+        if self._size is None:
+            if self._parent:
+                self._size = self._current_size
 
-    def getFieldIndex(self, field):
-        return self._field_array.index(field)
+        new_field = self._fixLastField()
+        self._field_generator = None
+        return new_field
 
 class SeekableFieldSet(RootSeekableFieldSet):
     def __init__(self, parent, name, description=None, size=None):
         assert issubclass(parent.__class__, BasicFieldSet)
         RootSeekableFieldSet.__init__(self, parent, name, parent.stream, description, size)
-
diff --git a/lib/hachoir_core/field/static_field_set.py b/lib/hachoir_core/field/static_field_set.py
index afe6cdb07967e6036f43b8b2e24c75eae2eecf5f..e3897b307c89885877456d514f39027df77447e8 100644
--- a/lib/hachoir_core/field/static_field_set.py
+++ b/lib/hachoir_core/field/static_field_set.py
@@ -20,7 +20,7 @@ class StaticFieldSet(FieldSet):
         if cls._class is not cls.__name__:
             cls._class = cls.__name__
             cls.static_size = cls._computeStaticSize()
-        return object.__new__(cls)
+        return object.__new__(cls, *args, **kw)
 
     @staticmethod
     def _computeItemSize(item):
diff --git a/lib/hachoir_core/field/timestamp.py b/lib/hachoir_core/field/timestamp.py
index a533a4b22200c0f8855760dc667408782fc59a36..8a07bcdf5f6f0137aaf3dc48df7806b7e16bf0a9 100644
--- a/lib/hachoir_core/field/timestamp.py
+++ b/lib/hachoir_core/field/timestamp.py
@@ -32,7 +32,7 @@ def timestampFactory(cls_name, handler, size):
 
 TimestampUnix32 = timestampFactory("TimestampUnix32", timestampUNIX, 32)
 TimestampUnix64 = timestampFactory("TimestampUnix64", timestampUNIX, 64)
-TimestampMac32 = timestampFactory("TimestampUnix32", timestampMac32, 32)
+TimestampMac32 = timestampFactory("TimestampMac32", timestampMac32, 32)
 TimestampUUID60 = timestampFactory("TimestampUUID60", timestampUUID60, 60)
 TimestampWin64 = timestampFactory("TimestampWin64", timestampWin64, 64)
 
diff --git a/lib/hachoir_core/iso639.py b/lib/hachoir_core/iso639.py
index 61a0ba939bd4f7e3443a5f8008c7644e15f3ab23..5da70e11e61f818a84bffa715d680f423dca3323 100644
--- a/lib/hachoir_core/iso639.py
+++ b/lib/hachoir_core/iso639.py
@@ -328,7 +328,6 @@ _ISO639 = (
     (u"Micmac", "mic", None),
     (u"Minangkabau", "min", None),
     (u"Mirandese", "mwl", None),
-    (u"Miscellaneous languages", "mis", None),
     (u"Mohawk", "moh", None),
     (u"Moksha", "mdf", None),
     (u"Moldavian", "mol", "mo"),
@@ -513,6 +512,7 @@ _ISO639 = (
     (u"Uighur", "uig", "ug"),
     (u"Ukrainian", "ukr", "uk"),
     (u"Umbundu", "umb", None),
+    (u"Uncoded languages", "mis", None),
     (u"Undetermined", "und", None),
     (u"Upper Sorbian", "hsb", None),
     (u"Urdu", "urd", "ur"),
diff --git a/lib/hachoir_core/stream/input.py b/lib/hachoir_core/stream/input.py
index ec2c27c1a2332c7ec19f4ed06e076a0773d8592d..79ca6da0aae0e6b6862014f03f3cecd8007c132e 100644
--- a/lib/hachoir_core/stream/input.py
+++ b/lib/hachoir_core/stream/input.py
@@ -1,11 +1,11 @@
-from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
 from hachoir_core.error import info
 from hachoir_core.log import Logger
 from hachoir_core.bits import str2long
 from hachoir_core.i18n import getTerminalCharset
 from hachoir_core.tools import lowerBound
 from hachoir_core.i18n import _
-from os import dup, fdopen
+from hachoir_core.tools import alignValue
 from errno import ESPIPE
 from weakref import ref as weakref_ref
 from hachoir_core.stream import StreamError
@@ -168,13 +168,20 @@ class InputStream(Logger):
         raise NotImplementedError
 
     def readBits(self, address, nbits, endian):
-        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN)
-
-        shift, data, missing = self.read(address, nbits)
+        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
+
+        if endian is MIDDLE_ENDIAN:
+            # read an aligned chunk of words
+            wordaddr, remainder = divmod(address, 16)
+            wordnbits = alignValue(remainder+nbits, 16)
+            _, data, missing = self.read(wordaddr*16, wordnbits)
+            shift = remainder
+        else:
+            shift, data, missing = self.read(address, nbits)
         if missing:
             raise ReadStreamError(nbits, address)
         value = str2long(data, endian)
-        if endian is BIG_ENDIAN:
+        if endian in (BIG_ENDIAN, MIDDLE_ENDIAN):
             value >>= len(data) * 8 - shift - nbits
         else:
             value >>= shift
@@ -404,6 +411,7 @@ class InputIOStream(InputStream):
 
     def file(self):
         if hasattr(self._input, "fileno"):
+            from os import dup, fdopen
             new_fd = dup(self._input.fileno())
             new_file = fdopen(new_fd, "r")
             new_file.seek(0)
diff --git a/lib/hachoir_core/stream/output.py b/lib/hachoir_core/stream/output.py
index e31637d699a9b5993f53159a63c07b13d9c3ba3f..4300cc66e3ad47544a46be52d214dd2027751894 100644
--- a/lib/hachoir_core/stream/output.py
+++ b/lib/hachoir_core/stream/output.py
@@ -1,5 +1,5 @@
 from cStringIO import StringIO
-from hachoir_core.endian import BIG_ENDIAN
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
 from hachoir_core.bits import long2raw
 from hachoir_core.stream import StreamError
 from errno import EBADF
@@ -21,6 +21,7 @@ class OutputStream(object):
     filename = property(_getFilename)
 
     def writeBit(self, state, endian):
+        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN) # middle endian not yet supported
         if self._bit_pos == 7:
             self._bit_pos = 0
             if state:
@@ -39,6 +40,7 @@ class OutputStream(object):
             self._bit_pos += 1
 
     def writeBits(self, count, value, endian):
+        assert endian in (BIG_ENDIAN, LITTLE_ENDIAN) # middle endian not yet supported
         assert 0 <= value < 2**count
 
         # Feed bits to align to byte address
diff --git a/lib/hachoir_core/tools.py b/lib/hachoir_core/tools.py
index 30fa327ab87717aa0e8a2b1d666de94f486c7ccb..a8f77334f777476829453e80c1c4130d08aa1a0c 100644
--- a/lib/hachoir_core/tools.py
+++ b/lib/hachoir_core/tools.py
@@ -330,7 +330,14 @@ def makeUnicode(text):
     if isinstance(text, str):
         text = unicode(text, "ISO-8859-1")
     elif not isinstance(text, unicode):
-        text = unicode(text)
+        try:
+            text = unicode(text)
+        except UnicodeError:
+            try:
+                text = str(text)
+            except Exception:
+                text = repr(text)
+            return makeUnicode(text)
     text = regex_control_code.sub(
         lambda regs: controlchars[ord(regs.group(1))], text)
     text = re.sub(r"\\x0([0-7])(?=[^0-7]|$)", r"\\\1", text)
diff --git a/lib/hachoir_core/version.py b/lib/hachoir_core/version.py
index c5e95447d0b16bc24ea2266a0a4e2710f1c6e94a..e3506e93252c56b73cd69582338412d23d87b80f 100644
--- a/lib/hachoir_core/version.py
+++ b/lib/hachoir_core/version.py
@@ -1,5 +1,5 @@
 PACKAGE = "hachoir-core"
-VERSION = "1.3.3"
+VERSION = "1.3.4"
 WEBSITE = 'http://bitbucket.org/haypo/hachoir/wiki/hachoir-core'
 LICENSE = 'GNU GPL v2'
 
diff --git a/lib/hachoir_metadata/archive.py b/lib/hachoir_metadata/archive.py
index 7fa39ea71d75e9cff4d35ad3773142465dfd9f07..2ca16321afd86b90f8efa44baa0a73685f9dac2f 100644
--- a/lib/hachoir_metadata/archive.py
+++ b/lib/hachoir_metadata/archive.py
@@ -110,7 +110,7 @@ class CabMetadata(MultipleMetadata):
     def extract(self, cab):
         if "folder[0]" in cab:
             self.useFolder(cab["folder[0]"])
-        self.format_version = "Microsoft Cabinet version %s" % cab["cab_version"].display
+        self.format_version = "Microsoft Cabinet version %s.%s" % (cab["major_version"].display, cab["minor_version"].display)
         self.comment = "%s folders, %s files" % (
             cab["nb_folder"].value, cab["nb_files"].value)
         max_nb = maxNbFile(self)
diff --git a/lib/hachoir_metadata/image.py b/lib/hachoir_metadata/image.py
index 905cdd7939f728fee4e7bf32c3220e5c6e289420..1416a8f94363698f6292eb2c7a0ffcd9ec05ae39 100644
--- a/lib/hachoir_metadata/image.py
+++ b/lib/hachoir_metadata/image.py
@@ -240,7 +240,7 @@ class GifMetadata(RootMetadata):
     def useScreen(self, screen):
         self.width = screen["width"].value
         self.height = screen["height"].value
-        self.bits_per_pixel = (1 + screen["bpp"].value)
+        self.bits_per_pixel = (1 + screen["size_global_map"].value)
 
 class TargaMetadata(RootMetadata):
     def extract(self, tga):
diff --git a/lib/hachoir_metadata/jpeg.py b/lib/hachoir_metadata/jpeg.py
index 29247dc6fe7696b536d31a5181e7478ff6b7463b..a112318fca49287429c4e25c6e018ca397301795 100644
--- a/lib/hachoir_metadata/jpeg.py
+++ b/lib/hachoir_metadata/jpeg.py
@@ -1,6 +1,6 @@
 from hachoir_metadata.metadata import RootMetadata, registerExtractor
 from hachoir_metadata.image import computeComprRate
-from hachoir_parser.image.exif import ExifEntry
+from hachoir_parser.image.exif import IFD, BasicIFDEntry
 from hachoir_parser.image.jpeg import (
     JpegFile, JpegChunk,
     QUALITY_HASH_COLOR, QUALITY_SUM_COLOR,
@@ -17,21 +17,21 @@ def deg2float(degree, minute, second):
 class JpegMetadata(RootMetadata):
     EXIF_KEY = {
         # Exif metadatas
-        ExifEntry.TAG_CAMERA_MANUFACTURER: "camera_manufacturer",
-        ExifEntry.TAG_CAMERA_MODEL: "camera_model",
-        ExifEntry.TAG_ORIENTATION: "image_orientation",
-        ExifEntry.TAG_EXPOSURE: "camera_exposure",
-        ExifEntry.TAG_FOCAL: "camera_focal",
-        ExifEntry.TAG_BRIGHTNESS: "camera_brightness",
-        ExifEntry.TAG_APERTURE: "camera_aperture",
+        "Make": "camera_manufacturer",
+        "Model": "camera_model",
+        "Orientation": "image_orientation",
+        "ExposureTime": "camera_exposure",
+        "FNumber": "camera_focal",
+        "BrightnessValue": "camera_brightness",
+        "MaxApertureValue": "camera_aperture",
 
         # Generic metadatas
-        ExifEntry.TAG_IMG_TITLE: "title",
-        ExifEntry.TAG_SOFTWARE: "producer",
-        ExifEntry.TAG_FILE_TIMESTAMP: "creation_date",
-        ExifEntry.TAG_WIDTH: "width",
-        ExifEntry.TAG_HEIGHT: "height",
-        ExifEntry.TAG_USER_COMMENT: "comment",
+        "ImageDescription": "title",
+        "Software": "producer",
+        "DateTime": "creation_date",
+        "PixelXDimension": "width",
+        "PixelYDimension": "height",
+        "UserComment": "comment",
     }
 
     IPTC_KEY = {
@@ -63,7 +63,8 @@ class JpegMetadata(RootMetadata):
             self.extractAPP0(jpeg["app0/content"])
 
         if "exif/content" in jpeg:
-            for ifd in jpeg.array("exif/content/ifd"):
+            for ifd in jpeg['exif/content']:
+                if not isinstance(ifd, IFD): continue
                 for entry in ifd.array("entry"):
                     self.processIfdEntry(ifd, entry)
                 self.readGPS(ifd)
@@ -156,7 +157,7 @@ class JpegMetadata(RootMetadata):
     @fault_tolerant
     def processIfdEntry(self, ifd, entry):
         # Skip unknown tags
-        tag = entry["tag"].value
+        tag = entry["tag"].display
         if tag not in self.EXIF_KEY:
             return
         key = self.EXIF_KEY[tag]
@@ -166,20 +167,17 @@ class JpegMetadata(RootMetadata):
             return
 
         # Read value
-        if "value" in entry:
-            value = entry["value"].value
-        else:
-            value = ifd["value_%s" % entry.name].value
+        value = ifd.getEntryValues(entry)[0].value
 
         # Convert value to string
-        if tag == ExifEntry.TAG_ORIENTATION:
+        if tag == "Orientation":
             value = self.orientation_name.get(value, value)
-        elif tag == ExifEntry.TAG_EXPOSURE:
+        elif tag == "ExposureTime":
             if not value:
                 return
             if isinstance(value, float):
                 value = (value, u"1/%g" % (1/value))
-        elif entry["type"].value in (ExifEntry.TYPE_RATIONAL, ExifEntry.TYPE_SIGNED_RATIONAL):
+        elif entry["type"].value in (BasicIFDEntry.TYPE_RATIONAL, BasicIFDEntry.TYPE_SIGNED_RATIONAL):
             value = (value, u"%.3g" % value)
 
         # Store information
@@ -197,35 +195,33 @@ class JpegMetadata(RootMetadata):
         timestamp = None
         datestamp = None
         for entry in ifd.array("entry"):
-            tag = entry["tag"].value
-            if tag == ExifEntry.TAG_GPS_LATITUDE_REF:
-                if entry["value"].value == "N":
+            tag = entry["tag"].display
+            values = [v.value for v in ifd.getEntryValues(entry)]
+            if tag == "GPSLatitudeRef":
+                if values[0] == "N":
                     latitude_ref = 1
                 else:
                     latitude_ref = -1
-            elif tag == ExifEntry.TAG_GPS_LONGITUDE_REF:
-                if entry["value"].value == "E":
+            elif tag == "GPSLongitudeRef":
+                if values[0] == "E":
                     longitude_ref = 1
                 else:
                     longitude_ref = -1
-            elif tag == ExifEntry.TAG_GPS_ALTITUDE_REF:
-                if entry["value"].value == 1:
+            elif tag == "GPSAltitudeRef":
+                if values[0] == 1:
                     altitude_ref = -1
                 else:
                     altitude_ref = 1
-            elif tag == ExifEntry.TAG_GPS_LATITUDE:
-                latitude = [ifd["value_%s[%u]" % (entry.name, index)].value for index in xrange(3)]
-            elif tag == ExifEntry.TAG_GPS_LONGITUDE:
-                longitude = [ifd["value_%s[%u]" % (entry.name, index)].value for index in xrange(3)]
-            elif tag == ExifEntry.TAG_GPS_ALTITUDE:
-                altitude = ifd["value_%s" % entry.name].value
-            elif tag == ExifEntry.TAG_GPS_DATESTAMP:
-                datestamp = ifd["value_%s" % entry.name].value
-            elif tag == ExifEntry.TAG_GPS_TIMESTAMP:
-                items = [ifd["value_%s[%u]" % (entry.name, index)].value for index in xrange(3)]
-                items = map(int, items)
-                items = map(str, items)
-                timestamp = ":".join(items)
+            elif tag == "GPSLatitude":
+                latitude = values
+            elif tag == "GPSLongitude":
+                longitude = values
+            elif tag == "GPSAltitude":
+                altitude = values[0]
+            elif tag == "GPSDateStamp":
+                datestamp = values[0]
+            elif tag == "GPSTimeStamp":
+                timestamp = ':'.join(str(int(x)) for x in values)
         if latitude_ref and latitude:
             value = deg2float(*latitude)
             if latitude_ref < 0:
diff --git a/lib/hachoir_metadata/misc.py b/lib/hachoir_metadata/misc.py
index 04c70a6e7047118ae81ffc8c67aac88eabe84885..c6bbe97fa0338cf9e141a40ea4ce01d5a15f14b5 100644
--- a/lib/hachoir_metadata/misc.py
+++ b/lib/hachoir_metadata/misc.py
@@ -109,45 +109,42 @@ class OLE2_Metadata(RootMetadata):
     def extract(self, ole2):
         self._extract(ole2)
 
-    def _extract(self, fieldset, main_document=True):
-        if main_document:
-            # _feedAll() is needed to make sure that we get all root[*] fragments
+    def _extract(self, fieldset):
+        try:
             fieldset._feedAll()
-            if "root[0]" in fieldset:
-                self.useRoot(fieldset["root[0]"])
-        doc_summary = self.getField(fieldset, main_document, "doc_summary[0]")
+        except StopIteration:
+            pass
+        if "root[0]" in fieldset:
+            self._extract(self.getFragment(fieldset["root[0]"]))
+        doc_summary = self.getField(fieldset, "doc_summary[0]")
         if doc_summary:
             self.useSummary(doc_summary, True)
-        word_doc = self.getField(fieldset, main_document, "word_doc[0]")
+        word_doc = self.getField(fieldset, "word_doc[0]")
         if word_doc:
             self.useWordDocument(word_doc)
-        summary = self.getField(fieldset, main_document, "summary[0]")
+        summary = self.getField(fieldset, "summary[0]")
         if summary:
             self.useSummary(summary, False)
 
-    @fault_tolerant
-    def useRoot(self, root):
-        stream = root.getSubIStream()
+    def getFragment(self, frag):
+        stream = frag.getSubIStream()
         ministream = guessParser(stream)
         if not ministream:
             warning("Unable to create the OLE2 mini stream parser!")
-            return
-        self._extract(ministream, main_document=False)
+            return frag
+        return ministream
 
-    def getField(self, fieldset, main_document, name):
-        if name not in fieldset:
-            return None
+    def getField(self, fieldset, name):
         # _feedAll() is needed to make sure that we get all fragments
         # eg. summary[0], summary[1], ..., summary[n]
-        fieldset._feedAll()
+        try:
+            fieldset._feedAll()
+        except StopIteration:
+            pass
+        if name not in fieldset:
+            return None
         field = fieldset[name]
-        if main_document:
-            stream = field.getSubIStream()
-            field = guessParser(stream)
-            if not field:
-                warning("Unable to create the OLE2 parser for %s!" % name)
-                return None
-        return field
+        return self.getFragment(field)
 
     @fault_tolerant
     def useSummary(self, summary, is_doc_summary):
@@ -161,7 +158,7 @@ class OLE2_Metadata(RootMetadata):
 
     @fault_tolerant
     def useWordDocument(self, doc):
-        self.comment = "Encrypted: %s" % doc["fEncrypted"].value
+        self.comment = "Encrypted: %s" % doc["FIB/fEncrypted"].value
 
     @fault_tolerant
     def useProperty(self, summary, property, is_doc_summary):
diff --git a/lib/hachoir_metadata/qt/dialog_ui.py b/lib/hachoir_metadata/qt/dialog_ui.py
deleted file mode 100644
index 970257cfd9e2583e9fa5668444aef5db020fc6d2..0000000000000000000000000000000000000000
--- a/lib/hachoir_metadata/qt/dialog_ui.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'hachoir_metadata/qt/dialog.ui'
-#
-# Created: Mon Jul 26 03:10:06 2010
-#      by: PyQt4 UI code generator 4.7.3
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt4 import QtCore, QtGui
-
-class Ui_Form(object):
-    def setupUi(self, Form):
-        Form.setObjectName("Form")
-        Form.resize(441, 412)
-        self.verticalLayout = QtGui.QVBoxLayout(Form)
-        self.verticalLayout.setObjectName("verticalLayout")
-        self.horizontalLayout_2 = QtGui.QHBoxLayout()
-        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
-        self.open_button = QtGui.QPushButton(Form)
-        self.open_button.setObjectName("open_button")
-        self.horizontalLayout_2.addWidget(self.open_button)
-        self.files_combo = QtGui.QComboBox(Form)
-        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
-        sizePolicy.setHorizontalStretch(0)
-        sizePolicy.setVerticalStretch(0)
-        sizePolicy.setHeightForWidth(self.files_combo.sizePolicy().hasHeightForWidth())
-        self.files_combo.setSizePolicy(sizePolicy)
-        self.files_combo.setObjectName("files_combo")
-        self.horizontalLayout_2.addWidget(self.files_combo)
-        self.verticalLayout.addLayout(self.horizontalLayout_2)
-        self.metadata_table = QtGui.QTableWidget(Form)
-        self.metadata_table.setAlternatingRowColors(True)
-        self.metadata_table.setShowGrid(False)
-        self.metadata_table.setRowCount(0)
-        self.metadata_table.setColumnCount(0)
-        self.metadata_table.setObjectName("metadata_table")
-        self.metadata_table.setColumnCount(0)
-        self.metadata_table.setRowCount(0)
-        self.verticalLayout.addWidget(self.metadata_table)
-        self.quit_button = QtGui.QPushButton(Form)
-        self.quit_button.setObjectName("quit_button")
-        self.verticalLayout.addWidget(self.quit_button)
-
-        self.retranslateUi(Form)
-        QtCore.QMetaObject.connectSlotsByName(Form)
-
-    def retranslateUi(self, Form):
-        Form.setWindowTitle(QtGui.QApplication.translate("Form", "hachoir-metadata", None, QtGui.QApplication.UnicodeUTF8))
-        self.open_button.setText(QtGui.QApplication.translate("Form", "Open", None, QtGui.QApplication.UnicodeUTF8))
-        self.quit_button.setText(QtGui.QApplication.translate("Form", "Quit", None, QtGui.QApplication.UnicodeUTF8))
-
diff --git a/lib/hachoir_metadata/video.py b/lib/hachoir_metadata/video.py
index 215ef225a667caead77b5abad687ed2b13fdcda7..5fcb2dd122c027fa3176512e03f370f018435248 100644
--- a/lib/hachoir_metadata/video.py
+++ b/lib/hachoir_metadata/video.py
@@ -59,9 +59,10 @@ class MkvMetadata(MultipleMetadata):
     def trackCommon(self, track, meta):
         if "Name/unicode" in track:
             meta.title = track["Name/unicode"].value
-        if "Language/string" in track \
-        and track["Language/string"].value not in ("mis", "und"):
+        if "Language/string" in track:
             meta.language = track["Language/string"].value
+        else:
+            meta.language = "eng"
 
     def processVideo(self, track):
         video = Metadata(self)
@@ -222,7 +223,7 @@ class MovMetadata(RootMetadata):
         self.last_modification = hdr["lastmod_date"].value
         self.duration = timedelta(seconds=float(hdr["duration"].value) / hdr["time_scale"].value)
         self.comment = _("Play speed: %.1f%%") % (hdr["play_speed"].value*100)
-        self.comment = _("User volume: %.1f%%") % (float(hdr["volume"].value)*100//255)
+        self.comment = _("User volume: %.1f%%") % (float(hdr["volume"].value)*100)
 
     @fault_tolerant
     def processTrackHeader(self, hdr):
diff --git a/lib/hachoir_parser/archive/__init__.py b/lib/hachoir_parser/archive/__init__.py
index ecd09e8f8b2968459bf6202c6003cb42a1efc5cf..46103c1ac0522c4894fef8403a5b42cc4e7956f7 100644
--- a/lib/hachoir_parser/archive/__init__.py
+++ b/lib/hachoir_parser/archive/__init__.py
@@ -9,4 +9,5 @@ from hachoir_parser.archive.rar import RarFile
 from hachoir_parser.archive.rpm import RpmFile
 from hachoir_parser.archive.sevenzip import SevenZipParser
 from hachoir_parser.archive.mar import MarFile
-
+from hachoir_parser.archive.mozilla_ar import MozillaArchive
+from hachoir_parser.archive.zlib import ZlibData
diff --git a/lib/hachoir_parser/archive/bzip2_parser.py b/lib/hachoir_parser/archive/bzip2_parser.py
index bec1d0e1cdaec315c67081bb627e210683400562..c7df9ea79d1854acaabb339a2ab28774a84d29ae 100644
--- a/lib/hachoir_parser/archive/bzip2_parser.py
+++ b/lib/hachoir_parser/archive/bzip2_parser.py
@@ -1,14 +1,18 @@
 """
 BZIP2 archive file
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
 """
 
 from hachoir_parser import Parser
-from hachoir_core.field import (ParserError, String,
-    Bytes, Character, UInt8, UInt32, CompressedField)
-from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_core.tools import paddingSize
+from hachoir_core.field import (Field, FieldSet, GenericVector,
+    ParserError, String,
+    PaddingBits, Bit, Bits, Character,
+    UInt32, Enum, CompressedField)
+from hachoir_core.endian import BIG_ENDIAN
 from hachoir_core.text_handler import textHandler, hexadecimal
+from hachoir_parser.archive.zlib import build_tree, HuffmanCode
 
 try:
     from bz2 import BZ2Decompressor
@@ -27,6 +31,152 @@ try:
 except ImportError:
     has_deflate = False
 
+class ZeroTerminatedNumber(Field):
+    """Zero (bit) terminated number: e.g. 11110 is 4."""
+    def __init__(self, parent, name, description=None):
+        Field.__init__(self, parent, name, 0, description)
+
+        endian = self.parent.endian
+        stream = self.parent.stream
+        addr = self.absolute_address
+
+        value = 0
+        while True:
+            bit = stream.readBits(addr, 1, endian)
+            addr += 1
+            self._size += 1
+            if not bit:
+                break
+            value += 1
+        self._value = value
+    def createValue(self):
+        return self._value
+
+def move_to_front(l, c):
+    l[:] = l[c:c+1] + l[0:c] + l[c+1:]
+
+class Bzip2Bitmap(FieldSet):
+    def __init__(self, parent, name, nb_items, start_index, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.nb_items = nb_items
+        self.start_index = start_index
+
+    def createFields(self):
+        for i in xrange(self.start_index, self.start_index+self.nb_items):
+            yield Bit(self, "symbol_used[%i]"%i, "Is the symbol %i (%r) used?"%(i, chr(i)))
+
+class Bzip2Lengths(FieldSet):
+    def __init__(self, parent, name, symbols, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.symbols = symbols
+
+    def createFields(self):
+        yield Bits(self, "start_length", 5)
+        length = self["start_length"].value
+        lengths = []
+        for i in xrange(self.symbols):
+            while True:
+                bit = Bit(self, "change_length[%i][]"%i, "Should the length be changed for symbol %i?"%i)
+                yield bit
+                if not bit.value:
+                    break
+                else:
+                    bit = Enum(Bit(self, "length_decrement[%i][]"%i, "Decrement the value?"), {True: "Decrement", False: "Increment"})
+                    yield bit
+                    if bit.value:
+                        length -= 1
+                    else:
+                        length += 1
+            lengths.append(length)
+        self.final_length = length
+        self.tree = build_tree(lengths)
+
+class Bzip2Selectors(FieldSet):
+    def __init__(self, parent, name, ngroups, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.groups = range(ngroups)
+
+    def createFields(self):
+        for i in xrange(self["../selectors_used"].value):
+            field = ZeroTerminatedNumber(self, "selector_list[]")
+            move_to_front(self.groups, field.value)
+            field.realvalue = self.groups[0]
+            field._description = "MTF'ed selector index: raw value %i, real value %i"%(field.value, field.realvalue)
+            yield field
+
+class Bzip2Block(FieldSet):
+    def createFields(self):
+        yield textHandler(Bits(self, "blockheader", 48, "Block header"), hexadecimal)
+        if self["blockheader"].value != 0x314159265359: # pi
+            raise ParserError("Invalid block header!")
+        yield textHandler(UInt32(self, "crc32", "CRC32 for this block"), hexadecimal)
+        yield Bit(self, "randomized", "Is this block randomized?")
+        yield Bits(self, "orig_bwt_pointer", 24, "Starting pointer into BWT after untransform")
+        yield GenericVector(self, "huffman_used_map", 16, Bit, 'block_used', "Bitmap showing which blocks (representing 16 literals each) are in use")
+        symbols_used = []
+        for index, block_used in enumerate(self["huffman_used_map"].array('block_used')):
+            if block_used.value:
+                start_index = index*16
+                field = Bzip2Bitmap(self, "huffman_used_bitmap[%i]"%index, 16, start_index, "Bitmap for block %i (literals %i to %i) showing which symbols are in use"%(index, start_index, start_index + 15))
+                yield field
+                for i, used in enumerate(field):
+                    if used.value:
+                        symbols_used.append(start_index + i)
+        yield Bits(self, "huffman_groups", 3, "Number of different Huffman tables in use")
+        yield Bits(self, "selectors_used", 15, "Number of times the Huffman tables are switched")
+        yield Bzip2Selectors(self, "selectors_list", self["huffman_groups"].value)
+        trees = []
+        for group in xrange(self["huffman_groups"].value):
+            field = Bzip2Lengths(self, "huffman_lengths[]", len(symbols_used)+2)
+            yield field
+            trees.append(field.tree)
+        counter = 0
+        rle_run = 0
+        selector_tree = None
+        while True:
+            if counter%50 == 0:
+                select_id = self["selectors_list"].array("selector_list")[counter//50].realvalue
+                selector_tree = trees[select_id]
+            field = HuffmanCode(self, "huffman_code[]", selector_tree)
+            if field.realvalue in [0, 1]:
+                # RLE codes
+                if rle_run == 0:
+                    rle_power = 1
+                rle_run += (field.realvalue + 1) * rle_power
+                rle_power <<= 1
+                field._description = "RLE Run Code %i (for %r); Total accumulated run %i (Huffman Code %i)" % (field.realvalue, chr(symbols_used[0]), rle_run, field.value)
+            elif field.realvalue == len(symbols_used)+1:
+                field._description = "Block Terminator (%i) (Huffman Code %i)"%(field.realvalue, field.value)
+                yield field
+                break
+            else:
+                rle_run = 0
+                move_to_front(symbols_used, field.realvalue-1)
+                field._description = "Literal %r (value %i) (Huffman Code %i)"%(chr(symbols_used[0]), field.realvalue, field.value)
+            yield field
+            if field.realvalue == len(symbols_used)+1:
+                break
+            counter += 1
+
+class Bzip2Stream(FieldSet):
+    START_BLOCK = 0x314159265359 # pi
+    END_STREAM = 0x177245385090 # sqrt(pi)
+    def createFields(self):
+        end = False
+        while not end:
+            marker = self.stream.readBits(self.absolute_address + self.current_size, 48, self.endian)
+            if marker == self.START_BLOCK:
+                yield Bzip2Block(self, "block[]")
+            elif marker == self.END_STREAM:
+                yield textHandler(Bits(self, "stream_end", 48, "End-of-stream marker"), hexadecimal)
+                yield textHandler(UInt32(self, "crc32", "CRC32 for entire stream"), hexadecimal)
+                padding = paddingSize(self.current_size, 8)
+                if padding:
+                    yield PaddingBits(self, "padding[]", padding)
+                end = True
+            else:
+                raise ParserError("Invalid marker 0x%02X!"%marker)
+
 class Bzip2Parser(Parser):
     PARSER_TAGS = {
         "id": "bzip2",
@@ -37,7 +187,7 @@ class Bzip2Parser(Parser):
         "magic": (('BZh', 0),),
         "description": "bzip2 archive"
     }
-    endian = LITTLE_ENDIAN
+    endian = BIG_ENDIAN
 
     def validate(self):
         if self.stream.readBytes(0, 3) != 'BZh':
@@ -50,18 +200,6 @@ class Bzip2Parser(Parser):
         yield String(self, "id", 3, "Identifier (BZh)", charset="ASCII")
         yield Character(self, "blocksize", "Block size (KB of memory needed to uncompress)")
 
-        yield UInt8(self, "blockheader", "Block header")
-        if self["blockheader"].value == 0x17:
-            yield String(self, "id2", 4, "Identifier2 (re8P)", charset="ASCII")
-            yield UInt8(self, "id3", "Identifier3 (0x90)")
-        elif self["blockheader"].value == 0x31:
-            yield String(self, "id2", 5, "Identifier 2 (AY&SY)", charset="ASCII")
-            if self["id2"].value != "AY&SY":
-                raise ParserError("Invalid identifier 2 (AY&SY)!")
-        else:
-            raise ParserError("Invalid block header!")
-        yield textHandler(UInt32(self, "crc32", "CRC32"), hexadecimal)
-
         if self._size is None: # TODO: is it possible to handle piped input?
             raise NotImplementedError
 
@@ -73,7 +211,7 @@ class Bzip2Parser(Parser):
                     break
             else:
                 filename = None
-            data = Bytes(self, "file", size)
+            data = Bzip2Stream(self, "file", size=size*8)
             if has_deflate:
                 CompressedField(self, Bunzip2)
                 def createInputStream(**args):
diff --git a/lib/hachoir_parser/archive/cab.py b/lib/hachoir_parser/archive/cab.py
index 856b01eebc46c60479f29e9722f92e88988e5cd6..66c0eec18647229eb089d9a35d8366523a47f7fd 100644
--- a/lib/hachoir_parser/archive/cab.py
+++ b/lib/hachoir_parser/archive/cab.py
@@ -1,18 +1,24 @@
 """
 Microsoft Cabinet (CAB) archive.
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
 Creation date: 31 january 2007
-"""
 
+- Microsoft Cabinet SDK
+  http://msdn2.microsoft.com/en-us/library/ms974336.aspx
+"""
+from __future__ import absolute_import
 from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, Enum,
     CString, String,
-    UInt16, UInt32, Bit, Bits, PaddingBits, NullBits,
+    UInt8, UInt16, UInt32, Bit, Bits, PaddingBits, NullBits,
     DateTimeMSDOS32, RawBytes)
-from hachoir_parser.common.msdos import MSDOSFileAttr16
 from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
 from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_core.tools import paddingSize
+from hachoir_core.stream import StringInputStream
+from hachoir_parser.archive.lzx import LZXStream, lzx_decompress
+from hachoir_parser.archive.zlib import DeflateBlock
 
 MAX_NB_FOLDER = 30
 
@@ -26,38 +32,54 @@ COMPRESSION_NAME = {
 
 class Folder(FieldSet):
     def createFields(self):
-        yield UInt32(self, "off_data", "Offset of data")
-        yield UInt16(self, "cf_data")
+        yield UInt32(self, "offset", "Offset to data (from file start)")
+        yield UInt16(self, "data_blocks", "Number of data blocks which are in this cabinet")
         yield Enum(Bits(self, "compr_method", 4, "Compression method"), COMPRESSION_NAME)
-        yield Bits(self, "compr_level", 5, "Compression level")
-        yield PaddingBits(self, "padding", 7)
+        if self["compr_method"].value in [2, 3]: # Quantum or LZX use compression level
+            yield PaddingBits(self, "padding[]", 4)
+            yield Bits(self, "compr_level", 5, "Compression level")
+            yield PaddingBits(self, "padding[]", 3)
+        else:
+            yield PaddingBits(self, "padding[]", 12)
+        if self["../flags/has_reserved"].value and self["../reserved_folder_size"].value:
+            yield RawBytes(self, "reserved_folder", self["../reserved_folder_size"].value, "Per-folder reserved area")
 
     def createDescription(self):
         text= "Folder: compression %s" % self["compr_method"].display
-        if self["compr_method"].value != COMPRESSION_NONE:
-            text += " (level %u)" % self["compr_level"].value
+        if self["compr_method"].value in [2, 3]: # Quantum or LZX use compression level
+            text += " (level %u: window size %u)" % (self["compr_level"].value, 2**self["compr_level"].value)
         return text
 
+class CabFileAttributes(FieldSet):
+    def createFields(self):
+        yield Bit(self, "readonly")
+        yield Bit(self, "hidden")
+        yield Bit(self, "system")
+        yield Bits(self, "reserved[]", 2)
+        yield Bit(self, "archive", "Has the file been modified since the last backup?")
+        yield Bit(self, "exec", "Run file after extraction?")
+        yield Bit(self, "name_is_utf", "Is the filename using UTF-8?")
+        yield Bits(self, "reserved[]", 8)
+
 class File(FieldSet):
     def createFields(self):
         yield filesizeHandler(UInt32(self, "filesize", "Uncompressed file size"))
-        yield UInt32(self, "offset", "File offset after decompression")
-        yield UInt16(self, "iFolder", "file control id")
+        yield UInt32(self, "folder_offset", "File offset in uncompressed folder")
+        yield Enum(UInt16(self, "folder_index", "Containing folder ID (index)"), {
+            0xFFFD:"Folder continued from previous cabinet (real folder ID = 0)",
+            0xFFFE:"Folder continued to next cabinet (real folder ID = %i)" % (self["../nb_folder"].value - 1),
+            0xFFFF:"Folder spanning previous, current and next cabinets (real folder ID = 0)"})
         yield DateTimeMSDOS32(self, "timestamp")
-        yield MSDOSFileAttr16(self, "attributes")
-        yield CString(self, "filename", charset="ASCII")
+        yield CabFileAttributes(self, "attributes")
+        if self["attributes/name_is_utf"].value:
+            yield CString(self, "filename", charset="UTF-8")
+        else:
+            yield CString(self, "filename", charset="ASCII")
 
     def createDescription(self):
         return "File %s (%s)" % (
             self["filename"].display, self["filesize"].display)
 
-class Reserved(FieldSet):
-    def createFields(self):
-        yield UInt32(self, "size")
-        size = self["size"].value
-        if size:
-            yield RawBytes(self, "data", size)
-
 class Flags(FieldSet):
     static_size = 16
     def createFields(self):
@@ -66,6 +88,111 @@ class Flags(FieldSet):
         yield Bit(self, "has_reserved")
         yield NullBits(self, "padding", 13)
 
+class FragmentGroup:
+    def __init__(self, parser):
+        self.items = []
+        self.parser = parser
+        self.args = {}
+
+    def add(self, item):
+        self.items.append(item)
+
+    def createInputStream(self):
+        # FIXME: Use lazy stream creation
+        data = []
+        for item in self.items:
+            data.append( item["rawdata"].value )
+        data = "".join(data)
+
+        # FIXME: Use smarter code to send arguments
+        self.args["compr_level"] = self.items[0].parent.parent.folder["compr_level"].value
+        tags = {"class": self.parser, "args": self.args}
+        tags = tags.iteritems()
+        return StringInputStream(data, "<fragment group>", tags=tags)
+
+class CustomFragment(FieldSet):
+    def __init__(self, parent, name, size, parser, description=None, group=None):
+        FieldSet.__init__(self, parent, name, description, size=size)
+        if not group:
+            group = FragmentGroup(parser)
+        self.field_size = size
+        self.group = group
+        self.group.add(self)
+
+    def createFields(self):
+        yield RawBytes(self, "rawdata", self.field_size//8)
+
+    def _createInputStream(self, **args):
+        return self.group.createInputStream()
+
+class DataBlock(FieldSet):
+    def __init__(self, *args, **kwargs):
+        FieldSet.__init__(self, *args, **kwargs)
+        size = (self["size"].value + 8) * 8 # +8 for header values
+        if self["/flags/has_reserved"].value:
+            size += self["/reserved_data_size"].value * 8
+        self._size = size
+
+    def createFields(self):
+        yield textHandler(UInt32(self, "crc32"), hexadecimal)
+        yield UInt16(self, "size")
+        yield UInt16(self, "uncompressed_size", "If this is 0, this block is continued in a subsequent cabinet")
+        if self["/flags/has_reserved"].value and self["/reserved_data_size"].value:
+            yield RawBytes(self, "reserved_data", self["/reserved_data_size"].value, "Per-datablock reserved area")
+        compr_method = self.parent.folder["compr_method"].value
+        if compr_method == 0: # Uncompressed
+            yield RawBytes(self, "data", self["size"].value, "Folder Data")
+            self.parent.uncompressed_data += self["data"].value
+        elif compr_method == 1: # MSZIP
+            yield String(self, "mszip_signature", 2, "MSZIP Signature (CK)")
+            yield DeflateBlock(self, "deflate_block", self.parent.uncompressed_data)
+            padding = paddingSize(self.current_size, 8)
+            if padding:
+                yield PaddingBits(self, "padding[]", padding)
+            self.parent.uncompressed_data = self["deflate_block"].uncomp_data
+        elif compr_method == 2: # Quantum
+            yield RawBytes(self, "compr_data", self["size"].value, "Compressed Folder Data")
+        elif compr_method == 3: # LZX
+            group = getattr(self.parent.folder, "lzx_group", None)
+            field = CustomFragment(self, "data", self["size"].value*8, LZXStream, "LZX data fragment", group)
+            self.parent.folder.lzx_group = field.group
+            yield field
+
+class FolderParser(Parser):
+    endian = LITTLE_ENDIAN
+    def createFields(self):
+        for file in sorted(self.files, key=lambda x:x["folder_offset"].value):
+            padding = self.seekByte(file["folder_offset"].value)
+            if padding:
+                yield padding
+            yield RawBytes(self, "file[]", file["filesize"].value, file.description)
+
+class FolderData(FieldSet):
+    def __init__(self, parent, name, folder, files, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        def createInputStream(cis, source=None, **args):
+            stream = cis(source=source)
+            tags = args.setdefault("tags",[])
+            tags.extend(stream.tags)
+            tags.append(( "class", FolderParser ))
+            tags.append(( "args", {'files': files} ))
+            for unused in self:
+                pass
+            if folder["compr_method"].value == 3: # LZX
+                self.uncompressed_data = lzx_decompress(self["block[0]/data"].getSubIStream(), folder["compr_level"].value)
+            return StringInputStream(self.uncompressed_data, source=source, **args)
+        self.setSubIStream(createInputStream)
+        self.files = files
+        self.folder = folder # Folder fieldset
+
+    def createFields(self):
+        self.uncompressed_data = ""
+        for index in xrange(self.folder["data_blocks"].value):
+            block = DataBlock(self, "block[]")
+            for i in block:
+                pass
+            yield block
+
 class CabFile(Parser):
     endian = LITTLE_ENDIAN
     MAGIC = "MSCF"
@@ -82,8 +209,8 @@ class CabFile(Parser):
     def validate(self):
         if self.stream.readBytes(0, 4) != self.MAGIC:
             return "Invalid magic"
-        if self["cab_version"].value != 0x0103:
-            return "Unknown version (%s)" % self["cab_version"].display
+        if self["major_version"].value != 1 or self["minor_version"].value != 3:
+            return "Unknown version (%i.%i)" % (self["major_version"].value, self["minor_version"].value)
         if not (1 <= self["nb_folder"].value <= MAX_NB_FOLDER):
             return "Invalid number of folder (%s)" % self["nb_folder"].value
         return True
@@ -95,26 +222,54 @@ class CabFile(Parser):
         yield textHandler(UInt32(self, "fld_checksum", "Folders checksum (0 if not used)"), hexadecimal)
         yield UInt32(self, "off_file", "Offset of first file")
         yield textHandler(UInt32(self, "files_checksum", "Files checksum (0 if not used)"), hexadecimal)
-        yield textHandler(UInt16(self, "cab_version", "Cabinet version"), hexadecimal)
+        yield UInt8(self, "minor_version", "Minor version (should be 3)")
+        yield UInt8(self, "major_version", "Major version (should be 1)")
         yield UInt16(self, "nb_folder", "Number of folders")
         yield UInt16(self, "nb_files", "Number of files")
         yield Flags(self, "flags")
         yield UInt16(self, "setid")
-        yield UInt16(self, "number", "Zero-based cabinet number")
+        yield UInt16(self, "cabinet_serial", "Zero-based cabinet number")
 
-        # --- TODO: Support flags
         if self["flags/has_reserved"].value:
-            yield Reserved(self, "reserved")
-        #(3) Previous cabinet name, if CAB_HEADER.flags & CAB_FLAG_HASPREV
-        #(4) Previous disk name, if CAB_HEADER.flags & CAB_FLAG_HASPREV
-        #(5) Next cabinet name, if CAB_HEADER.flags & CAB_FLAG_HASNEXT
-        #(6) Next disk name, if CAB_HEADER.flags & CAB_FLAG_HASNEXT
-        # ----
+            yield UInt16(self, "reserved_header_size", "Size of per-cabinet reserved area")
+            yield UInt8(self, "reserved_folder_size", "Size of per-folder reserved area")
+            yield UInt8(self, "reserved_data_size", "Size of per-datablock reserved area")
+            if self["reserved_header_size"].value:
+                yield RawBytes(self, "reserved_header", self["reserved_header_size"].value, "Per-cabinet reserved area")
+        if self["flags/has_previous"].value:
+            yield CString(self, "previous_cabinet", "File name of previous cabinet", charset="ASCII")
+            yield CString(self, "previous_disk", "Description of disk/media on which previous cabinet resides", charset="ASCII")
+        if self["flags/has_next"].value:
+            yield CString(self, "next_cabinet", "File name of next cabinet", charset="ASCII")
+            yield CString(self, "next_disk", "Description of disk/media on which next cabinet resides", charset="ASCII")
 
+        folders = []
+        files = []
         for index in xrange(self["nb_folder"].value):
-            yield Folder(self, "folder[]")
+            folder = Folder(self, "folder[]")
+            yield folder
+            folders.append(folder)
         for index in xrange(self["nb_files"].value):
-            yield File(self, "file[]")
+            file = File(self, "file[]")
+            yield file
+            files.append(file)
+
+        folders = sorted(enumerate(folders), key=lambda x:x[1]["offset"].value)
+
+        for i in xrange(len(folders)):
+            index, folder = folders[i]
+            padding = self.seekByte(folder["offset"].value)
+            if padding:
+                yield padding
+            files = []
+            for file in files:
+                if file["folder_index"].value == index:
+                    files.append(file)
+            if i+1 == len(folders):
+                size = (self.size // 8) - folder["offset"].value
+            else:
+                size = (folders[i+1][1]["offset"].value) - folder["offset"].value
+            yield FolderData(self, "folder_data[%i]" % index, folder, files, size=size*8)
 
         end = self.seekBit(self.size, "endraw")
         if end:
diff --git a/lib/hachoir_parser/archive/lzx.py b/lib/hachoir_parser/archive/lzx.py
new file mode 100644
index 0000000000000000000000000000000000000000..39f5a6ef0f4eb60a68bf23aec69abce606f407c9
--- /dev/null
+++ b/lib/hachoir_parser/archive/lzx.py
@@ -0,0 +1,267 @@
+"""LZX data stream parser.
+
+Also includes a decompression function (slow!!) which can decompress
+LZX data stored in a Hachoir stream.
+
+Author: Robert Xiao
+Creation date: July 18, 2007
+"""
+from hachoir_parser import Parser
+from hachoir_core.field import (FieldSet,
+    UInt32, Bit, Bits, PaddingBits,
+    RawBytes, ParserError)
+from hachoir_core.endian import MIDDLE_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.tools import paddingSize, alignValue
+from hachoir_parser.archive.zlib import build_tree, HuffmanCode, extend_data
+from hachoir_core.bits import str2long
+import new # for instancemethod
+
+class LZXPreTreeEncodedTree(FieldSet):
+    def __init__(self, parent, name, num_elements, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.num_elements = num_elements
+        
+    def createFields(self):
+        for i in xrange(20):
+            yield Bits(self, "pretree_lengths[]", 4)
+        pre_tree = build_tree([self['pretree_lengths[%d]'%x].value for x in xrange(20)])
+        if not hasattr(self.root, "lzx_tree_lengths_"+self.name):
+            self.lengths = [0] * self.num_elements
+            setattr(self.root, "lzx_tree_lengths_"+self.name, self.lengths)
+        else:
+            self.lengths = getattr(self.root, "lzx_tree_lengths_"+self.name)
+        i = 0
+        while i < self.num_elements:
+            field = HuffmanCode(self, "tree_code[]", pre_tree)
+            if field.realvalue <= 16:
+                self.lengths[i] = (self.lengths[i] - field.realvalue) % 17
+                field._description = "Literal tree delta length %i (new length value %i for element %i)" % (
+                        field.realvalue, self.lengths[i], i)
+                i += 1
+                yield field
+            elif field.realvalue == 17:
+                field._description = "Tree Code 17: Zeros for 4-19 elements"
+                yield field
+                extra = Bits(self, "extra[]", 4)
+                zeros = 4 + extra.value
+                extra._description = "Extra bits: zeros for %i elements (elements %i through %i)" % (zeros, i, i+zeros-1)
+                yield extra
+                self.lengths[i:i+zeros] = [0] * zeros
+                i += zeros
+            elif field.realvalue == 18:
+                field._description = "Tree Code 18: Zeros for 20-51 elements"
+                yield field
+                extra = Bits(self, "extra[]", 5)
+                zeros = 20 + extra.value
+                extra._description = "Extra bits: zeros for %i elements (elements %i through %i)" % (zeros, i, i+zeros-1)
+                yield extra
+                self.lengths[i:i+zeros] = [0] * zeros
+                i += zeros
+            elif field.realvalue == 19:
+                field._description = "Tree Code 19: Same code for 4-5 elements"
+                yield field
+                extra = Bits(self, "extra[]", 1)
+                run = 4 + extra.value
+                extra._description = "Extra bits: run for %i elements (elements %i through %i)" % (run, i, i+run-1)
+                yield extra
+                newfield = HuffmanCode(self, "tree_code[]", pre_tree)
+                assert newfield.realvalue <= 16
+                newfield._description = "Literal tree delta length %i (new length value %i for elements %i through %i)" % (
+                        newfield.realvalue, self.lengths[i], i, i+run-1)
+                self.lengths[i:i+run] = [(self.lengths[i] - newfield.realvalue) % 17] * run
+                i += run
+                yield newfield
+
+class LZXBlock(FieldSet):
+    WINDOW_SIZE = {15:30,
+                   16:32,
+                   17:34,
+                   18:36,
+                   19:38,
+                   20:42,
+                   21:50}
+    POSITION_SLOTS = {0:(0,0,0),
+                      1:(1,1,0),
+                      2:(2,2,0),
+                      3:(3,3,0),
+                      4:(4,5,1),
+                      5:(6,7,1),
+                      6:(8,11,2),
+                      7:(12,15,2),
+                      8:(16,23,3),
+                      9:(24,31,3),
+                      10:(32,47,4),
+                      11:(48,63,4),
+                      12:(64,95,5),
+                      13:(96,127,5),
+                      14:(128,191,6),
+                      15:(192,255,6),
+                      16:(256,383,7),
+                      17:(384,511,7),
+                      18:(512,767,8),
+                      19:(768,1023,8),
+                      20:(1024,1535,9),
+                      21:(1536,2047,9),
+                      22:(2048,3071,10),
+                      23:(3072,4095,10),
+                      24:(4096,6143,11),
+                      25:(6144,8191,11),
+                      26:(8192,12287,12),
+                      27:(12288,16383,12),
+                      28:(16384,24575,13),
+                      29:(24576,32767,13),
+                      30:(32768,49151,14),
+                      31:(49152,65535,14),
+                      32:(65536,98303,15),
+                      33:(98304,131071,15),
+                      34:(131072,196607,16),
+                      35:(196608,262143,16),
+                      36:(262144,393215,17),
+                      37:(393216,524287,17),
+                      38:(524288,655359,17),
+                      39:(655360,786431,17),
+                      40:(786432,917503,17),
+                      41:(917504,1048575,17),
+                      42:(1048576,1179647,17),
+                      43:(1179648,1310719,17),
+                      44:(1310720,1441791,17),
+                      45:(1441792,1572863,17),
+                      46:(1572864,1703935,17),
+                      47:(1703936,1835007,17),
+                      48:(1835008,1966079,17),
+                      49:(1966080,2097151,17),
+                      }
+    def createFields(self):
+        yield Bits(self, "block_type", 3)
+        yield Bits(self, "block_size", 24)
+        self.uncompressed_size = self["block_size"].value
+        self.compression_level = self.root.compr_level
+        self.window_size = self.WINDOW_SIZE[self.compression_level]
+        self.block_type = self["block_type"].value
+        curlen = len(self.parent.uncompressed_data)
+        if self.block_type in (1, 2): # Verbatim or aligned offset block
+            if self.block_type == 2:
+                for i in xrange(8):
+                    yield Bits(self, "aligned_len[]", 3)
+                aligned_tree = build_tree([self['aligned_len[%d]'%i].value for i in xrange(8)])
+            yield LZXPreTreeEncodedTree(self, "main_tree_start", 256)
+            yield LZXPreTreeEncodedTree(self, "main_tree_rest", self.window_size * 8)
+            main_tree = build_tree(self["main_tree_start"].lengths + self["main_tree_rest"].lengths)
+            yield LZXPreTreeEncodedTree(self, "length_tree", 249)
+            length_tree = build_tree(self["length_tree"].lengths)
+            current_decoded_size = 0
+            while current_decoded_size < self.uncompressed_size:
+                if (curlen+current_decoded_size) % 32768 == 0 and (curlen+current_decoded_size) != 0:
+                    padding = paddingSize(self.address + self.current_size, 16)
+                    if padding:
+                        yield PaddingBits(self, "padding[]", padding)
+                field = HuffmanCode(self, "main_code[]", main_tree)
+                if field.realvalue < 256:
+                    field._description = "Literal value %r" % chr(field.realvalue)
+                    current_decoded_size += 1
+                    self.parent.uncompressed_data += chr(field.realvalue)
+                    yield field
+                    continue
+                position_header, length_header = divmod(field.realvalue - 256, 8)
+                info = self.POSITION_SLOTS[position_header]
+                if info[2] == 0:
+                    if info[0] == 0:
+                        position = self.parent.r0
+                        field._description = "Position Slot %i, Position [R0] (%i)" % (position_header, position)
+                    elif info[0] == 1:
+                        position = self.parent.r1
+                        self.parent.r1 = self.parent.r0
+                        self.parent.r0 = position
+                        field._description = "Position Slot %i, Position [R1] (%i)" % (position_header, position)
+                    elif info[0] == 2:
+                        position = self.parent.r2
+                        self.parent.r2 = self.parent.r0
+                        self.parent.r0 = position
+                        field._description = "Position Slot %i, Position [R2] (%i)" % (position_header, position)
+                    else:
+                        position = info[0] - 2
+                        self.parent.r2 = self.parent.r1
+                        self.parent.r1 = self.parent.r0
+                        self.parent.r0 = position
+                        field._description = "Position Slot %i, Position %i" % (position_header, position)
+                else:
+                    field._description = "Position Slot %i, Positions %i to %i" % (position_header, info[0] - 2, info[1] - 2)
+                if length_header == 7:
+                    field._description += ", Length Values 9 and up"
+                    yield field
+                    length_field = HuffmanCode(self, "length_code[]", length_tree)
+                    length = length_field.realvalue + 9
+                    length_field._description = "Length Code %i, total length %i" % (length_field.realvalue, length)
+                    yield length_field
+                else:
+                    field._description += ", Length Value %i (Huffman Code %i)"%(length_header + 2, field.value)
+                    yield field
+                    length = length_header + 2
+                if info[2]:
+                    if self.block_type == 1 or info[2] < 3: # verbatim
+                        extrafield = Bits(self, "position_extra[%s" % field.name.split('[')[1], info[2])
+                        position = extrafield.value + info[0] - 2
+                        extrafield._description = "Position Extra Bits (%i), total position %i"%(extrafield.value, position)
+                        yield extrafield
+                    else: # aligned offset
+                        position = info[0] - 2
+                        if info[2] > 3:
+                            extrafield = Bits(self, "position_verbatim[%s" % field.name.split('[')[1], info[2]-3)
+                            position += extrafield.value*8
+                            extrafield._description = "Position Verbatim Bits (%i), added position %i"%(extrafield.value, extrafield.value*8)
+                            yield extrafield
+                        if info[2] >= 3:
+                            extrafield = HuffmanCode(self, "position_aligned[%s" % field.name.split('[')[1], aligned_tree)
+                            position += extrafield.realvalue
+                            extrafield._description = "Position Aligned Bits (%i), total position %i"%(extrafield.realvalue, position)
+                            yield extrafield
+                    self.parent.r2 = self.parent.r1
+                    self.parent.r1 = self.parent.r0
+                    self.parent.r0 = position
+                self.parent.uncompressed_data = extend_data(self.parent.uncompressed_data, length, position)
+                current_decoded_size += length
+        elif self.block_type == 3: # Uncompressed block
+            padding = paddingSize(self.address + self.current_size, 16)
+            if padding:
+                yield PaddingBits(self, "padding[]", padding)
+            else:
+                yield PaddingBits(self, "padding[]", 16)
+            self.endian = LITTLE_ENDIAN
+            yield UInt32(self, "r[]", "New value of R0")
+            yield UInt32(self, "r[]", "New value of R1")
+            yield UInt32(self, "r[]", "New value of R2")
+            self.parent.r0 = self["r[0]"].value
+            self.parent.r1 = self["r[1]"].value
+            self.parent.r2 = self["r[2]"].value
+            yield RawBytes(self, "data", self.uncompressed_size)
+            self.parent.uncompressed_data+=self["data"].value
+            if self["block_size"].value % 2:
+                yield PaddingBits(self, "padding", 8)
+        else:
+            raise ParserError("Unknown block type %d!"%self.block_type)
+
+class LZXStream(Parser):
+    endian = MIDDLE_ENDIAN
+    def createFields(self):
+        self.uncompressed_data = ""
+        self.r0 = 1
+        self.r1 = 1
+        self.r2 = 1
+        yield Bit(self, "filesize_indicator")
+        if self["filesize_indicator"].value:
+            yield UInt32(self, "filesize")
+        while self.current_size < self.size:
+            block = LZXBlock(self, "block[]")
+            yield block
+            if self.size - self.current_size < 16:
+                padding = paddingSize(self.address + self.current_size, 16)
+                if padding:
+                    yield PaddingBits(self, "padding[]", padding)
+                break
+
+def lzx_decompress(stream, window_bits):
+    data = LZXStream(stream)
+    data.compr_level = window_bits
+    for unused in data:
+        pass
+    return data.uncompressed_data
diff --git a/lib/hachoir_parser/archive/mozilla_ar.py b/lib/hachoir_parser/archive/mozilla_ar.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b18f434948ea18ba6e3d7714c8001d0038966ef
--- /dev/null
+++ b/lib/hachoir_parser/archive/mozilla_ar.py
@@ -0,0 +1,60 @@
+"""MAR (Mozilla ARchive) parser
+
+Author: Robert Xiao
+Creation date: July 10, 2007
+
+"""
+
+from hachoir_core.endian import BIG_ENDIAN
+from hachoir_core.field import (RootSeekableFieldSet, FieldSet,
+    String, CString, UInt32, RawBytes)
+from hachoir_core.text_handler import displayHandler, filesizeHandler
+from hachoir_core.tools import humanUnixAttributes
+from hachoir_parser import HachoirParser
+
+class IndexEntry(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "offset", "Offset in bytes relative to start of archive")
+        yield filesizeHandler(UInt32(self, "length", "Length in bytes"))
+        yield displayHandler(UInt32(self, "flags"), humanUnixAttributes)
+        yield CString(self, "name", "Filename (byte array)")
+
+    def createDescription(self):
+        return 'File %s, Size %s, Mode %s'%(
+            self["name"].display, self["length"].display, self["flags"].display)
+
+class MozillaArchive(HachoirParser, RootSeekableFieldSet):
+    MAGIC = "MAR1"
+    PARSER_TAGS = {
+        "id": "mozilla_ar",
+        "category": "archive",
+        "file_ext": ("mar",),
+        "min_size": (8+4+13)*8,  # Header, Index Header, 1 Index Entry
+        "magic": ((MAGIC, 0),),
+        "description": "Mozilla Archive",
+    }
+    endian = BIG_ENDIAN
+    
+    def __init__(self, stream, **args):
+        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
+        HachoirParser.__init__(self, stream, **args)
+        
+    def validate(self):
+        if self.stream.readBytes(0, 4) != self.MAGIC:
+            return "Invalid magic"
+        return True
+
+    def createFields(self):
+        yield String(self, "magic", 4, "File signature (MAR1)", charset="ASCII")
+        yield UInt32(self, "index_offset", "Offset to index relative to file start")
+        self.seekByte(self["index_offset"].value, False)
+        yield UInt32(self, "index_size", "size of index in bytes")
+        current_index_size = 0 # bytes
+        while current_index_size < self["index_size"].value:
+            # plus 4 compensates for index_size
+            self.seekByte(self["index_offset"].value + current_index_size + 4, False)
+            entry = IndexEntry(self, "index_entry[]")
+            yield entry
+            current_index_size += entry.size // 8
+            self.seekByte(entry["offset"].value, False)
+            yield RawBytes(self, "file[]", entry["length"].value)
diff --git a/lib/hachoir_parser/archive/zip.py b/lib/hachoir_parser/archive/zip.py
index b3cd54a59965c2d6092075077f8c6ee298fa91fc..8271ac93cece32562c15a62111e9d9be9d298868 100644
--- a/lib/hachoir_parser/archive/zip.py
+++ b/lib/hachoir_parser/archive/zip.py
@@ -80,16 +80,7 @@ class ZipGeneralFlags(FieldSet):
         # Need the compression info from the parent, and that is the byte following
         method = self.stream.readBits(self.absolute_address+16, 16, LITTLE_ENDIAN)
 
-        yield Bits(self, "unused[]", 2, "Unused")
-        yield Bit(self, "encrypted_central_dir", "Selected data values in the Local Header are masked")
-        yield Bit(self, "incomplete", "Reserved by PKWARE for enhanced compression.")
-        yield Bit(self, "uses_unicode", "Filename and comments are in UTF-8")
-        yield Bits(self, "unused[]", 4, "Unused")
-        yield Bit(self, "strong_encrypt", "Strong encryption (version >= 50)")
-        yield Bit(self, "is_patched", "File is compressed with patched data?")
-        yield Bit(self, "enhanced_deflate", "Reserved for use with method 8")
-        yield Bit(self, "has_descriptor",
-                  "Compressed data followed by descriptor?")
+        yield Bit(self, "is_encrypted", "File is encrypted?")
         if method == 6:
             yield Bit(self, "use_8k_sliding", "Use 8K sliding dictionary (instead of 4K)")
             yield Bit(self, "use_3shannon", "Use a 3 Shannon-Fano tree (instead of 2 Shannon-Fano)")
@@ -106,7 +97,16 @@ class ZipGeneralFlags(FieldSet):
             yield Bit(self, "unused[]")
         else:
             yield Bits(self, "compression_info", 2)
-        yield Bit(self, "is_encrypted", "File is encrypted?")
+        yield Bit(self, "has_descriptor",
+                  "Compressed data followed by descriptor?")
+        yield Bit(self, "enhanced_deflate", "Reserved for use with method 8")
+        yield Bit(self, "is_patched", "File is compressed with patched data?")
+        yield Bit(self, "strong_encrypt", "Strong encryption (version >= 50)")
+        yield Bits(self, "unused[]", 4, "Unused")
+        yield Bit(self, "uses_unicode", "Filename and comments are in UTF-8")
+        yield Bit(self, "incomplete", "Reserved by PKWARE for enhanced compression.")
+        yield Bit(self, "encrypted_central_dir", "Selected data values in the Local Header are masked")
+        yield Bits(self, "unused[]", 2, "Unused")
 
 class ExtraField(FieldSet):
     EXTRA_FIELD_ID = {
@@ -141,7 +141,12 @@ class ExtraField(FieldSet):
         size = UInt16(self, "field_data_size", "Extra field data size")
         yield size
         if size.value > 0:
-            yield RawBytes(self, "field_data", size, "Unknown field data")
+            yield RawBytes(self, "field_data", size.value, "Unknown field data")
+
+class ExtraFields(FieldSet):
+    def createFields(self):
+        while self.current_size < self.size:
+            yield ExtraField(self, "extra[]")
 
 def ZipStartCommonFields(self):
     yield ZipVersion(self, "version_needed", "Version needed")
@@ -179,8 +184,8 @@ class ZipCentralDirectory(FieldSet):
         yield String(self, "filename", self["filename_length"].value,
                      "Filename", charset=charset)
         if 0 < self["extra_length"].value:
-            yield RawBytes(self, "extra", self["extra_length"].value,
-                           "Extra fields")
+            yield ExtraFields(self, "extra", size=self["extra_length"].value*8,
+                           description="Extra fields")
         if 0 < self["comment_length"].value:
             yield String(self, "comment", self["comment_length"].value,
                          "Comment", charset=charset)
@@ -278,14 +283,15 @@ class FileEntry(FieldSet):
             yield filename
             self.filename = filename.value
         if self["extra_length"].value:
-            yield RawBytes(self, "extra", self["extra_length"].value, "Extra")
+            yield ExtraFields(self, "extra", size=self["extra_length"].value*8,
+                           description="Extra fields")
         size = self["compressed_size"].value
         if size > 0:
             yield self.data(size)
         elif self["flags/incomplete"].value:
             for field in self.resync():
                 yield field
-        if self["flags/has_descriptor"].value:
+        if self["flags/has_descriptor"].value and self['crc32'].value == 0:
             yield ZipDataDescriptor(self, "data_desc", "Data descriptor")
 
     def createDescription(self):
diff --git a/lib/hachoir_parser/archive/zlib.py b/lib/hachoir_parser/archive/zlib.py
new file mode 100644
index 0000000000000000000000000000000000000000..bde94b1da4a5de99927254af132eaee8756cdd38
--- /dev/null
+++ b/lib/hachoir_parser/archive/zlib.py
@@ -0,0 +1,301 @@
+"""Detailed ZLIB parser
+
+Author: Robert Xiao
+Creation date: July 9 2007
+
+"""
+
+from hachoir_parser import Parser
+from hachoir_core.field import (Bit, Bits, Field, Int16, UInt32,
+    Enum, FieldSet, GenericFieldSet,
+    PaddingBits, ParserError, RawBytes)
+from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_core.text_handler import textHandler, hexadecimal
+from hachoir_core.tools import paddingSize, alignValue
+
+def extend_data(data, length, offset):
+    """Extend data using a length and an offset."""
+    if length >= offset:
+        new_data = data[-offset:] * (alignValue(length, offset) // offset)
+        return data + new_data[:length]
+    else:
+        return data + data[-offset:-offset+length]
+
+def build_tree(lengths):
+    """Build a Huffman tree from a list of lengths.
+       The ith entry of the input list is the length of the Huffman code corresponding to
+       integer i, or 0 if the integer i is unused."""
+    max_length = max(lengths) + 1
+    bit_counts = [0]*max_length
+    next_code = [0]*max_length
+    tree = {}
+    for i in lengths:
+        if i:
+            bit_counts[i] += 1
+    code = 0
+    for i in xrange(1, len(bit_counts)):
+        next_code[i] = code = (code + bit_counts[i-1]) << 1
+    for i, ln in enumerate(lengths):
+        if ln:
+            tree[(ln, next_code[ln])] = i
+            next_code[ln] += 1
+    return tree
+
+class HuffmanCode(Field):
+    """Huffman code. Uses tree parameter as the Huffman tree."""
+    def __init__(self, parent, name, tree, description=None):
+        Field.__init__(self, parent, name, 0, description)
+
+        endian = self.parent.endian
+        stream = self.parent.stream
+        addr = self.absolute_address
+
+        value = 0
+        while (self.size, value) not in tree:
+            if self.size > 256:
+                raise ParserError("Huffman code too long!")
+            bit = stream.readBits(addr, 1, endian)
+            value <<= 1
+            value += bit
+            self._size += 1
+            addr += 1
+        self.huffvalue = value
+        self.realvalue = tree[(self.size, value)]
+    def createValue(self):
+        return self.huffvalue
+
+class DeflateBlock(FieldSet):
+    # code: (min, max, extrabits)
+    LENGTH_SYMBOLS = {257:(3,3,0),
+                      258:(4,4,0),
+                      259:(5,5,0),
+                      260:(6,6,0),
+                      261:(7,7,0),
+                      262:(8,8,0),
+                      263:(9,9,0),
+                      264:(10,10,0),
+                      265:(11,12,1),
+                      266:(13,14,1),
+                      267:(15,16,1),
+                      268:(17,18,1),
+                      269:(19,22,2),
+                      270:(23,26,2),
+                      271:(27,30,2),
+                      272:(31,34,2),
+                      273:(35,42,3),
+                      274:(43,50,3),
+                      275:(51,58,3),
+                      276:(59,66,3),
+                      277:(67,82,4),
+                      278:(83,98,4),
+                      279:(99,114,4),
+                      280:(115,130,4),
+                      281:(131,162,5),
+                      282:(163,194,5),
+                      283:(195,226,5),
+                      284:(227,257,5),
+                      285:(258,258,0)
+                      }
+    DISTANCE_SYMBOLS = {0:(1,1,0),
+                        1:(2,2,0),
+                        2:(3,3,0),
+                        3:(4,4,0),
+                        4:(5,6,1),
+                        5:(7,8,1),
+                        6:(9,12,2),
+                        7:(13,16,2),
+                        8:(17,24,3),
+                        9:(25,32,3),
+                        10:(33,48,4),
+                        11:(49,64,4),
+                        12:(65,96,5),
+                        13:(97,128,5),
+                        14:(129,192,6),
+                        15:(193,256,6),
+                        16:(257,384,7),
+                        17:(385,512,7),
+                        18:(513,768,8),
+                        19:(769,1024,8),
+                        20:(1025,1536,9),
+                        21:(1537,2048,9),
+                        22:(2049,3072,10),
+                        23:(3073,4096,10),
+                        24:(4097,6144,11),
+                        25:(6145,8192,11),
+                        26:(8193,12288,12),
+                        27:(12289,16384,12),
+                        28:(16385,24576,13),
+                        29:(24577,32768,13),
+                        }
+    CODE_LENGTH_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]
+    def __init__(self, parent, name, uncomp_data="", *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.uncomp_data = uncomp_data
+        
+    def createFields(self):
+        yield Bit(self, "final", "Is this the final block?") # BFINAL
+        yield Enum(Bits(self, "compression_type", 2), # BTYPE
+                   {0:"None", 1:"Fixed Huffman", 2:"Dynamic Huffman", 3:"Reserved"})
+        if self["compression_type"].value == 0: # no compression
+            padding = paddingSize(self.current_size + self.absolute_address, 8) # align on byte boundary
+            if padding:
+                yield PaddingBits(self, "padding[]", padding)
+            yield Int16(self, "len")
+            yield Int16(self, "nlen", "One's complement of len")
+            if self["len"].value != ~self["nlen"].value:
+                raise ParserError("len must be equal to the one's complement of nlen!")
+            if self["len"].value: # null stored blocks produced by some encoders (e.g. PIL)
+                yield RawBytes(self, "data", self["len"].value, "Uncompressed data")
+            return
+        elif self["compression_type"].value == 1: # Fixed Huffman
+            length_tree = {} # (size, huffman code): value
+            distance_tree = {}
+            for i in xrange(144):
+                length_tree[(8, i+48)] = i
+            for i in xrange(144, 256):
+                length_tree[(9, i+256)] = i
+            for i in xrange(256, 280):
+                length_tree[(7, i-256)] = i
+            for i in xrange(280, 288):
+                length_tree[(8, i-88)] = i
+            for i in xrange(32):
+                distance_tree[(5, i)] = i
+        elif self["compression_type"].value == 2: # Dynamic Huffman
+            yield Bits(self, "huff_num_length_codes", 5, "Number of Literal/Length Codes, minus 257")
+            yield Bits(self, "huff_num_distance_codes", 5, "Number of Distance Codes, minus 1")
+            yield Bits(self, "huff_num_code_length_codes", 4, "Number of Code Length Codes, minus 4")
+            code_length_code_lengths = [0]*19 # confusing variable name...
+            for i in self.CODE_LENGTH_ORDER[:self["huff_num_code_length_codes"].value+4]:
+                field = Bits(self, "huff_code_length_code[%i]" % i, 3, "Code lengths for the code length alphabet")
+                yield field
+                code_length_code_lengths[i] = field.value
+            code_length_tree = build_tree(code_length_code_lengths)
+            length_code_lengths = []
+            distance_code_lengths = []
+            for numcodes, name, lengths in (
+                (self["huff_num_length_codes"].value + 257, "length", length_code_lengths),
+                (self["huff_num_distance_codes"].value + 1, "distance", distance_code_lengths)):
+                while len(lengths) < numcodes:
+                    field = HuffmanCode(self, "huff_%s_code[]" % name, code_length_tree)
+                    value = field.realvalue
+                    if value < 16:
+                        prev_value = value
+                        field._description = "Literal Code Length %i (Huffman Code %i)" % (value, field.value)
+                        yield field
+                        lengths.append(value)
+                    else:
+                        info = {16: (3,6,2),
+                                17: (3,10,3),
+                                18: (11,138,7)}[value]
+                        if value == 16:
+                            repvalue = prev_value
+                        else:
+                            repvalue = 0
+                        field._description = "Repeat Code %i, Repeating value (%i) %i to %i times (Huffman Code %i)" % (value, repvalue, info[0], info[1], field.value)
+                        yield field
+                        extrafield = Bits(self, "huff_%s_code_extra[%s" % (name, field.name.split('[')[1]), info[2])
+                        num_repeats = extrafield.value+info[0]
+                        extrafield._description = "Repeat Extra Bits (%i), total repeats %i"%(extrafield.value, num_repeats)
+                        yield extrafield
+                        lengths += [repvalue]*num_repeats
+            length_tree = build_tree(length_code_lengths)
+            distance_tree = build_tree(distance_code_lengths)
+        else:
+            raise ParserError("Unsupported compression type 3!")
+        while True:
+            field = HuffmanCode(self, "length_code[]", length_tree)
+            value = field.realvalue
+            if value < 256:
+                field._description = "Literal Code %r (Huffman Code %i)" % (chr(value), field.value)
+                yield field
+                self.uncomp_data += chr(value)
+            if value == 256:
+                field._description = "Block Terminator Code (256) (Huffman Code %i)" % field.value
+                yield field
+                break
+            elif value > 256:
+                info = self.LENGTH_SYMBOLS[value]
+                if info[2] == 0:
+                    field._description = "Length Code %i, Value %i (Huffman Code %i)" % (value, info[0], field.value)
+                    length = info[0]
+                    yield field
+                else:
+                    field._description = "Length Code %i, Values %i to %i (Huffman Code %i)" % (value, info[0], info[1], field.value)
+                    yield field
+                    extrafield = Bits(self, "length_extra[%s" % field.name.split('[')[1], info[2])
+                    length = extrafield.value + info[0]
+                    extrafield._description = "Length Extra Bits (%i), total length %i"%(extrafield.value, length)
+                    yield extrafield
+                field = HuffmanCode(self, "distance_code[]", distance_tree)
+                value = field.realvalue
+                info = self.DISTANCE_SYMBOLS[value]
+                if info[2] == 0:
+                    field._description = "Distance Code %i, Value %i (Huffman Code %i)" % (value, info[0], field.value)
+                    distance = info[0]
+                    yield field
+                else:
+                    field._description = "Distance Code %i, Values %i to %i (Huffman Code %i)" % (value, info[0], info[1], field.value)
+                    yield field
+                    extrafield = Bits(self, "distance_extra[%s" % field.name.split('[')[1], info[2])
+                    distance = extrafield.value + info[0]
+                    extrafield._description = "Distance Extra Bits (%i), total length %i"%(extrafield.value, distance)
+                    yield extrafield
+                self.uncomp_data = extend_data(self.uncomp_data, length, distance)
+
+class DeflateData(GenericFieldSet):
+    endian = LITTLE_ENDIAN
+    def createFields(self):
+        uncomp_data = ""
+        blk=DeflateBlock(self, "compressed_block[]", uncomp_data)
+        yield blk
+        uncomp_data = blk.uncomp_data
+        while not blk["final"].value:
+            blk=DeflateBlock(self, "compressed_block[]", uncomp_data)
+            yield blk
+            uncomp_data = blk.uncomp_data
+        padding = paddingSize(self.current_size + self.absolute_address, 8) # align on byte boundary
+        if padding:
+            yield PaddingBits(self, "padding[]", padding)
+        self.uncompressed_data = uncomp_data
+
+class ZlibData(Parser):
+    PARSER_TAGS = {
+        "id": "zlib",
+        "category": "archive",
+        "file_ext": ("zlib",),
+        "min_size": 8*8,
+        "description": "ZLIB Data",
+    }
+    endian = LITTLE_ENDIAN
+
+    def validate(self):
+        if self["compression_method"].value != 8:
+            return "Incorrect compression method"
+        if ((self["compression_info"].value << 12) +
+            (self["compression_method"].value << 8) +
+            (self["flag_compression_level"].value << 6) +
+            (self["flag_dictionary_present"].value << 5) +
+            (self["flag_check_bits"].value)) % 31 != 0:
+            return "Invalid flag check value"
+        return True
+
+    def createFields(self):
+        yield Enum(Bits(self, "compression_method", 4), {8:"deflate", 15:"reserved"}) # CM
+        yield Bits(self, "compression_info", 4, "base-2 log of the window size") # CINFO
+        yield Bits(self, "flag_check_bits", 5) # FCHECK
+        yield Bit(self, "flag_dictionary_present") # FDICT
+        yield Enum(Bits(self, "flag_compression_level", 2), # FLEVEL
+                   {0:"Fastest", 1:"Fast", 2:"Default", 3:"Maximum, Slowest"})
+        if self["flag_dictionary_present"].value:
+            yield textHandler(UInt32(self, "dict_checksum", "ADLER32 checksum of dictionary information"), hexadecimal)
+        yield DeflateData(self, "data", self.stream, description = "Compressed Data")
+        yield textHandler(UInt32(self, "data_checksum", "ADLER32 checksum of compressed data"), hexadecimal)
+
+def zlib_inflate(stream, wbits=None, prevdata=""):
+    if wbits is None or wbits >= 0:
+        return ZlibData(stream)["data"].uncompressed_data
+    else:
+        data = DeflateData(None, "root", stream, "", stream.askSize(None))
+        for unused in data:
+            pass
+        return data.uncompressed_data
diff --git a/lib/hachoir_parser/audio/8svx.py b/lib/hachoir_parser/audio/8svx.py
deleted file mode 100644
index 109d03dae0348e4ab3d446e2ff16d98b316aea5c..0000000000000000000000000000000000000000
--- a/lib/hachoir_parser/audio/8svx.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""
-Audio Interchange File Format (AIFF) parser.
-
-Author: Victor Stinner
-Creation: 27 december 2006
-"""
-
-from hachoir_parser import Parser
-from hachoir_core.field import (FieldSet,
-    UInt16, UInt32, Float80, TimestampMac32,
-    RawBytes, NullBytes,
-    String, Enum, PascalString32)
-from hachoir_core.endian import BIG_ENDIAN
-from hachoir_core.text_handler import filesizeHandler
-from hachoir_core.tools import alignValue
-from hachoir_parser.audio.id3 import ID3v2
-
-CODEC_NAME = {
-    'ACE2': u"ACE 2-to-1",
-    'ACE8': u"ACE 8-to-3",
-    'MAC3': u"MAC 3-to-1",
-    'MAC6': u"MAC 6-to-1",
-    'NONE': u"None",
-    'sowt': u"Little-endian, no compression",
-}
-
-class Comment(FieldSet):
-    def createFields(self):
-        yield TimestampMac32(self, "timestamp")
-        yield PascalString32(self, "text")
-
-def parseText(self):
-    yield String(self, "text", self["size"].value)
-
-def parseID3(self):
-    yield ID3v2(self, "id3v2", size=self["size"].value*8)
-
-def parseComment(self):
-    yield UInt16(self, "nb_comment")
-    for index in xrange(self["nb_comment"].value):
-        yield Comment(self, "comment[]")
-
-def parseCommon(self):
-    yield UInt16(self, "nb_channel")
-    yield UInt32(self, "nb_sample")
-    yield UInt16(self, "sample_size")
-    yield Float80(self, "sample_rate")
-    yield Enum(String(self, "codec", 4, strip="\0", charset="ASCII"), CODEC_NAME)
-
-def parseVersion(self):
-    yield TimestampMac32(self, "timestamp")
-
-def parseSound(self):
-    yield UInt32(self, "offset")
-    yield UInt32(self, "block_size")
-    size = (self.size - self.current_size) // 8
-    if size:
-        yield RawBytes(self, "data", size)
-
-class Chunk(FieldSet):
-    TAG_INFO = {
-        'COMM': ('common', "Common chunk", parseCommon),
-        'COMT': ('comment', "Comment", parseComment),
-        'NAME': ('name', "Name", parseText),
-        'AUTH': ('author', "Author", parseText),
-        'FVER': ('version', "Version", parseVersion),
-        'SSND': ('sound', "Sound data", parseSound),
-        'ID3 ': ('id3', "ID3", parseID3),
-    }
-
-    def __init__(self, *args):
-        FieldSet.__init__(self, *args)
-        self._size = (8 + alignValue(self["size"].value, 2)) * 8
-        tag = self["type"].value
-        if tag in self.TAG_INFO:
-            self._name, self._description, self._parser = self.TAG_INFO[tag]
-        else:
-            self._parser = None
-
-    def createFields(self):
-        yield String(self, "type", 4, "Signature (FORM)", charset="ASCII")
-        yield filesizeHandler(UInt32(self, "size"))
-        size = self["size"].value
-        if size:
-            if self._parser:
-                for field in self._parser(self):
-                    yield field
-                if size % 2:
-                    yield NullBytes(self, "padding", 1)
-            else:
-                yield RawBytes(self, "data", size)
-
-class HeightSVX(Parser):
-    PARSER_TAGS = {
-        "id": "8svx",
-        "category": "audio",
-        "file_ext": ("8svx",),
-        "mime": (u"audio/x-aiff",),
-        "min_size": 12*8,
-        "description": "8SVX (audio) format"
-    }
-    endian = BIG_ENDIAN
-
-    def validate(self):
-        if self.stream.readBytes(0, 4) != "FORM":
-            return "Invalid signature"
-        if self.stream.readBytes(8*8, 4) != "8SVX":
-            return "Invalid type"
-        return True
-
-    def createFields(self):
-        yield String(self, "signature", 4, "Signature (FORM)", charset="ASCII")
-        yield filesizeHandler(UInt32(self, "filesize"))
-        yield String(self, "type", 4, "Form type (AIFF or AIFC)", charset="ASCII")
-        while not self.eof:
-            yield Chunk(self, "chunk[]")
-
-    def createDescription(self):
-        if self["type"].value == "AIFC":
-            return "Audio Interchange File Format Compressed (AIFC)"
-        else:
-            return "Audio Interchange File Format (AIFF)"
-
-    def createContentSize(self):
-        return self["filesize"].value * 8
-
diff --git a/lib/hachoir_parser/audio/itunesdb.py b/lib/hachoir_parser/audio/itunesdb.py
index 3472d2d85738a2221097d54db88443b14b301bb1..a70d9cb0008b02eb46cecdb3b03b56eb5cc37d8b 100644
--- a/lib/hachoir_parser/audio/itunesdb.py
+++ b/lib/hachoir_parser/audio/itunesdb.py
@@ -10,8 +10,8 @@ Creation date: 19 august 2006
 
 from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet,
-    UInt8, UInt16, UInt32, UInt64, TimestampMac32,
-    String, Float32, NullBytes, Enum)
+    UInt8, UInt16, UInt32, Int32, UInt64, TimestampMac32,
+    String, Float32, NullBytes, Enum, RawBytes)
 from hachoir_core.endian import LITTLE_ENDIAN
 from hachoir_core.tools import humanDuration
 from hachoir_core.text_handler import displayHandler, filesizeHandler
@@ -75,6 +75,9 @@ class DataObject(FieldSet):
         51:"Smart Playlist Rules",
         52:"Library Playlist Index",
         100:"Column info",
+        200:"Album name (for album descriptions)",
+        201:"Album artist (for album descriptions)",
+        202:"Album sort artist (for album descriptions)"
     }
 
     mhod52_sort_index_type_name={
@@ -94,7 +97,7 @@ class DataObject(FieldSet):
         yield UInt32(self, "header_length", "Header Length")
         yield UInt32(self, "entry_length", "Entry Length")
         yield Enum(UInt32(self, "type", "type"),self.type_name)
-        if(self["type"].value<15):
+        if(self["type"].value<15) or (self["type"].value >= 200):
             yield UInt32(self, "unknown[]")
             yield UInt32(self, "unknown[]")
             yield UInt32(self, "position", "Position")
@@ -162,7 +165,7 @@ class TrackItem(FieldSet):
         yield Enum(UInt8(self, "x2_type", "Extended type 2"),self.x2_type_name)
         yield UInt8(self, "compilation_flag", "Compilation Flag")
         yield UInt8(self, "rating", "Rating")
-        yield TimestampMac32(self, "added_date", "Date when the item was added")
+        yield TimestampMac32(self, "last_modified", "Time of the last modification of the track")
         yield filesizeHandler(UInt32(self, "size", "Track size in bytes"))
         yield displayHandler(UInt32(self, "length", "Track length in milliseconds"), humanDuration)
         yield UInt32(self, "track_number", "Number of this track")
@@ -180,23 +183,24 @@ class TrackItem(FieldSet):
         yield UInt32(self, "disc_number", "disc number in multi disc sets")
         yield UInt32(self, "total_discs", "Total number of discs in the disc set")
         yield UInt32(self, "userid", "User ID in the DRM scheme")
-        yield TimestampMac32(self, "last_modified", "Time of the last modification of the track")
+        yield TimestampMac32(self, "added_date", "Date when the item was added")
         yield UInt32(self, "bookmark_time", "Bookmark time for AudioBook")
         yield UInt64(self, "dbid", "Unique DataBase ID for the song (identical in mhit and in mhii)")
         yield UInt8(self, "checked", "song is checked")
         yield UInt8(self, "application_rating", "Last Rating before change")
         yield UInt16(self, "BPM", "BPM of the track")
-        yield UInt16(self, "artwork_count", "number of artworks fo this item")
+        yield UInt16(self, "artwork_count", "number of artworks for this item")
         yield UInt16(self, "unknown[]")
         yield UInt32(self, "artwork_size", "Total size of artworks in bytes")
         yield UInt32(self, "unknown[]")
         yield Float32(self, "sample_rate_2", "Sample Rate express in float")
         yield UInt32(self, "released_date", "Date of release in Music Store or in Podcast")
+        yield UInt16(self, "unknown[]")
+        yield UInt16(self, "explicit_flag[]", "Explicit flag")
         yield UInt32(self, "unknown[]")
         yield UInt32(self, "unknown[]")
-        yield UInt32(self, "unknown[]")
-        yield UInt32(self, "unknown[]")
-        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "skip_count[]", "Skip Count")
+        yield TimestampMac32(self, "last_skipped", "Date when the item was last skipped")
         yield UInt8(self, "has_artwork", "0x01 for track with artwork, 0x02 otherwise")
         yield UInt8(self, "skip_wen_shuffling", "Skip that track when shuffling")
         yield UInt8(self, "remember_playback_position", "Remember playback position")
@@ -207,11 +211,10 @@ class TrackItem(FieldSet):
         yield UInt8(self, "played_mark", "Track has been played")
         yield UInt8(self, "unknown[]")
         yield UInt32(self, "unknown[]")
+        yield UInt32(self, "pregap[]", "Number of samples of silence before the song starts")
+        yield UInt64(self, "sample_count", "Number of samples in the song (only for WAV and AAC files)")
         yield UInt32(self, "unknown[]")
-        yield UInt32(self, "sample_count", "Number of samples in the song (only for WAV and AAC files)")
-        yield UInt32(self, "unknown[]")
-        yield UInt32(self, "unknown[]")
-        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "postgap[]", "Number of samples of silence at the end of the song")
         yield UInt32(self, "unknown[]")
         yield Enum(UInt32(self, "media_type", "Media Type for video iPod"),self.media_type_name)
         yield UInt32(self, "season_number", "Season Number")
@@ -222,6 +225,20 @@ class TrackItem(FieldSet):
         yield UInt32(self, "unknown[]")
         yield UInt32(self, "unknown[]")
         yield UInt32(self, "unknown[]")
+        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "gapless_data[]","The size in bytes from first Sync Frame until the 8th before the last frame." )
+        yield UInt32(self, "unknown[]")
+        yield UInt16(self, "gaplessTrackFlag[]", "1 if track has gapless data")
+        yield UInt16(self, "gaplessAlbumFlag[]", "1 if track uses crossfading in iTunes")
+        yield RawBytes(self, "unknown[]", 20)
+        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "unknown[]")
+        yield UInt16(self, "unknown[]")
+        yield UInt16(self, "album_id[]", "Album ID (used to link tracks with MHIAs)")
+        yield RawBytes(self, "unknown[]", 52)
+        yield UInt32(self, "mhii_link[]", "Artwork ID (used to link tracks with MHIIs)")
         padding = self.seekByte(self["header_length"].value, "header padding")
         if padding:
             yield padding
@@ -319,7 +336,7 @@ class Playlist(FieldSet):
         self._size = self["entry_length"].value *8
 
     def createFields(self):
-        yield String(self, "header_id", 4, "Playlist List Header Markup (\"mhyp\")", charset="ISO-8859-1")
+        yield String(self, "header_id", 4, "Playlist Header Markup (\"mhyp\")", charset="ISO-8859-1")
         yield UInt32(self, "header_length", "Header Length")
         yield UInt32(self, "entry_length", "Entry Length")
         yield UInt32(self, "data_object_child_count", "Number of Child Data Objects")
@@ -360,11 +377,48 @@ class PlaylistList(FieldSet):
         for i in xrange(self["playlist_number"].value):
             yield Playlist(self, "playlist[]")
 
+class Album(FieldSet):
+    def __init__(self, *args, **kw):
+        FieldSet.__init__(self, *args, **kw)
+        self._size = self["entry_length"].value *8
+
+    def createFields(self):
+        yield String(self, "header_id", 4, "Album Item Header Markup (\"mhia\")", charset="ISO-8859-1")
+        yield UInt32(self, "header_length", "Header Length")
+        yield UInt32(self, "entry_length", "Entry Length")
+        yield UInt32(self, "data_object_child_count", "Number of Child Data Objects")
+        yield UInt16(self, "unknow[]")
+        yield UInt16(self, "album_id[]", "Album ID")
+        yield UInt32(self, "unknow[]")
+        yield UInt32(self, "unknow[]")
+        yield UInt32(self, "unknow[]")
+
+        padding = self.seekByte(self["header_length"].value, "entry padding")
+        if padding:
+            yield padding
+
+        for i in xrange(self["data_object_child_count"].value):
+            yield DataObject(self, "mhod[]")
+
+class AlbumList(FieldSet):
+    def createFields(self):
+        yield String(self, "header_id", 4, "Album List Header Markup (\"mhla\")", charset="ISO-8859-1")
+        yield UInt32(self, "header_length", "Header Length")
+        yield UInt32(self, "album_number", "Number of Albums")
+
+        padding = self.seekByte(self["header_length"].value, "header padding")
+        if padding:
+            yield padding
+
+        for i in xrange(self["album_number"].value):
+            yield Album(self, "album[]")
+
 class DataSet(FieldSet):
     type_name={
         1:"Track List",
         2:"Play List",
-        3:"Podcast List"
+        3:"Podcast List",
+        4:"Album List"
         }
     def __init__(self, *args, **kw):
         FieldSet.__init__(self, *args, **kw)
@@ -384,6 +438,8 @@ class DataSet(FieldSet):
             yield PlaylistList(self, "playlist_list[]");
         if self["type"].value == 3:
             yield PlaylistList(self, "podcast_list[]");
+        if self["type"].value == 4:
+            yield AlbumList(self, "album_list[]");
         padding = self.seekBit(self._size, "entry padding")
         if padding:
             yield padding
@@ -417,8 +473,20 @@ class ITunesDBFile(Parser):
         yield UInt32(self, "version_number", "Version Number")
         yield UInt32(self, "child_number", "Number of Children")
         yield UInt64(self, "id", "ID for this database")
+        yield UInt16(self, "unknown[]")
+        yield UInt32(self, "unknown[]")
+        yield UInt64(self, "unknown[]")
+        yield UInt16(self, "unknown[]")
+        yield UInt16(self, "hashing_scheme[]", "Algorithm used to calculate the database hash")
+        yield NullBytes(self, "unknown[]", 20)
+        yield String(self, "language_id", 2, "Language ID")
+        yield UInt64(self, "persistent_id", "Library Persistent ID")
         yield UInt32(self, "unknown[]")
-        yield UInt64(self, "initial_dbid", "Initial DBID")
+        yield UInt32(self, "unknown[]")
+        yield RawBytes(self, "hash[]", 20)
+        yield Int32(self, "timezone_offset[]", "Timezone offset in seconds")
+        yield UInt16(self, "unknown[]")
+        yield RawBytes(self, "iphone_hash[]", 45)
         size = self["header_length"].value-self.current_size/ 8
         if size>0:
             yield NullBytes(self, "padding", size)
diff --git a/lib/hachoir_parser/audio/midi.py b/lib/hachoir_parser/audio/midi.py
index 01da22893f39ddcfe3dce8a95cecd422907aa7b5..211e7b78830e8b26fc40248cdff7d12b535ced58 100644
--- a/lib/hachoir_parser/audio/midi.py
+++ b/lib/hachoir_parser/audio/midi.py
@@ -10,7 +10,7 @@ Creation: 27 december 2006
 
 from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, Bits, ParserError,
-    String, UInt32, UInt24, UInt16, UInt8, Enum, RawBytes)
+    String, UInt32, UInt24, UInt16, UInt8, Enum, RawBits, RawBytes)
 from hachoir_core.endian import BIG_ENDIAN
 from hachoir_core.text_handler import textHandler, hexadecimal
 from hachoir_core.tools import createDict, humanDurationNanosec
@@ -46,7 +46,7 @@ def parseControl(parser):
 def parsePatch(parser):
     yield UInt8(parser, "program", "New program number")
 
-def parseChannel(parser):
+def parseChannel(parser, size=1):
     yield UInt8(parser, "channel", "Channel number")
 
 def parsePitch(parser):
@@ -56,6 +56,16 @@ def parsePitch(parser):
 def parseText(parser, size):
     yield String(parser, "text", size)
 
+def parseSMPTEOffset(parser, size):
+    yield RawBits(parser, "padding", 1)
+    yield Enum(Bits(parser, "frame_rate", 2),
+        {0:"24 fps", 1:"25 fps", 2:"30 fps (drop frame)", 3:"30 fps"})
+    yield Bits(parser, "hour", 5)
+    yield UInt8(parser, "minute")
+    yield UInt8(parser, "second")
+    yield UInt8(parser, "frame")
+    yield UInt8(parser, "subframe", "100 subframes per frame")
+
 def formatTempo(field):
     return humanDurationNanosec(field.value*1000)
 
@@ -92,8 +102,10 @@ class Command(FieldSet):
         0x05: ("Lyric", parseText),
         0x06: ("Marker", parseText),
         0x07: ("Cue point", parseText),
+        0x20: ("MIDI Channel Prefix", parseChannel),
         0x2F: ("End of the track", None),
         0x51: ("Set tempo", parseTempo),
+        0x54: ("SMPTE offset", parseSMPTEOffset),
         0x58: ("Time Signature", parseTimeSignature),
         0x59: ("Key signature", None),
         0x7F: ("Sequencer specific information", None),
@@ -101,11 +113,27 @@ class Command(FieldSet):
     META_COMMAND_DESC = createDict(META_COMMAND, 0)
     META_COMMAND_PARSER = createDict(META_COMMAND, 1)
 
+    def __init__(self, *args, **kwargs):
+        if 'prev_command' in kwargs:
+            self.prev_command = kwargs['prev_command']
+            del kwargs['prev_command']
+        else:
+            self.prev_command = None
+        self.command = None
+        FieldSet.__init__(self, *args, **kwargs)
+
     def createFields(self):
         yield Integer(self, "time", "Delta time in ticks")
-        yield Enum(textHandler(UInt8(self, "command"), hexadecimal), self.COMMAND_DESC)
-        command = self["command"].value
-        if command == 0xFF:
+        next = self.stream.readBits(self.absolute_address+self.current_size, 8, self.root.endian)
+        if next & 0x80 == 0:
+            # "Running Status" command
+            if self.prev_command is None:
+                raise ParserError("Running Status command not preceded by another command.")
+            self.command = self.prev_command.command
+        else:
+            yield Enum(textHandler(UInt8(self, "command"), hexadecimal), self.COMMAND_DESC)
+            self.command = self["command"].value
+        if self.command == 0xFF:
             yield Enum(textHandler(UInt8(self, "meta_command"), hexadecimal), self.META_COMMAND_DESC)
             yield UInt8(self, "data_len")
             size = self["data_len"].value
@@ -121,9 +149,9 @@ class Command(FieldSet):
                 else:
                     yield RawBytes(self, "data", size)
         else:
-            if command not in self.COMMAND_PARSER:
+            if self.command not in self.COMMAND_PARSER:
                 raise ParserError("Unknown command: %s" % self["command"].display)
-            parser = self.COMMAND_PARSER[command]
+            parser = self.COMMAND_PARSER[self.command]
             for field in parser(self):
                 yield field
 
@@ -131,7 +159,7 @@ class Command(FieldSet):
         if "meta_command" in self:
             return self["meta_command"].display
         else:
-            return self["command"].display
+            return self.COMMAND_DESC[self.command]
 
 class Track(FieldSet):
     def __init__(self, *args):
@@ -141,9 +169,11 @@ class Track(FieldSet):
     def createFields(self):
         yield String(self, "marker", 4, "Track marker (MTrk)", charset="ASCII")
         yield UInt32(self, "size")
+        cur = None
         if True:
             while not self.eof:
-                yield Command(self, "command[]")
+                cur = Command(self, "command[]", prev_command=cur)
+                yield cur
         else:
             size = self["size"].value
             if size:
diff --git a/lib/hachoir_parser/audio/s3m.py b/lib/hachoir_parser/audio/s3m.py
index 1b2a73260fed885603e460dcfab8329bd0ef6cf9..a3e28579c23e564296b18fc2295d159703bd0994 100644
--- a/lib/hachoir_parser/audio/s3m.py
+++ b/lib/hachoir_parser/audio/s3m.py
@@ -326,7 +326,7 @@ class PTMHeader(Header):
     # static_size should prime over _size, right?
     static_size = 8*608
 
-    def getTrackerVersion(val):
+    def getTrackerVersion(self, val):
         val = val.value
         return "ProTracker x%04X" % val
 
diff --git a/lib/hachoir_parser/common/deflate.py b/lib/hachoir_parser/common/deflate.py
index df9c2e0a021d8efb87d4013eca07ce4baad07729..8aa8e51a78be7b2d45033556fea52343c1db547e 100644
--- a/lib/hachoir_parser/common/deflate.py
+++ b/lib/hachoir_parser/common/deflate.py
@@ -12,8 +12,8 @@ try:
 
         def __call__(self, size, data=None):
             if data is None:
-                data = self.gzip.unconsumed_tail
-            return self.gzip.decompress(data, size)
+                data = ''
+            return self.gzip.decompress(self.gzip.unconsumed_tail+data, size)
 
     class DeflateStreamWbits(DeflateStream):
         def __init__(self, stream):
diff --git a/lib/hachoir_parser/common/win32.py b/lib/hachoir_parser/common/win32.py
index 177190ec0d9f3f73d307c3fbcdabbc8ee829672b..f5adf4fddddc1829269d8fcf1fb49953deb1bd10 100644
--- a/lib/hachoir_parser/common/win32.py
+++ b/lib/hachoir_parser/common/win32.py
@@ -24,6 +24,26 @@ CODEPAGE_CHARSET = {
     65001: "UTF-8",
 }
 
+class PascalStringWin16(FieldSet):
+    def __init__(self, parent, name, description=None, strip=None, charset="UTF-16-LE"):
+        FieldSet.__init__(self, parent, name, description)
+        length = self["length"].value
+        self._size = 16 + length * 16
+        self.strip = strip
+        self.charset = charset
+
+    def createFields(self):
+        yield UInt16(self, "length", "Length in widechar characters")
+        size = self["length"].value
+        if size:
+            yield String(self, "text", size*2, charset=self.charset, strip=self.strip)
+
+    def createValue(self):
+        if "text" in self:
+            return self["text"].value
+        else:
+            return None
+
 class PascalStringWin32(FieldSet):
     def __init__(self, parent, name, description=None, strip=None, charset="UTF-16-LE"):
         FieldSet.__init__(self, parent, name, description)
diff --git a/lib/hachoir_parser/container/action_script.py b/lib/hachoir_parser/container/action_script.py
index 6769ac114ea4f988c62a858dc06f037dbe784835..4e22cef986b10410f8a1a6caac74811f41fedf16 100644
--- a/lib/hachoir_parser/container/action_script.py
+++ b/lib/hachoir_parser/container/action_script.py
@@ -5,29 +5,64 @@ Documentation:
 
  - Alexis' SWF Reference:
    http://www.m2osw.com/swf_alexref.html
+ - Tamarin ABC format:
+   http://www.m2osw.com/abc_format.html
 
-Author: Sebastien Ponce
+Authors: Sebastien Ponce, Robert Xiao
 Creation date: 26 April 2008
 """
 
+from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, ParserError,
-    Bit, Bits, UInt8, UInt32, Int16, UInt16, Float32, CString,
-    RawBytes)
-#from hachoir_core.field import Field
+    Bit, Bits, UInt8, UInt32, Int16, UInt16, Float32, Float64, CString, Enum,
+    Bytes, RawBytes, NullBits, String, SubFile, Field)
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 from hachoir_core.field.float import FloatExponent
 from struct import unpack
 
+class FlashPackedInteger(Bits):
+    def __init__(self, parent, name, signed=False, nbits=30, description=None):
+        Bits.__init__(self, parent, name, 8, description)
+        stream = self._parent.stream
+        addr = self.absolute_address
+        size = 0
+        value = 0
+        mult = 1
+        while True:
+            byte = stream.readBits(addr+size, 8, LITTLE_ENDIAN)
+            value += mult * (byte & 0x7f)
+            size += 8
+            mult <<= 7
+            if byte < 128:
+                break
+        self._size = size
+        if signed and (1 << (nbits-1)) <= value:
+            value -= (1 << nbits)
+        self.createValue = lambda: value
+
+class FlashU30(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=False, nbits=30, description=description)
+
+class FlashS32(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=True, nbits=32, description=description)
+
+class FlashU32(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=False, nbits=32, description=description)
+
 class FlashFloat64(FieldSet):
     def createFields(self):
-        yield Bits(self, "mantisa_high", 20)
+        yield Bits(self, "mantissa_high", 20)
         yield FloatExponent(self, "exponent", 11)
         yield Bit(self, "negative")
-        yield Bits(self, "mantisa_low", 32)
+        yield Bits(self, "mantissa_low", 32)
 
     def createValue(self):
         # Manual computation:
-        # mantisa = mantisa_high * 2^32 + mantisa_low
-        # float = 2^exponent + (1 + mantisa / 2^52)
+        # mantissa = mantissa_high * 2^32 + mantissa_low
+        # float = 2^exponent + (1 + mantissa / 2^52)
         # (and float is negative if negative=True)
         bytes = self.parent.stream.readBytes(
             self.absolute_address, self.size//8)
@@ -44,8 +79,8 @@ TYPE_INFO = {
     0x05: (UInt8, "Boolean[]"),
     0x06: (FlashFloat64, "Double[]"),
     0x07: (UInt32, "Integer[]"),
-    0x08: (UInt8, "Dictionnary_Lookup_Index[]"),
-    0x09: (UInt16, "Large_Dictionnary_Lookup_Index[]"),
+    0x08: (UInt8, "Dictionary_Lookup_Index[]"),
+    0x09: (UInt16, "Large_Dictionary_Lookup_Index[]"),
 }
 
 def parseBranch(parent, size):
@@ -135,7 +170,7 @@ def parseWaitForFrame(parent, size):
 def parseWaitForFrameDyn(parent, size):
     yield UInt8(parent, "skip")
 
-def parseDeclareDictionnary(parent, size):
+def parseDeclareDictionary(parent, size):
     count = UInt16(parent, "count")
     yield count
     for i in range(count.value):
@@ -231,7 +266,7 @@ class Instruction(FieldSet):
         # Objects
         0x2B: ("Cast_Object[]", "Cast Object", None),
         0x42: ("Declare_Array[]", "Declare Array", None),
-        0x88: ("Declare_Dictionary[]", "Declare Dictionary", parseDeclareDictionnary),
+        0x88: ("Declare_Dictionary[]", "Declare Dictionary", parseDeclareDictionary),
         0x43: ("Declare_Object[]", "Declare Object", None),
         0x3A: ("Delete[]", "Delete", None),
         0x3B: ("Delete_All[]", "Delete All", None),
@@ -314,3 +349,313 @@ class ActionScript(FieldSet):
 def parseActionScript(parent, size):
     yield ActionScript(parent, "action", size=size*8)
 
+def FindABC(field):
+    while not getattr(field, "isABC", False):
+        field = field.parent
+        if field is None:
+            return None
+    return field
+
+def GetConstant(field, pool, index):
+    if index == 0:
+        return None
+    return FindABC(field)["constant_%s_pool/constant[%i]"%(pool, index)]
+
+def GetMultiname(field, index):
+    fld = GetConstant(field, "multiname", index)
+    if fld is None:
+        return "*"
+    if "name_index" not in fld:
+        return "?"
+    fld2 = GetConstant(fld, "string", fld["name_index"].value)
+    if fld2 is None:
+        return "*"
+    return fld2.value
+
+class ABCStringIndex(FlashU30):
+    def createDisplay(self):
+        fld = GetConstant(self, "string", self.value)
+        if fld is None:
+            return "*"
+        return fld.value
+
+class ABCNSIndex(FlashU30):
+    def createDisplay(self):
+        fld = GetConstant(self, "namespace", self.value)
+        if fld is None:
+            return "*"
+        return fld.display
+
+class ABCMethodIndex(FlashU30):
+    def createDisplay(self):
+        fld = FindABC(self)["method_array/method[%i]"%self.value]
+        if fld is None:
+            return "*"
+        return fld.description
+
+class ABCMultinameIndex(FlashU30):
+    def createDisplay(self):
+        return GetMultiname(self, self.value)
+
+class ABCConstantPool(FieldSet):
+    def __init__(self, parent, name, klass):
+        FieldSet.__init__(self, parent, 'constant_%s_pool'%name)
+        self.klass = klass
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value-1):
+            yield self.klass(self, "constant[%i]"%(i+1))
+
+class ABCObjectArray(FieldSet):
+    def __init__(self, parent, name, klass):
+        self.arrname = name
+        FieldSet.__init__(self, parent, name+'_array')
+        self.klass = klass
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield self.klass(self, self.arrname+"[]")
+
+class ABCClassArray(FieldSet):
+    def __init__(self, parent, name):
+        FieldSet.__init__(self, parent, name+'_array')
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield ABCInstanceInfo(self, "instance[]")
+        for i in xrange(ctr.value):
+            yield ABCClassInfo(self, "class[]")
+
+class ABCConstantString(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "length")
+        size = self["length"].value
+        if size:
+            yield String(self, "data", size, charset="UTF-8")
+
+    def createDisplay(self):
+        if "data" in self:
+            return self["data"].display
+        else:
+            return "<empty>"
+
+    def createValue(self):
+        if "data" in self:
+            return self["data"].value
+        else:
+            return ""
+
+class ABCConstantNamespace(FieldSet):
+    NAMESPACE_KIND = {8: "Namespace",
+                      5: "PrivateNamespace",
+                      22: "PackageNamespace",
+                      23: "PacakgeInternalNamespace",
+                      24: "ProtectedNamespace",
+                      25: "ExplicitNamespace",
+                      26: "MultinameL"}
+    def createFields(self):
+        yield Enum(UInt8(self, "kind"), self.NAMESPACE_KIND)
+        yield ABCStringIndex(self, "name_index")
+
+    def createDisplay(self):
+        return "%s %s"%(self["kind"].display, self["name_index"].display)
+
+    def createValue(self):
+        return self["name_index"].value
+
+class ABCConstantNamespaceSet(FieldSet):
+    def createFields(self):
+        ctr = FlashU30(self, "namespace_count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield ABCNSIndex(self, "namespace_index[]")
+
+    def createDescription(self):
+        ret = [fld.display for fld in self.array("namespace_index")]
+        return ', '.join(ret)
+
+class ABCConstantMultiname(FieldSet):
+    MULTINAME_KIND = {7: "Qname",
+                      13: "QnameA",
+                      9: "Multiname",
+                      14: "MultinameA",
+                      15: "RTQname",
+                      16: "RTQnameA",
+                      27: "MultinameL",
+                      17: "RTQnameL",
+                      18: "RTQnameLA"}
+    def createFields(self):
+        yield Enum(UInt8(self, "kind"), self.MULTINAME_KIND)
+        kind = self["kind"].value
+        if kind in (7,13): # Qname
+            yield FlashU30(self, "namespace_index")
+            yield ABCStringIndex(self, "name_index")
+        elif kind in (9,14): # Multiname
+            yield ABCStringIndex(self, "name_index")
+            yield FlashU30(self, "namespace_set_index")
+        elif kind in (15,16): # RTQname
+            yield ABCStringIndex(self, "name_index")
+        elif kind == 27: # MultinameL
+            yield FlashU30(self, "namespace_set_index")
+        elif kind in (17,18): # RTQnameL
+            pass
+
+    def createDisplay(self):
+        kind = self["kind"].display
+        if "name_index" in self:
+            return kind + " " + self["name_index"].display
+        return kind
+
+    def createValue(self):
+        return self["kind"].value
+
+class ABCTrait(FieldSet):
+    TRAIT_KIND = {0: "slot",
+                  1: "method",
+                  2: "getter",
+                  3: "setter",
+                  4: "class",
+                  5: "function",
+                  6: "const",}
+    def createFields(self):
+        yield ABCMultinameIndex(self, "name_index")
+        yield Enum(Bits(self, "kind", 4), self.TRAIT_KIND)
+        yield Enum(Bit(self, "is_final"), {True:'final',False:'virtual'})
+        yield Enum(Bit(self, "is_override"), {True:'override',False:'new'})
+        yield Bit(self, "has_metadata")
+        yield Bits(self, "unused", 1)
+        kind = self["kind"].value
+        if kind in (0,6): # slot, const
+            yield FlashU30(self, "slot_id")
+            yield ABCMultinameIndex(self, "type_index")
+            ### TODO reference appropriate constant pool using value_kind
+            yield FlashU30(self, "value_index")
+            if self['value_index'].value != 0:
+                yield UInt8(self, "value_kind")
+        elif kind in (1,2,3): # method, getter, setter
+            yield FlashU30(self, "disp_id")
+            yield ABCMethodIndex(self, "method_info")
+        elif kind == 4: # class
+            yield FlashU30(self, "disp_id")
+            yield FlashU30(self, "class_info")
+        elif kind == 5: # function
+            yield FlashU30(self, "disp_id")
+            yield ABCMethodIndex(self, "method_info")
+        if self['has_metadata'].value:
+            yield ABCObjectArray(self, "metadata", FlashU30)
+
+class ABCValueKind(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "value_index")
+        yield UInt8(self, "value_kind")
+
+class ABCMethodInfo(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "param_count")
+        yield ABCMultinameIndex(self, "ret_type")
+        for i in xrange(self["param_count"].value):
+            yield ABCMultinameIndex(self, "param_type[]")
+        yield ABCStringIndex(self, "name_index")
+        yield Bit(self, "need_arguments")
+        yield Bit(self, "need_activation")
+        yield Bit(self, "need_rest")
+        yield Bit(self, "has_optional")
+        yield Bit(self, "ignore_rest")
+        yield Bit(self, "explicit")
+        yield Bit(self, "setsdxns")
+        yield Bit(self, "has_paramnames")
+        if self["has_optional"].value:
+            yield ABCObjectArray(self, "optional", ABCValueKind)
+        if self["has_paramnames"].value:
+            for i in xrange(self["param_count"].value):
+                yield FlashU30(self, "param_name[]")
+
+    def createDescription(self):
+        ret = GetMultiname(self, self["ret_type"].value)
+        ret += " " + self["name_index"].display
+        ret += "(" + ", ".join(GetMultiname(self, fld.value) for fld in self.array("param_type")) + ")"
+        return ret
+
+class ABCMetadataInfo(FieldSet):
+    def createFields(self):
+        yield ABCStringIndex(self, "name_index")
+        yield FlashU30(self, "values_count")
+        count = self["values_count"].value
+        for i in xrange(count):
+            yield FlashU30(self, "key[]")
+        for i in xrange(count):
+            yield FlashU30(self, "value[]")
+
+class ABCInstanceInfo(FieldSet):
+    def createFields(self):
+        yield ABCMultinameIndex(self, "name_index")
+        yield ABCMultinameIndex(self, "super_index")
+        yield Bit(self, "is_sealed")
+        yield Bit(self, "is_final")
+        yield Bit(self, "is_interface")
+        yield Bit(self, "is_protected")
+        yield Bits(self, "unused", 4)
+        if self['is_protected'].value:
+            yield ABCNSIndex(self, "protectedNS")
+        yield FlashU30(self, "interfaces_count")
+        for i in xrange(self["interfaces_count"].value):
+            yield ABCMultinameIndex(self, "interface[]")
+        yield ABCMethodIndex(self, "iinit_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCClassInfo(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "cinit_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCScriptInfo(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "init_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCException(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "start")
+        yield FlashU30(self, "end")
+        yield FlashU30(self, "target")
+        yield FlashU30(self, "type_index")
+        yield FlashU30(self, "name_index")
+
+class ABCMethodBody(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "method_info")
+        yield FlashU30(self, "max_stack")
+        yield FlashU30(self, "max_regs")
+        yield FlashU30(self, "scope_depth")
+        yield FlashU30(self, "max_scope")
+        yield FlashU30(self, "code_length")
+        yield RawBytes(self, "code", self['code_length'].value)
+        yield ABCObjectArray(self, "exception", ABCException)
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+def parseABC(parent, size):
+    code = parent["code"].value
+    if code == parent.TAG_DO_ABC_DEFINE:
+        yield UInt32(parent, "action_flags")
+        yield CString(parent, "action_name")
+    yield UInt16(parent, "minor_version")
+    yield UInt16(parent, "major_version")
+    parent.isABC = True
+
+    yield ABCConstantPool(parent, "int", FlashS32)
+    yield ABCConstantPool(parent, "uint", FlashU32)
+    yield ABCConstantPool(parent, "double", Float64)
+    yield ABCConstantPool(parent, "string", ABCConstantString)
+    yield ABCConstantPool(parent, "namespace", ABCConstantNamespace)
+    yield ABCConstantPool(parent, "namespace_set", ABCConstantNamespaceSet)
+    yield ABCConstantPool(parent, "multiname", ABCConstantMultiname)
+
+    yield ABCObjectArray(parent, "method", ABCMethodInfo)
+    yield ABCObjectArray(parent, "metadata", ABCMetadataInfo)
+    yield ABCClassArray(parent, "class")
+    yield ABCObjectArray(parent, "script", ABCScriptInfo)
+    yield ABCObjectArray(parent, "body", ABCMethodBody)
+
diff --git a/lib/hachoir_parser/container/mkv.py b/lib/hachoir_parser/container/mkv.py
index 4e90f460e57680050237e1dc86d15af0618ea8c3..0d3974c0d2150dab5cdb1bad003307df6e67dd24 100644
--- a/lib/hachoir_parser/container/mkv.py
+++ b/lib/hachoir_parser/container/mkv.py
@@ -66,7 +66,7 @@ def SInt(parent):
     return GenericInteger(parent, 'signed', True, parent['size'].value*8)
 
 def String(parent):
-    return _String(parent, 'string', parent['size'].value, charset="ASCII")
+    return _String(parent, 'string', parent['size'].value, charset="ASCII", strip="\0")
 
 def EnumString(parent, enum):
     return _Enum(String(parent), enum)
@@ -206,7 +206,7 @@ class Block(FieldSet):
             yield Bit(self, 'invisible')
             yield self.lacing()
             yield NullBits(self, 'reserved[]', 1)
-        elif self.parent._name == 'SimpleBlock[]':
+        elif self.parent._name.startswith('SimpleBlock'):
             yield Bit(self, 'keyframe')
             yield NullBits(self, 'reserved', 3)
             yield Bit(self, 'invisible')
diff --git a/lib/hachoir_parser/container/swf.py b/lib/hachoir_parser/container/swf.py
index 951c62192c83cca88fedd57759d4c19d85714071..942e3d9eeb1eaa54cceeadb654444e524fa7d140 100644
--- a/lib/hachoir_parser/container/swf.py
+++ b/lib/hachoir_parser/container/swf.py
@@ -15,7 +15,7 @@ Creation date: 29 october 2006
 
 from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, ParserError,
-    Bit, Bits, UInt8, UInt32, UInt16, CString, Enum,
+    Bit, Bits, UInt8, UInt16, Int32, UInt32, Int64, CString, Enum,
     Bytes, RawBytes, NullBits, String, SubFile)
 from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 from hachoir_core.text_handler import textHandler, filesizeHandler
@@ -24,7 +24,7 @@ from hachoir_parser.image.common import RGB
 from hachoir_parser.image.jpeg import JpegChunk, JpegFile
 from hachoir_core.stream import StringInputStream, ConcatStream
 from hachoir_parser.common.deflate import Deflate, has_deflate
-from hachoir_parser.container.action_script import parseActionScript
+from hachoir_parser.container.action_script import parseActionScript, parseABC
 import math
 
 # Maximum file size (50 MB)
@@ -206,10 +206,35 @@ def parseExport(parent, size):
     for index in xrange(parent["count"].value):
         yield Export(parent, "export[]")
 
+def parseProductInfo(parent, size):
+    yield Int32(parent, "product_id")
+    yield Int32(parent, "edition")
+    yield UInt8(parent, "major_version")
+    yield UInt8(parent, "minor_version")
+    yield Int64(parent, "build_number")
+    yield Int64(parent, "compilation_date")
+
+def parseScriptLimits(parent, size):
+    yield UInt16(parent, "max_recursion_limit")
+    yield UInt16(parent, "timeout_seconds", "Seconds of processing until the SWF is considered 'stuck'")
+
+def parseSymbolClass(parent, size):
+    yield UInt16(parent, "count")
+    for index in xrange(parent["count"].value):
+        yield UInt16(parent, "symbol_id[]")
+        yield CString(parent, "symbol_name[]")
+
+def parseBinaryData(parent, size):
+    yield UInt16(parent, "data_id")
+    yield UInt32(parent, "reserved")
+    if size > 6:
+        yield RawBytes(parent, "data", size-6)
+
 class Tag(FieldSet):
     TAG_BITS = 6
     TAG_BITS_JPEG2 = 32
     TAG_BITS_JPEG3 = 35
+    TAG_DO_ABC_DEFINE = 82
     TAG_INFO = {
         # SWF version 1.0
          0: ("end[]", "End", None),
@@ -253,7 +278,7 @@ class Tag(FieldSet):
         36: ("def_bits_lossless2[]", "Define bits lossless 2", None),
         39: ("def_sprite[]", "Define sprite", None),
         40: ("name_character[]", "Name character", None),
-        41: ("serial_number", "Serial number", None),
+        41: ("product_info", "Generator product info", parseProductInfo),
         42: ("generator_text[]", "Generator text", None),
         43: ("frame_label[]", "Frame label", None),
         45: ("sound_hdr2[]", "Sound stream header2", parseSoundHeader),
@@ -283,7 +308,7 @@ class Tag(FieldSet):
         64: ("enable_debug2", "Enable debugger 2", None),
 
         # SWF version 7.0
-        65: ("script_limits[]", "Script limits", None),
+        65: ("script_limits[]", "Script limits", parseScriptLimits),
         66: ("tab_index[]", "Set tab index", None),
 
         # SWF version 8.0
@@ -297,6 +322,14 @@ class Tag(FieldSet):
         78: ("def_scale_grid[]", "Define scaling factors", None),
         83: ("def_shape4[]", "Define shape 4", None),
         84: ("def_morph2[]", "Define a morphing shape 2", None),
+
+        # SWF version 9.0
+        72: ("do_abc[]", "SWF 9 ActionScript container; actions only", parseABC),
+        76: ("symbol_class[]", "Instantiate objects from a set of classes", parseSymbolClass),
+        82: ("do_abc_define[]", "SWF 9 ActionScript container; identifier, name, actions", parseABC),
+        86: ("def_scene_frame[]", "Define raw data for scenes and frames", None),
+        87: ("def_binary_data[]", "Defines a buffer of any size with any binary user data", parseBinaryData),
+        88: ("def_font_name[]", "Define the legal font name and copyright", None),
     }
 
     def __init__(self, *args):
@@ -332,7 +365,7 @@ class Tag(FieldSet):
         return "Tag: %s (%s)" % (self["code"].display, self["length"].display)
 
 class SwfFile(Parser):
-    VALID_VERSIONS = set(xrange(1, 9+1))
+    VALID_VERSIONS = set(xrange(1, 10+1))
     PARSER_TAGS = {
         "id": "swf",
         "category": "container",
diff --git a/lib/hachoir_parser/file_system/reiser_fs.py b/lib/hachoir_parser/file_system/reiser_fs.py
index e71eb9509358715f2fe1b4b0e09eb2a969fc6717..52a0dbf89e82e261e95402d07447b61d55bb5d90 100644
--- a/lib/hachoir_parser/file_system/reiser_fs.py
+++ b/lib/hachoir_parser/file_system/reiser_fs.py
@@ -1,5 +1,5 @@
 """
-ReiserFS file system version 3 parser (version 1, 2 and 4 are not supported).
+ReiserFS file system version 3 parser (other version have not been tested).
 
 Author: Frederic Weisbecker
 Creation date: 8 december 2006
@@ -22,9 +22,62 @@ Kurz.
 
 from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, Enum,
-    UInt16, UInt32, String, RawBytes, NullBytes)
+    UInt16, UInt32, String, RawBytes, NullBytes, SeekableFieldSet, Bit)
 from hachoir_core.endian import LITTLE_ENDIAN
 
+
+class BlockState(Bit):
+    """The state (used/free) of a ReiserFs Block"""
+
+    STATE={
+        True : "used",
+        False : "free"
+    }
+
+    block_nb = 0
+
+    def __init__(self, parent, name, nb_block):
+        """@param nb_block: Number of the block concerned"""
+        Bit.__init__(self, parent, name)
+        self.block_nb = self.__class__.block_nb
+        self.__class__.block_nb += 1
+
+    def createDescription(self):
+        return "State of the block %d" %  self.block_nb
+
+    def createDisplay(self):
+        return self.STATE[Bit.createValue(self)]
+
+
+class BitmapBlock(SeekableFieldSet):
+    """ The bitmap blocks are Reiserfs blocks where each byte contains
+        the state of 8 blocks in the filesystem. So each bit will describe
+        the state of a block to tell if it is used or not.
+    """    
+    def createFields(self):
+        block_size=self["/superblock/blocksize"].value
+        
+        for i in xrange(0, block_size * 8):
+            yield BlockState(self, "block[]", i)
+
+
+class BitmapBlockGroup(SeekableFieldSet):
+    """The group that manages the Bitmap Blocks"""
+    
+    def createFields(self):   
+        block_size=self["/superblock/blocksize"].value  
+        nb_bitmap_block = self["/superblock/bmap_nr"].value
+        # Position of the first bitmap block
+        self.seekByte(REISER_FS.SUPERBLOCK_OFFSET + block_size, relative=False)
+   
+        yield BitmapBlock(self, "BitmapBlock[]", "Bitmap blocks tells for each block if it is used")    
+        # The other bitmap blocks
+        for i in xrange(1, nb_bitmap_block):
+            self.seekByte( (block_size**2) * 8 * i, relative=False)
+            yield BitmapBlock(self, "BitmapBlock[]", "Bitmap blocks tells for each block if it is used")
+
+
+
 class Journal_params(FieldSet):
     static_size = 32*8
 
@@ -44,7 +97,7 @@ class Journal_params(FieldSet):
         return "Parameters of the journal"
 
 class SuperBlock(FieldSet):
-    static_size = 204*8
+    #static_size = 204*8
 
     UMOUNT_STATE = { 1: "unmounted", 2: "not unmounted" }
     HASH_FUNCTIONS = {
@@ -84,6 +137,7 @@ class SuperBlock(FieldSet):
         yield RawBytes(self, "uuid", 16, "Filesystem unique identifier")
         yield String(self, "label", 16, "Filesystem volume label", strip="\0")
         yield NullBytes(self, "unused", 88)
+        yield NullBytes(self, "Bytes before end of the block", self["blocksize"].value-204)
 
     def createDescription(self):
         return "Superblock: ReiserFs Filesystem"
@@ -108,13 +162,11 @@ class REISER_FS(Parser):
     def validate(self):
         # Let's look at the magic field in the superblock
         magic = self.stream.readBytes(self.MAGIC_OFFSET*8, 9).rstrip("\0")
-        if magic == "ReIsEr3Fs":
+        if magic in ("ReIsEr3Fs", "ReIsErFs", "ReIsEr2Fs"):
             return True
-        if magic in ("ReIsEr2Fs", "ReIsErFs"):
-            return "Unsupported version of ReiserFs"
         return "Invalid magic string"
 
     def createFields(self):
         yield NullBytes(self, "padding[]", self.SUPERBLOCK_OFFSET)
         yield SuperBlock(self, "superblock")
-
+        yield BitmapBlockGroup(self, "Group of bitmap blocks")
diff --git a/lib/hachoir_parser/guess.py b/lib/hachoir_parser/guess.py
index 1f77b48b5f2ad1f614a7d3ba380d9bdccdac35d4..758cec6506b1a83cb0c2a0b4dd6f4a866074eac4 100644
--- a/lib/hachoir_parser/guess.py
+++ b/lib/hachoir_parser/guess.py
@@ -8,6 +8,7 @@ from hachoir_core.error import warning, info, HACHOIR_ERRORS
 from hachoir_parser import ValidateError, HachoirParserList
 from hachoir_core.stream import FileInputStream
 from hachoir_core.i18n import _
+import weakref
 
 
 class QueryParser(object):
@@ -80,6 +81,19 @@ class QueryParser(object):
         return parsers
 
     def parse(self, stream, fallback=True):
+        if hasattr(stream, "_cached_parser"):
+            parser = stream._cached_parser()
+        else:
+            parser = None
+        if parser is not None:
+            if parser.__class__ in self.parsers:
+                return parser
+        parser = self.doparse(stream, fallback)
+        if parser is not None:
+            stream._cached_parser = weakref.ref(parser)
+        return parser
+
+    def doparse(self, stream, fallback=True):
         fb = None
         warn = warning
         for parser in self.parsers:
diff --git a/lib/hachoir_parser/image/exif.py b/lib/hachoir_parser/image/exif.py
index 7b867933310f3b7aa2b860f150396c6ef7b3683a..449c7ba0c2f64a76e9bed549fc534cf38fa8fb85 100644
--- a/lib/hachoir_parser/image/exif.py
+++ b/lib/hachoir_parser/image/exif.py
@@ -1,19 +1,26 @@
 """
-EXIF metadata parser (can be found in a JPEG picture for example)
+EXIF metadata parser; also parses TIFF file headers.
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
+
+References:
+- Exif 2.2 Specification (JEITA CP-3451)
+    http://www.exif.org/Exif2-2.PDF
+- TIFF 6.0 Specification
+    http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
 """
 
-from hachoir_core.field import (FieldSet, ParserError,
+from hachoir_core.field import (FieldSet, SeekableFieldSet, ParserError,
     UInt8, UInt16, UInt32,
-    Int32, Enum, String,
-    Bytes, SubFile,
-    NullBytes, createPaddingField)
+    Int8, Int16, Int32,
+    Float32, Float64,
+    Enum, String, Bytes, SubFile,
+    NullBits, NullBytes, createPaddingField)
 from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN, NETWORK_ENDIAN
 from hachoir_core.text_handler import textHandler, hexadecimal
 from hachoir_core.tools import createDict
 
-MAX_COUNT = 1000
+MAX_COUNT = 1000 # maximum number of array entries in an IFD entry (excluding string types)
 
 def rationalFactory(class_name, size, field_class):
     class Rational(FieldSet):
@@ -32,6 +39,16 @@ def rationalFactory(class_name, size, field_class):
 RationalInt32 = rationalFactory("RationalInt32", 64, Int32)
 RationalUInt32 = rationalFactory("RationalUInt32", 64, UInt32)
 
+class ASCIIString(String):
+    def __init__(self, parent, name, nbytes, description=None, strip=' \0', charset='ISO-8859-1', *args, **kwargs):
+        String.__init__(self, parent, name, nbytes, description, strip, charset, *args, **kwargs)
+
+class IFDTag(UInt16):
+    def getTag(self):
+        return self.parent.TAG_INFO.get(self.value, (hex(self.value), ""))
+    def createDisplay(self):
+        return self.getTag()[0]
+
 class BasicIFDEntry(FieldSet):
     TYPE_BYTE = 0
     TYPE_UNDEFINED = 7
@@ -39,323 +56,364 @@ class BasicIFDEntry(FieldSet):
     TYPE_SIGNED_RATIONAL = 10
     TYPE_INFO = {
          1: (UInt8, "BYTE (8 bits)"),
-         2: (String, "ASCII (8 bits)"),
+         2: (ASCIIString, "ASCII (8 bits)"),
          3: (UInt16, "SHORT (16 bits)"),
          4: (UInt32, "LONG (32 bits)"),
          5: (RationalUInt32, "RATIONAL (2x LONG, 64 bits)"),
+         6: (Int8, "SBYTE (8 bits)"),
          7: (Bytes, "UNDEFINED (8 bits)"),
-         9: (Int32, "SIGNED LONG (32 bits)"),
-        10: (RationalInt32, "SRATIONAL (2x SIGNED LONGs, 64 bits)"),
+         8: (Int16, "SSHORT (16 bits)"),
+         9: (Int32, "SLONG (32 bits)"),
+        10: (RationalInt32, "SRATIONAL (2x SLONG, 64 bits)"),
+        11: (Float32, "FLOAT (32 bits)"),
+        12: (Float64, "DOUBLE (64 bits)"),
     }
     ENTRY_FORMAT = createDict(TYPE_INFO, 0)
     TYPE_NAME = createDict(TYPE_INFO, 1)
+    TAG_INFO = {}
 
     def createFields(self):
-        yield Enum(textHandler(UInt16(self, "tag", "Tag"), hexadecimal), self.TAG_NAME)
-        yield Enum(textHandler(UInt16(self, "type", "Type"), hexadecimal), self.TYPE_NAME)
+        yield IFDTag(self, "tag", "Tag")
+        yield Enum(UInt16(self, "type", "Type"), self.TYPE_NAME)
+        self.value_cls = self.ENTRY_FORMAT.get(self['type'].value, Bytes)
+        if issubclass(self.value_cls, Bytes):
+            self.value_size = 8
+        else:
+            self.value_size = self.value_cls.static_size
         yield UInt32(self, "count", "Count")
-        if self["type"].value not in (self.TYPE_BYTE, self.TYPE_UNDEFINED) \
-        and  MAX_COUNT < self["count"].value:
+
+        if not issubclass(self.value_cls, Bytes) \
+          and self["count"].value > MAX_COUNT:
             raise ParserError("EXIF: Invalid count value (%s)" % self["count"].value)
-        value_size, array_size = self.getSizes()
 
-        # Get offset/value
-        if not value_size:
+        count = self['count'].value
+        totalsize = self.value_size * count
+        if count == 0:
             yield NullBytes(self, "padding", 4)
-        elif value_size <= 32:
-            if 1 < array_size:
-                name = "value[]"
-            else:
-                name = "value"
-            kw = {}
-            cls = self.value_cls
-            if cls is String:
-                args = (self, name, value_size/8, "Value")
-                kw["strip"] = " \0"
-                kw["charset"] = "ISO-8859-1"
-            elif cls is Bytes:
-                args = (self, name, value_size/8, "Value")
+        elif totalsize <= 32:
+            name = "value"
+            if issubclass(self.value_cls, Bytes):
+                yield self.value_cls(self, name, count)
             else:
-                args = (self, name, "Value")
-            for index in xrange(array_size):
-                yield cls(*args, **kw)
-
-            size = array_size * value_size
-            if size < 32:
-                yield NullBytes(self, "padding", (32-size)//8)
+                if count > 1:
+                    name += "[]"
+                for i in xrange(count):
+                    yield self.value_cls(self, name)
+            if totalsize < 32:
+                yield NullBits(self, "padding", 32-totalsize)
         else:
             yield UInt32(self, "offset", "Value offset")
 
-    def getSizes(self):
-        """
-        Returns (value_size, array_size): value_size in bits and
-        array_size in number of items.
-        """
-        # Create format
-        self.value_cls = self.ENTRY_FORMAT.get(self["type"].value, Bytes)
+    def createValue(self):
+        if "value" in self:
+            return self['value'].value
+        return None
 
-        # Set size
-        count = self["count"].value
-        if self.value_cls in (String, Bytes):
-            return 8 * count, 1
-        else:
-            return self.value_cls.static_size * count, count
+    def createDescription(self):
+        return "Entry: "+self["tag"].getTag()[1]
 
-class ExifEntry(BasicIFDEntry):
-    OFFSET_JPEG_SOI = 0x0201
+class IFDEntry(BasicIFDEntry):
     EXIF_IFD_POINTER = 0x8769
+    GPS_IFD_POINTER = 0x8825
+    INTEROP_IFD_POINTER = 0xA005
+
+    TAG_INFO = {
+        # image data structure
+        0x0100: ("ImageWidth", "Image width"),
+        0x0101: ("ImageLength", "Image height"),
+        0x0102: ("BitsPerSample", "Number of bits per component"),
+        0x0103: ("Compression", "Compression scheme"),
+        0x0106: ("PhotometricInterpretation", "Pixel composition"),
+        0x0112: ("Orientation", "Orientation of image"),
+        0x0115: ("SamplesPerPixel", "Number of components"),
+        0x011C: ("PlanarConfiguration", "Image data arrangement"),
+        0x0212: ("YCbCrSubSampling", "Subsampling ratio of Y to C"),
+        0x0213: ("YCbCrPositioning", "Y and C positioning"),
+        0x011A: ("XResolution", "Image resolution in width direction"),
+        0x011B: ("YResolution", "Image resolution in height direction"),
+        0x0128: ("ResolutionUnit", "Unit of X and Y resolution"),
+        # recording offset
+        0x0111: ("StripOffsets", "Image data location"),
+        0x0116: ("RowsPerStrip", "Number of rows per strip"),
+        0x0117: ("StripByteCounts", "Bytes per compressed strip"),
+        0x0201: ("JPEGInterchangeFormat", "Offset to JPEG SOI"),
+        0x0202: ("JPEGInterchangeFormatLength", "Bytes of JPEG data"),
+        # image data characteristics
+        0x012D: ("TransferFunction", "Transfer function"),
+        0x013E: ("WhitePoint", "White point chromaticity"),
+        0x013F: ("PrimaryChromaticities", "Chromaticities of primaries"),
+        0x0211: ("YCbCrCoefficients", "Color space transformation matrix coefficients"),
+        0x0214: ("ReferenceBlackWhite", "Pair of black and white reference values"),
+        # other tags
+        0x0132: ("DateTime", "File change date and time"),
+        0x010E: ("ImageDescription", "Image title"),
+        0x010F: ("Make", "Image input equipment manufacturer"),
+        0x0110: ("Model", "Image input equipment model"),
+        0x0131: ("Software", "Software used"),
+        0x013B: ("Artist", "Person who created the image"),
+        0x8298: ("Copyright", "Copyright holder"),
+        # TIFF-specific tags
+        0x00FE: ("NewSubfileType", "NewSubfileType"),
+        0x00FF: ("SubfileType", "SubfileType"),
+        0x0107: ("Threshholding", "Threshholding"),
+        0x0108: ("CellWidth", "CellWidth"),
+        0x0109: ("CellLength", "CellLength"),
+        0x010A: ("FillOrder", "FillOrder"),
+        0x010D: ("DocumentName", "DocumentName"),
+        0x0118: ("MinSampleValue", "MinSampleValue"),
+        0x0119: ("MaxSampleValue", "MaxSampleValue"),
+        0x011D: ("PageName", "PageName"),
+        0x011E: ("XPosition", "XPosition"),
+        0x011F: ("YPosition", "YPosition"),
+        0x0120: ("FreeOffsets", "FreeOffsets"),
+        0x0121: ("FreeByteCounts", "FreeByteCounts"),
+        0x0122: ("GrayResponseUnit", "GrayResponseUnit"),
+        0x0123: ("GrayResponseCurve", "GrayResponseCurve"),
+        0x0124: ("T4Options", "T4Options"),
+        0x0125: ("T6Options", "T6Options"),
+        0x0129: ("PageNumber", "PageNumber"),
+        0x013C: ("HostComputer", "HostComputer"),
+        0x013D: ("Predictor", "Predictor"),
+        0x0140: ("ColorMap", "ColorMap"),
+        0x0141: ("HalftoneHints", "HalftoneHints"),
+        0x0142: ("TileWidth", "TileWidth"),
+        0x0143: ("TileLength", "TileLength"),
+        0x0144: ("TileOffsets", "TileOffsets"),
+        0x0145: ("TileByteCounts", "TileByteCounts"),
+        0x014C: ("InkSet", "InkSet"),
+        0x014D: ("InkNames", "InkNames"),
+        0x014E: ("NumberOfInks", "NumberOfInks"),
+        0x0150: ("DotRange", "DotRange"),
+        0x0151: ("TargetPrinter", "TargetPrinter"),
+        0x0152: ("ExtraSamples", "ExtraSamples"),
+        0x0153: ("SampleFormat", "SampleFormat"),
+        0x0154: ("SMinSampleValue", "SMinSampleValue"),
+        0x0155: ("SMaxSampleValue", "SMaxSampleValue"),
+        0x0156: ("TransferRange", "TransferRange"),
+        0x0200: ("JPEGProc", "JPEGProc"),
+        0x0203: ("JPEGRestartInterval", "JPEGRestartInterval"),
+        0x0205: ("JPEGLosslessPredictors", "JPEGLosslessPredictors"),
+        0x0206: ("JPEGPointTransforms", "JPEGPointTransforms"),
+        0x0207: ("JPEGQTables", "JPEGQTables"),
+        0x0208: ("JPEGDCTables", "JPEGDCTables"),
+        0x0209: ("JPEGACTables", "JPEGACTables"),
+        # IFD pointers
+        EXIF_IFD_POINTER: ("IFDExif", "Exif IFD Pointer"),
+        GPS_IFD_POINTER: ("IFDGPS", "GPS IFD Pointer"),
+        INTEROP_IFD_POINTER: ("IFDInterop", "Interoperability IFD Pointer"),
+    }
 
-    TAG_WIDTH = 0xA002
-    TAG_HEIGHT = 0xA003
-
-    TAG_GPS_LATITUDE_REF = 0x0001
-    TAG_GPS_LATITUDE = 0x0002
-    TAG_GPS_LONGITUDE_REF = 0x0003
-    TAG_GPS_LONGITUDE = 0x0004
-    TAG_GPS_ALTITUDE_REF = 0x0005
-    TAG_GPS_ALTITUDE = 0x0006
-    TAG_GPS_TIMESTAMP = 0x0007
-    TAG_GPS_DATESTAMP = 0x001d
-
-    TAG_IMG_TITLE = 0x010e
-    TAG_FILE_TIMESTAMP = 0x0132
-    TAG_SOFTWARE = 0x0131
-    TAG_CAMERA_MODEL = 0x0110
-    TAG_CAMERA_MANUFACTURER = 0x010f
-    TAG_ORIENTATION = 0x0112
-    TAG_EXPOSURE = 0x829A
-    TAG_FOCAL = 0x829D
-    TAG_BRIGHTNESS = 0x9203
-    TAG_APERTURE = 0x9205
-    TAG_USER_COMMENT = 0x9286
-
-    TAG_NAME = {
-        # GPS
-        0x0000: "GPS version ID",
-        0x0001: "GPS latitude ref",
-        0x0002: "GPS latitude",
-        0x0003: "GPS longitude ref",
-        0x0004: "GPS longitude",
-        0x0005: "GPS altitude ref",
-        0x0006: "GPS altitude",
-        0x0007: "GPS timestamp",
-        0x0008: "GPS satellites",
-        0x0009: "GPS status",
-        0x000a: "GPS measure mode",
-        0x000b: "GPS DOP",
-        0x000c: "GPS speed ref",
-        0x000d: "GPS speed",
-        0x000e: "GPS track ref",
-        0x000f: "GPS track",
-        0x0010: "GPS img direction ref",
-        0x0011: "GPS img direction",
-        0x0012: "GPS map datum",
-        0x0013: "GPS dest latitude ref",
-        0x0014: "GPS dest latitude",
-        0x0015: "GPS dest longitude ref",
-        0x0016: "GPS dest longitude",
-        0x0017: "GPS dest bearing ref",
-        0x0018: "GPS dest bearing",
-        0x0019: "GPS dest distance ref",
-        0x001a: "GPS dest distance",
-        0x001b: "GPS processing method",
-        0x001c: "GPS area information",
-        0x001d: "GPS datestamp",
-        0x001e: "GPS differential",
-
-        0x0100: "Image width",
-        0x0101: "Image height",
-        0x0102: "Number of bits per component",
-        0x0103: "Compression scheme",
-        0x0106: "Pixel composition",
-        TAG_ORIENTATION: "Orientation of image",
-        0x0115: "Number of components",
-        0x011C: "Image data arrangement",
-        0x0212: "Subsampling ratio Y to C",
-        0x0213: "Y and C positioning",
-        0x011A: "Image resolution width direction",
-        0x011B: "Image resolution in height direction",
-        0x0128: "Unit of X and Y resolution",
-
-        0x0111: "Image data location",
-        0x0116: "Number of rows per strip",
-        0x0117: "Bytes per compressed strip",
-        0x0201: "Offset to JPEG SOI",
-        0x0202: "Bytes of JPEG data",
-
-        0x012D: "Transfer function",
-        0x013E: "White point chromaticity",
-        0x013F: "Chromaticities of primaries",
-        0x0211: "Color space transformation matrix coefficients",
-        0x0214: "Pair of blank and white reference values",
-
-        TAG_FILE_TIMESTAMP: "File change date and time",
-        TAG_IMG_TITLE: "Image title",
-        TAG_CAMERA_MANUFACTURER: "Camera (Image input equipment) manufacturer",
-        TAG_CAMERA_MODEL: "Camera (Input input equipment) model",
-        TAG_SOFTWARE: "Software",
-        0x013B: "File change date and time",
-        0x8298: "Copyright holder",
-        0x8769: "Exif IFD Pointer",
-
-        TAG_EXPOSURE: "Exposure time",
-        TAG_FOCAL: "F number",
-        0x8822: "Exposure program",
-        0x8824: "Spectral sensitivity",
-        0x8827: "ISO speed rating",
-        0x8828: "Optoelectric conversion factor OECF",
-        0x9201: "Shutter speed",
-        0x9202: "Aperture",
-        TAG_BRIGHTNESS: "Brightness",
-        0x9204: "Exposure bias",
-        TAG_APERTURE: "Maximum lens aperture",
-        0x9206: "Subject distance",
-        0x9207: "Metering mode",
-        0x9208: "Light source",
-        0x9209: "Flash",
-        0x920A: "Lens focal length",
-        0x9214: "Subject area",
-        0xA20B: "Flash energy",
-        0xA20C: "Spatial frequency response",
-        0xA20E: "Focal plane X resolution",
-        0xA20F: "Focal plane Y resolution",
-        0xA210: "Focal plane resolution unit",
-        0xA214: "Subject location",
-        0xA215: "Exposure index",
-        0xA217: "Sensing method",
-        0xA300: "File source",
-        0xA301: "Scene type",
-        0xA302: "CFA pattern",
-        0xA401: "Custom image processing",
-        0xA402: "Exposure mode",
-        0xA403: "White balance",
-        0xA404: "Digital zoom ratio",
-        0xA405: "Focal length in 35 mm film",
-        0xA406: "Scene capture type",
-        0xA407: "Gain control",
-        0xA408: "Contrast",
-
-        0x9000: "Exif version",
-        0xA000: "Supported Flashpix version",
-        0xA001: "Color space information",
-        0x9101: "Meaning of each component",
-        0x9102: "Image compression mode",
-        TAG_WIDTH: "Valid image width",
-        TAG_HEIGHT: "Valid image height",
-        0x927C: "Manufacturer notes",
-        TAG_USER_COMMENT: "User comments",
-        0xA004: "Related audio file",
-        0x9003: "Date and time of original data generation",
-        0x9004: "Date and time of digital data generation",
-        0x9290: "DateTime subseconds",
-        0x9291: "DateTimeOriginal subseconds",
-        0x9292: "DateTimeDigitized subseconds",
-        0xA420: "Unique image ID",
-        0xA005: "Interoperability IFD Pointer"
+class ExifIFDEntry(BasicIFDEntry):
+    TAG_INFO = {
+        # version
+        0x9000: ("ExifVersion", "Exif version"),
+        0xA000: ("FlashpixVersion", "Supported Flashpix version"),
+        # image data characteristics
+        0xA001: ("ColorSpace", "Color space information"),
+        # image configuration
+        0x9101: ("ComponentsConfiguration", "Meaning of each component"),
+        0x9102: ("CompressedBitsPerPixel", "Image compression mode"),
+        0xA002: ("PixelXDimension", "Valid image width"),
+        0xA003: ("PixelYDimension", "Valid image height"),
+        # user information
+        0x927C: ("MakerNote", "Manufacturer notes"),
+        0x9286: ("UserComment", "User comments"),
+        # related file information
+        0xA004: ("RelatedSoundFile", "Related audio file"),
+        # date and time
+        0x9003: ("DateTimeOriginal", "Date and time of original data generation"),
+        0x9004: ("DateTimeDigitized", "Date and time of digital data generation"),
+        0x9290: ("SubSecTime", "DateTime subseconds"),
+        0x9291: ("SubSecTimeOriginal", "DateTimeOriginal subseconds"),
+        0x9292: ("SubSecTimeDigitized", "DateTimeDigitized subseconds"),
+        # picture-taking conditions
+        0x829A: ("ExposureTime", "Exposure time"),
+        0x829D: ("FNumber", "F number"),
+        0x8822: ("ExposureProgram", "Exposure program"),
+        0x8824: ("SpectralSensitivity", "Spectral sensitivity"),
+        0x8827: ("ISOSpeedRatings", "ISO speed rating"),
+        0x8828: ("OECF", "Optoelectric conversion factor"),
+        0x9201: ("ShutterSpeedValue", "Shutter speed"),
+        0x9202: ("ApertureValue", "Aperture"),
+        0x9203: ("BrightnessValue", "Brightness"),
+        0x9204: ("ExposureBiasValue", "Exposure bias"),
+        0x9205: ("MaxApertureValue", "Maximum lens aperture"),
+        0x9206: ("SubjectDistance", "Subject distance"),
+        0x9207: ("MeteringMode", "Metering mode"),
+        0x9208: ("LightSource", "Light source"),
+        0x9209: ("Flash", "Flash"),
+        0x920A: ("FocalLength", "Lens focal length"),
+        0x9214: ("SubjectArea", "Subject area"),
+        0xA20B: ("FlashEnergy", "Flash energy"),
+        0xA20C: ("SpatialFrequencyResponse", "Spatial frequency response"),
+        0xA20E: ("FocalPlaneXResolution", "Focal plane X resolution"),
+        0xA20F: ("FocalPlaneYResolution", "Focal plane Y resolution"),
+        0xA210: ("FocalPlaneResolutionUnit", "Focal plane resolution unit"),
+        0xA214: ("SubjectLocation", "Subject location"),
+        0xA215: ("ExposureIndex", "Exposure index"),
+        0xA217: ("SensingMethod", "Sensing method"),
+        0xA300: ("FileSource", "File source"),
+        0xA301: ("SceneType", "Scene type"),
+        0xA302: ("CFAPattern", "CFA pattern"),
+        0xA401: ("CustomRendered", "Custom image processing"),
+        0xA402: ("ExposureMode", "Exposure mode"),
+        0xA403: ("WhiteBalance", "White balance"),
+        0xA404: ("DigitalZoomRatio", "Digital zoom ratio"),
+        0xA405: ("FocalLengthIn35mmFilm", "Focal length in 35 mm film"),
+        0xA406: ("SceneCaptureType", "Scene capture type"),
+        0xA407: ("GainControl", "Gain control"),
+        0xA408: ("Contrast", "Contrast"),
+        0xA409: ("Saturation", "Saturation"),
+        0xA40A: ("Sharpness", "Sharpness"),
+        0xA40B: ("DeviceSettingDescription", "Device settings description"),
+        0xA40C: ("SubjectDistanceRange", "Subject distance range"),
+        # other tags
+        0xA420: ("ImageUniqueID", "Unique image ID"),
     }
 
-    def createDescription(self):
-        return "Entry: %s" % self["tag"].display
-
-def sortExifEntry(a,b):
-    return int( a["offset"].value - b["offset"].value )
-
-class ExifIFD(FieldSet):
-    def seek(self, offset):
-        """
-        Seek to byte address relative to parent address.
-        """
-        padding = offset - (self.address + self.current_size)/8
-        if 0 < padding:
-            return createPaddingField(self, padding*8)
-        else:
-            return None
+class GPSIFDEntry(BasicIFDEntry):
+    TAG_INFO = {
+        0x0000: ("GPSVersionID", "GPS tag version"),
+        0x0001: ("GPSLatitudeRef", "North or South Latitude"),
+        0x0002: ("GPSLatitude", "Latitude"),
+        0x0003: ("GPSLongitudeRef", "East or West Longitude"),
+        0x0004: ("GPSLongitude", "Longitude"),
+        0x0005: ("GPSAltitudeRef", "Altitude reference"),
+        0x0006: ("GPSAltitude", "Altitude"),
+        0x0007: ("GPSTimeStamp", "GPS time (atomic clock)"),
+        0x0008: ("GPSSatellites", "GPS satellites used for measurement"),
+        0x0009: ("GPSStatus", "GPS receiver status"),
+        0x000A: ("GPSMeasureMode", "GPS measurement mode"),
+        0x000B: ("GPSDOP", "Measurement precision"),
+        0x000C: ("GPSSpeedRef", "Speed unit"),
+        0x000D: ("GPSSpeed", "Speed of GPS receiver"),
+        0x000E: ("GPSTrackRef", "Reference for direction of movement"),
+        0x000F: ("GPSTrack", "Direction of movement"),
+        0x0010: ("GPSImgDirectionRef", "Reference for direction of image"),
+        0x0011: ("GPSImgDirection", "Direction of image"),
+        0x0012: ("GPSMapDatum", "Geodetic survey data used"),
+        0x0013: ("GPSDestLatitudeRef", "Reference for latitude of destination"),
+        0x0014: ("GPSDestLatitude", "Latitude of destination"),
+        0x0015: ("GPSDestLongitudeRef", "Reference for longitude of destination"),
+        0x0016: ("GPSDestLongitude", "Longitude of destination"),
+        0x0017: ("GPSDestBearingRef", "Reference for bearing of destination"),
+        0x0018: ("GPSDestBearing", "Bearing of destination"),
+        0x0019: ("GPSDestDistanceRef", "Reference for distance to destination"),
+        0x001A: ("GPSDestDistance", "Distance to destination"),
+        0x001B: ("GPSProcessingMethod", "Name of GPS processing method"),
+        0x001C: ("GPSAreaInformation", "Name of GPS area"),
+        0x001D: ("GPSDateStamp", "GPS date"),
+        0x001E: ("GPSDifferential", "GPS differential correction"),
+    }
+
+class InteropIFDEntry(BasicIFDEntry):
+    TAG_INFO = {
+        0x0001: ("InteroperabilityIndex", "Interoperability Identification"),
+    }
+
+class IFD(SeekableFieldSet):
+    EntryClass = IFDEntry
+    def __init__(self, parent, name, base_addr):
+        self.base_addr = base_addr
+        SeekableFieldSet.__init__(self, parent, name)
 
     def createFields(self):
-        offset_diff = 6
         yield UInt16(self, "count", "Number of entries")
-        entries = []
-        next_chunk_offset = None
         count = self["count"].value
-        if not count:
-            return
-        while count:
-            addr = self.absolute_address + self.current_size
-            next = self.stream.readBits(addr, 32, NETWORK_ENDIAN)
-            if next in (0, 0xF0000000):
-                break
-            entry = ExifEntry(self, "entry[]")
-            yield entry
-            if entry["tag"].value in (ExifEntry.EXIF_IFD_POINTER, ExifEntry.OFFSET_JPEG_SOI):
-                next_chunk_offset = entry["value"].value + offset_diff
-            if 32 < entry.getSizes()[0]:
-                entries.append(entry)
-            count -= 1
-        yield UInt32(self, "next", "Next IFD offset")
-        try:
-            entries.sort( sortExifEntry )
-        except TypeError:
-            raise ParserError("Unable to sort entries!")
-        value_index = 0
-        for entry in entries:
-            padding = self.seek(entry["offset"].value + offset_diff)
-            if padding is not None:
-                yield padding
-
-            value_size, array_size = entry.getSizes()
-            if not array_size:
+        if count == 0:
+            raise ParserError("IFDs cannot be empty.")
+        for i in xrange(count):
+            yield self.EntryClass(self, "entry[]")
+        yield UInt32(self, "next", "Offset to next IFD")
+        for i in xrange(count):
+            entry = self['entry[%d]'%i]
+            if 'offset' not in entry:
                 continue
-            cls = entry.value_cls
-            if 1 < array_size:
-                name = "value_%s[]" % entry.name
+            self.seekByte(entry['offset'].value+self.base_addr//8, relative=False)
+            count = entry['count'].value
+            name = "value[%s]"%i
+            if issubclass(entry.value_cls, Bytes):
+                yield entry.value_cls(self, name, count)
             else:
-                name = "value_%s" % entry.name
-            desc = "Value of \"%s\"" % entry["tag"].display
-            if cls is String:
-                for index in xrange(array_size):
-                    yield cls(self, name, value_size/8, desc, strip=" \0", charset="ISO-8859-1")
-            elif cls is Bytes:
-                for index in xrange(array_size):
-                    yield cls(self, name, value_size/8, desc)
-            else:
-                for index in xrange(array_size):
-                    yield cls(self, name, desc)
-            value_index += 1
-        if next_chunk_offset is not None:
-            padding = self.seek(next_chunk_offset)
-            if padding is not None:
-                yield padding
-
-    def createDescription(self):
-        return "Exif IFD (id %s)" % self["id"].value
-
-class Exif(FieldSet):
+                if count > 1:
+                    name += "[]"
+                for i in xrange(count):
+                    yield entry.value_cls(self, name)
+
+    def getEntryValues(self, entry):
+        n = int(entry.name.rsplit('[',1)[1].strip(']'))
+        if 'offset' in entry:
+            field = 'value[%d]'%n
+            base = self
+        else:
+            field = 'value'
+            base = entry
+        if field in base:
+            return [base[field]]
+        else:
+            return base.array(field)
+
+class ExifIFD(IFD):
+    EntryClass = ExifIFDEntry
+
+class GPSIFD(IFD):
+    EntryClass = GPSIFDEntry
+
+class InteropIFD(IFD):
+    EntryClass = InteropIFDEntry
+
+IFD_TAGS = {
+    IFDEntry.EXIF_IFD_POINTER: ('exif', ExifIFD),
+    IFDEntry.GPS_IFD_POINTER: ('exif_gps', GPSIFD),
+    IFDEntry.INTEROP_IFD_POINTER: ('exif_interop', InteropIFD),
+}
+
+def TIFF(self):
+    iff_start = self.absolute_address + self.current_size
+    yield String(self, "endian", 2, "Endian ('II' or 'MM')", charset="ASCII")
+    if self["endian"].value not in ("II", "MM"):
+        raise ParserError("Invalid endian!")
+    if self["endian"].value == "II":
+       self.endian = LITTLE_ENDIAN
+    else:
+       self.endian = BIG_ENDIAN
+
+    yield UInt16(self, "version", "TIFF version number")
+    yield UInt32(self, "img_dir_ofs", "Next image directory offset")
+    offsets = [(self['img_dir_ofs'].value, 'ifd[]', IFD)]
+    while offsets:
+        offset, name, klass = offsets.pop(0)
+        self.seekByte(offset+iff_start//8, relative=False)
+        ifd = klass(self, name, iff_start)
+        yield ifd
+        for entry in ifd.array('entry'):
+            tag = entry['tag'].value
+            if tag in IFD_TAGS:
+                name, klass = IFD_TAGS[tag]
+                offsets.append((ifd.getEntryValues(entry)[0].value, name+'[]', klass))
+        if ifd['next'].value != 0:
+            offsets.append((ifd['next'].value, 'ifd[]', IFD))
+
+class Exif(SeekableFieldSet):
     def createFields(self):
         # Headers
         yield String(self, "header", 6, "Header (Exif\\0\\0)", charset="ASCII")
         if self["header"].value != "Exif\0\0":
             raise ParserError("Invalid EXIF signature!")
-        yield String(self, "byte_order", 2, "Byte order", charset="ASCII")
-        if self["byte_order"].value not in ("II", "MM"):
-            raise ParserError("Invalid endian!")
-        if self["byte_order"].value == "II":
-           self.endian = LITTLE_ENDIAN
-        else:
-           self.endian = BIG_ENDIAN
-        yield UInt16(self, "version", "TIFF version number")
-        yield UInt32(self, "img_dir_ofs", "Next image directory offset")
-        while not self.eof:
-            addr = self.absolute_address + self.current_size
-            tag = self.stream.readBits(addr, 16, NETWORK_ENDIAN)
-            if tag == 0xFFD8:
-                size = (self._size - self.current_size) // 8
-                yield SubFile(self, "thumbnail", size, "Thumbnail (JPEG file)", mime_type="image/jpeg")
-                break
-            elif tag == 0xFFFF:
-                break
-            yield ExifIFD(self, "ifd[]", "IFD")
-        padding = self.seekBit(self._size)
-        if padding is not None:
-            yield padding
-
-
+        iff_start = self.absolute_address + self.current_size
+        ifds = []
+        for field in TIFF(self):
+            yield field
+            if isinstance(field, IFD):
+                ifds.append(field)
+
+        for ifd in ifds:
+            data = {}
+            for i, entry in enumerate(ifd.array('entry')):
+                data[entry['tag'].display] = entry
+            if 'JPEGInterchangeFormat' in data and 'JPEGInterchangeFormatLength' in data:
+                offs = ifd.getEntryValues(data['JPEGInterchangeFormat'])[0].value
+                size = ifd.getEntryValues(data['JPEGInterchangeFormatLength'])[0].value
+                if size == 0: continue
+                self.seekByte(offs + iff_start//8, relative=False)
+                yield SubFile(self, "thumbnail[]", size, "Thumbnail (JPEG file)", mime_type="image/jpeg")
diff --git a/lib/hachoir_parser/image/gif.py b/lib/hachoir_parser/image/gif.py
index c7e0b89c33ba4c1c0b451e6b9c91dee5dc6f3b98..b870b6739d1bcf3597c7c19bbe69822731e6aad3 100644
--- a/lib/hachoir_parser/image/gif.py
+++ b/lib/hachoir_parser/image/gif.py
@@ -1,7 +1,12 @@
 """
 GIF picture parser.
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
+
+- GIF format
+  http://local.wasp.uwa.edu.au/~pbourke/dataformats/gif/
+- LZW compression
+  http://en.wikipedia.org/wiki/LZW
 """
 
 from hachoir_parser import Parser
@@ -12,7 +17,8 @@ from hachoir_core.field import (FieldSet, ParserError,
     NullBits, RawBytes)
 from hachoir_parser.image.common import PaletteRGB
 from hachoir_core.endian import LITTLE_ENDIAN
-from hachoir_core.tools import humanDuration
+from hachoir_core.stream import StringInputStream
+from hachoir_core.tools import humanDuration, paddingSize
 from hachoir_core.text_handler import textHandler, displayHandler, hexadecimal
 
 # Maximum image dimension (in pixel)
@@ -20,6 +26,137 @@ MAX_WIDTH = 6000
 MAX_HEIGHT = MAX_WIDTH
 MAX_FILE_SIZE = 100 * 1024 * 1024
 
+class FragmentGroup:
+    def __init__(self, parser):
+        self.items = []
+        self.parser = parser
+        self.args = {}
+
+    def add(self, item):
+        self.items.append(item)
+
+    def createInputStream(self):
+        # FIXME: Use lazy stream creation
+        data = []
+        for item in self.items:
+            data.append( item["rawdata"].value )
+        data = "".join(data)
+
+        # FIXME: Use smarter code to send arguments
+        self.args["startbits"] = self.items[0].parent["lzw_min_code_size"].value
+        tags = {"class": self.parser, "args": self.args}
+        tags = tags.iteritems()
+        return StringInputStream(data, "<fragment group>", tags=tags)
+
+class CustomFragment(FieldSet):
+    def __init__(self, parent, name, size, parser, description=None, group=None):
+        FieldSet.__init__(self, parent, name, description, size=size)
+        if not group:
+            group = FragmentGroup(parser)
+        self.group = group
+        self.group.add(self)
+
+    def createFields(self):
+        yield UInt8(self, "size")
+        yield RawBytes(self, "rawdata", self["size"].value)
+
+    def _createInputStream(self, **args):
+        return self.group.createInputStream()
+
+def rle_repr(l):
+    """Run-length encode a list into an "eval"-able form
+
+    Example:
+    >>> rle_repr([20, 16, 16, 16, 16, 16, 18, 18, 65])
+    '[20] + [16]*5 + [18]*2 + [65]'
+
+    Adapted from http://twistedmatrix.com/trac/browser/trunk/twisted/python/dxprofile.py
+    """
+    def add_rle(previous, runlen, result):
+        if isinstance(previous, (list, tuple)):
+            previous = rle_repr(previous)
+        if runlen>1:
+            result.append('[%s]*%i'%(previous, runlen))
+        else:
+            if result and '*' not in result[-1]:
+                result[-1] = '[%s, %s]'%(result[-1][1:-1], previous)
+            else:
+                result.append('[%s]'%previous)
+    iterable = iter(l)
+    runlen = 1
+    result = []
+    try:
+        previous = iterable.next()
+    except StopIteration:
+        return "[]"
+    for element in iterable:
+        if element == previous:
+            runlen = runlen + 1
+            continue
+        else:
+            add_rle(previous, runlen, result)
+            previous = element
+            runlen = 1
+    add_rle(previous, runlen, result)
+    return ' + '.join(result)
+
+class GifImageBlock(Parser):
+    endian = LITTLE_ENDIAN
+    def createFields(self):
+        dictionary = {}
+        self.nbits = self.startbits
+        CLEAR_CODE = 2**self.nbits
+        END_CODE = CLEAR_CODE + 1
+        compress_code = CLEAR_CODE + 2
+        obuf = []
+        output = []
+        while True:
+            if compress_code >= 2**self.nbits:
+                self.nbits += 1
+            code = Bits(self, "code[]", self.nbits)
+            if code.value == CLEAR_CODE:
+                if compress_code == 2**(self.nbits-1):
+                    # this fixes a bizarre edge case where the reset code could
+                    # appear just after the bits incremented. Apparently, the
+                    # correct behaviour is to express the reset code with the
+                    # old number of bits, not the new...
+                    code = Bits(self, "code[]", self.nbits-1)
+                self.nbits = self.startbits + 1
+                dictionary = {}
+                compress_code = CLEAR_CODE + 2
+                obuf = []
+                code._description = "Reset Code (LZW code %i)" % code.value
+                yield code
+                continue
+            elif code.value == END_CODE:
+                code._description = "End of Information Code (LZW code %i)" % code.value
+                yield code
+                break
+            if code.value < CLEAR_CODE: # literal
+                if obuf:
+                    chain = obuf + [code.value]
+                    dictionary[compress_code] = chain
+                    compress_code += 1
+                obuf = [code.value]
+                output.append(code.value)
+                code._description = "Literal Code %i" % code.value
+            elif code.value >= CLEAR_CODE + 2:
+                if code.value in dictionary:
+                    chain = dictionary[code.value]
+                    code._description = "Compression Code %i (found in dictionary as %s)" % (code.value, rle_repr(chain))
+                else:
+                    chain = obuf + [obuf[0]]
+                    code._description = "Compression Code %i (not found in dictionary; guessed to be %s)" % (code.value, rle_repr(chain))
+                dictionary[compress_code] = obuf + [chain[0]]
+                compress_code += 1
+                obuf = chain
+                output += chain
+            code._description += "; Current Decoded Length %i"%len(output)
+            yield code
+        padding = paddingSize(self.current_size, 8)
+        if padding:
+            yield NullBits(self, "padding[]", padding)
+
 class Image(FieldSet):
     def createFields(self):
         yield UInt16(self, "left", "Left")
@@ -27,24 +164,26 @@ class Image(FieldSet):
         yield UInt16(self, "width", "Width")
         yield UInt16(self, "height", "Height")
 
-        yield Bits(self, "bpp", 3, "Bits / pixel minus one")
-        yield NullBits(self, "nul", 2)
-        yield Bit(self, "sorted", "Sorted??")
+        yield Bits(self, "size_local_map", 3, "log2(size of local map) minus one")
+        yield NullBits(self, "reserved", 2)
+        yield Bit(self, "sort_flag", "Is the local map sorted by decreasing importance?")
         yield Bit(self, "interlaced", "Interlaced?")
         yield Bit(self, "has_local_map", "Use local color map?")
 
         if self["has_local_map"].value:
-            nb_color = 1 << (1 + self["bpp"].value)
+            nb_color = 1 << (1 + self["size_local_map"].value)
             yield PaletteRGB(self, "local_map", nb_color, "Local color map")
 
-        yield UInt8(self, "code_size", "LZW Minimum Code Size")
+        yield UInt8(self, "lzw_min_code_size", "LZW Minimum Code Size")
+        group = None
         while True:
-            blen = UInt8(self, "block_len[]", "Block Length")
-            yield blen
-            if blen.value != 0:
-                yield RawBytes(self, "data[]", blen.value, "Image Data")
-            else:
+            size = UInt8(self, "block_size")
+            if size.value == 0:
                 break
+            block = CustomFragment(self, "image_block[]", None, GifImageBlock, "GIF Image Block", group)
+            group = block.group
+            yield block
+        yield NullBytes(self, "terminator", 1, "Terminator (0)")
 
     def createDescription(self):
         return "Image: %ux%u pixels at (%u,%u)" % (
@@ -64,16 +203,19 @@ NETSCAPE_CODE = {
 
 def parseApplicationExtension(parent):
     yield PascalString8(parent, "app_name", "Application name")
-    yield UInt8(parent, "size")
-    size = parent["size"].value
-    if parent["app_name"].value == "NETSCAPE2.0" and size == 3:
-        yield Enum(UInt8(parent, "netscape_code"), NETSCAPE_CODE)
-        if parent["netscape_code"].value == 1:
-            yield UInt16(parent, "loop_count")
+    while True:
+        size = UInt8(parent, "size[]")
+        if size.value == 0:
+            break
+        yield size
+        if parent["app_name"].value == "NETSCAPE2.0" and size.value == 3:
+            yield Enum(UInt8(parent, "netscape_code"), NETSCAPE_CODE)
+            if parent["netscape_code"].value == 1:
+                yield UInt16(parent, "loop_count")
+            else:
+                yield RawBytes(parent, "raw[]", 2)
         else:
-            yield RawBytes(parent, "raw", 2)
-    else:
-        yield RawBytes(parent, "raw", size)
+            yield RawBytes(parent, "raw[]", size.value)
     yield NullBytes(parent, "terminator", 1, "Terminator (0)")
 
 def parseGraphicControl(parent):
@@ -149,15 +291,20 @@ class ScreenDescriptor(FieldSet):
     def createFields(self):
         yield UInt16(self, "width", "Width")
         yield UInt16(self, "height", "Height")
-        yield Bits(self, "bpp", 3, "Bits per pixel minus one")
-        yield Bit(self, "reserved", "(reserved)")
+        yield Bits(self, "size_global_map", 3, "log2(size of global map) minus one")
+        yield Bit(self, "sort_flag", "Is the global map sorted by decreasing importance?")
         yield Bits(self, "color_res", 3, "Color resolution minus one")
         yield Bit(self, "global_map", "Has global map?")
         yield UInt8(self, "background", "Background color")
-        yield UInt8(self, "pixel_aspect_ratio", "Pixel Aspect Ratio")
+        field = UInt8(self, "pixel_aspect_ratio")
+        if field.value:
+            field._description = "Pixel aspect ratio: %f (stored as %i)"%((field.value + 15)/64., field.value)
+        else:
+            field._description = "Pixel aspect ratio: not specified"
+        yield field
 
     def createDescription(self):
-        colors = 1 << (self["bpp"].value+1)
+        colors = 1 << (self["size_global_map"].value+1)
         return "Screen descriptor: %ux%u pixels %u colors" \
             % (self["width"].value, self["height"].value, colors)
 
@@ -196,7 +343,7 @@ class GifFile(Parser):
 
         yield ScreenDescriptor(self, "screen")
         if self["screen/global_map"].value:
-            bpp = (self["screen/bpp"].value+1)
+            bpp = (self["screen/size_global_map"].value+1)
             yield PaletteRGB(self, "color_map", 1 << bpp, "Color map")
             self.color_map = self["color_map"]
         else:
diff --git a/lib/hachoir_parser/image/jpeg.py b/lib/hachoir_parser/image/jpeg.py
index 30944aae724ca276475ea47a23a535b4ba9e1b27..4a361962d5c5468aba24d98596fc6b2e22426e51 100644
--- a/lib/hachoir_parser/image/jpeg.py
+++ b/lib/hachoir_parser/image/jpeg.py
@@ -8,21 +8,25 @@ Information:
   http://java.sun.com/j2se/1.5.0/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color
 - APP12:
   http://search.cpan.org/~exiftool/Image-ExifTool/lib/Image/ExifTool/TagNames.pod
+- JPEG Data Format
+  http://www.w3.org/Graphics/JPEG/itu-t81.pdf
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
 """
 
 from hachoir_core.error import HachoirError
 from hachoir_parser import Parser
-from hachoir_core.field import (FieldSet, ParserError,
-    UInt8, UInt16, Enum,
-    Bit, Bits, NullBits, NullBytes,
+from hachoir_core.field import (FieldSet, ParserError, FieldError,
+    UInt8, UInt16, Enum, Field,
+    Bit, Bits, NullBits, NullBytes, PaddingBits,
     String, RawBytes)
 from hachoir_parser.image.common import PaletteRGB
 from hachoir_core.endian import BIG_ENDIAN
 from hachoir_core.text_handler import textHandler, hexadecimal
 from hachoir_parser.image.exif import Exif
 from hachoir_parser.image.photoshop_metadata import PhotoshopMetadata
+from hachoir_parser.archive.zlib import build_tree
+from hachoir_core.tools import paddingSize, alignValue
 
 MAX_FILESIZE = 100 * 1024 * 1024
 
@@ -144,6 +148,13 @@ class APP12(FieldSet):
         while not self.eof:
             yield Ducky(self, "item[]")
 
+class SOFComponent(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "component_id")
+        yield Bits(self, "horiz_sample", 4, "Horizontal sampling factor")
+        yield Bits(self, "vert_sample", 4, "Vertical sampling factor")
+        yield UInt8(self, "quant_table", "Quantization table destination selector")
+
 class StartOfFrame(FieldSet):
     def createFields(self):
         yield UInt8(self, "precision")
@@ -153,9 +164,7 @@ class StartOfFrame(FieldSet):
         yield UInt8(self, "nr_components")
 
         for index in range(self["nr_components"].value):
-            yield UInt8(self, "component_id[]")
-            yield UInt8(self, "high[]")
-            yield UInt8(self, "low[]")
+            yield SOFComponent(self, "component[]")
 
 class Comment(FieldSet):
     def createFields(self):
@@ -178,17 +187,25 @@ class AdobeChunk(FieldSet):
         yield NullBytes(self, "flags1", 2)
         yield Enum(UInt8(self, "color_transform", "Colorspace transformation code"), self.COLORSPACE_TRANSFORMATION)
 
+class SOSComponent(FieldSet):
+    def createFields(self):
+        comp_id = UInt8(self, "component_id")
+        yield comp_id
+        if not(1 <= comp_id.value <= self["../nr_components"].value):
+           raise ParserError("JPEG error: Invalid component-id")
+        yield Bits(self, "dc_coding_table", 4, "DC entropy coding table destination selector")
+        yield Bits(self, "ac_coding_table", 4, "AC entropy coding table destination selector")
+
 class StartOfScan(FieldSet):
     def createFields(self):
         yield UInt8(self, "nr_components")
 
         for index in range(self["nr_components"].value):
-            comp_id = UInt8(self, "component_id[]")
-            yield comp_id
-            if not(1 <= comp_id.value <= self["nr_components"].value):
-               raise ParserError("JPEG error: Invalid component-id")
-            yield UInt8(self, "value[]")
-        yield RawBytes(self, "raw", 3) # TODO: What's this???
+            yield SOSComponent(self, "component[]")
+        yield UInt8(self, "spectral_start", "Start of spectral or predictor selection")
+        yield UInt8(self, "spectral_end", "End of spectral selection")
+        yield Bits(self, "bit_pos_high", 4, "Successive approximation bit position high")
+        yield Bits(self, "bit_pos_low", 4, "Successive approximation bit position low or point transform")
 
 class RestartInterval(FieldSet):
     def createFields(self):
@@ -217,6 +234,182 @@ class DefineQuantizationTable(FieldSet):
         while self.current_size < self.size:
             yield QuantizationTable(self, "qt[]")
 
+class HuffmanTable(FieldSet):
+    def createFields(self):
+        # http://www.w3.org/Graphics/JPEG/itu-t81.pdf, page 40-41
+        yield Enum(Bits(self, "table_class", 4, "Table class"), {
+            0:"DC or Lossless Table",
+            1:"AC Table"})
+        yield Bits(self, "index", 4, "Huffman table destination identifier")
+        for i in xrange(1, 17):
+            yield UInt8(self, "count[%i]" % i, "Number of codes of length %i" % i)
+        lengths = []
+        remap = {}
+        for i in xrange(1, 17):
+            for j in xrange(self["count[%i]" % i].value):
+                field = UInt8(self, "value[%i][%i]" % (i, j), "Value of code #%i of length %i" % (j, i))
+                yield field
+                remap[len(lengths)] = field.value
+                lengths.append(i)
+        self.tree = {}
+        for i,j in build_tree(lengths).iteritems():
+            self.tree[i] = remap[j]
+
+class DefineHuffmanTable(FieldSet):
+    def createFields(self):
+        while self.current_size < self.size:
+            yield HuffmanTable(self, "huffman_table[]")
+
+class HuffmanCode(Field):
+    """Huffman code. Uses tree parameter as the Huffman tree."""
+    def __init__(self, parent, name, tree, description=""):
+        Field.__init__(self, parent, name, 0, description)
+
+        endian = self.parent.endian
+        stream = self.parent.stream
+        addr = self.absolute_address
+
+        value = 0
+        met_ff = False
+        while (self.size, value) not in tree:
+            if addr % 8 == 0:
+                last_byte = stream.readBytes(addr - 8, 1)
+                if last_byte == '\xFF':
+                    next_byte = stream.readBytes(addr, 1)
+                    if next_byte != '\x00':
+                        raise FieldError("Unexpected byte sequence %r!"%(last_byte + next_byte))
+                    addr += 8 # hack hack hack
+                    met_ff = True
+                    self._description = "[skipped 8 bits after 0xFF] "
+            bit = stream.readBits(addr, 1, endian)
+            value <<= 1
+            value += bit
+            self._size += 1
+            addr += 1
+        self.createValue = lambda: value
+        self.realvalue = tree[(self.size, value)]
+        if met_ff:
+            self._size += 8
+
+class JpegHuffmanImageUnit(FieldSet):
+    """8x8 block of sample/coefficient values"""
+    def __init__(self, parent, name, dc_tree, ac_tree, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.dc_tree = dc_tree
+        self.ac_tree = ac_tree
+
+    def createFields(self):
+        field = HuffmanCode(self, "dc_data", self.dc_tree)
+        field._description = "DC Code %i (Huffman Code %i)" % (field.realvalue, field.value) + field._description
+        yield field
+        if field.realvalue != 0:
+            extra = Bits(self, "dc_data_extra", field.realvalue)
+            if extra.value < 2**(field.realvalue - 1):
+                corrected_value = extra.value + (-1 << field.realvalue) + 1
+            else:
+                corrected_value = extra.value
+            extra._description = "Extra Bits: Corrected DC Value %i" % corrected_value
+            yield extra
+        data = []
+        while len(data) < 63:
+            field = HuffmanCode(self, "ac_data[]", self.ac_tree)
+            value_r = field.realvalue >> 4
+            if value_r:
+                data += [0] * value_r
+            value_s = field.realvalue & 0x0F
+            if value_r == value_s == 0:
+                field._description = "AC Code Block Terminator (0, 0) (Huffman Code %i)" % field.value + field._description
+                yield field
+                return
+            field._description = "AC Code %i, %i (Huffman Code %i)" % (value_r, value_s, field.value) + field._description
+            yield field
+            if value_s != 0:
+                extra = Bits(self, "ac_data_extra[%s" % field.name.split('[')[1], value_s)
+                if extra.value < 2**(value_s - 1):
+                    corrected_value = extra.value + (-1 << value_s) + 1
+                else:
+                    corrected_value = extra.value
+                extra._description = "Extra Bits: Corrected AC Value %i" % corrected_value
+                data.append(corrected_value)
+                yield extra
+            else:
+                data.append(0)
+
+class JpegImageData(FieldSet):
+    def __init__(self, parent, name, frame, scan, restart_interval, restart_offset=0, *args, **kwargs):
+        FieldSet.__init__(self, parent, name, *args, **kwargs)
+        self.frame = frame
+        self.scan = scan
+        self.restart_interval = restart_interval
+        self.restart_offset = restart_offset
+        # try to figure out where this field ends
+        start = self.absolute_address
+        while True:
+            end = self.stream.searchBytes("\xff", start, MAX_FILESIZE*8)
+            if end is None:
+                # this is a bad sign, since it means there is no terminator
+                # we ignore this; it likely means a truncated image
+                break
+            if self.stream.readBytes(end, 2) == '\xff\x00':
+                # padding: false alarm
+                start=end+16
+                continue
+            else:
+                self._size = end-self.absolute_address
+                break
+
+    def createFields(self):
+        if self.frame["../type"].value in [0xC0, 0xC1]:
+            # yay, huffman coding!
+            if not hasattr(self, "huffman_tables"):
+                self.huffman_tables = {}
+                for huffman in self.parent.array("huffman"):
+                    for table in huffman["content"].array("huffman_table"):
+                        for _dummy_ in table:
+                            # exhaust table, so the huffman tree is built
+                            pass
+                        self.huffman_tables[table["table_class"].value, table["index"].value] = table.tree
+            components = [] # sos_comp, samples
+            max_vert = 0
+            max_horiz = 0
+            for component in self.scan.array("component"):
+                for sof_comp in self.frame.array("component"):
+                    if sof_comp["component_id"].value == component["component_id"].value:
+                        vert = sof_comp["vert_sample"].value
+                        horiz = sof_comp["horiz_sample"].value
+                        components.append((component, vert * horiz))
+                        max_vert = max(max_vert, vert)
+                        max_horiz = max(max_horiz, horiz)
+            mcu_height = alignValue(self.frame["height"].value, 8 * max_vert) // (8 * max_vert)
+            mcu_width = alignValue(self.frame["width"].value, 8 * max_horiz) // (8 * max_horiz)
+            if self.restart_interval and self.restart_offset > 0:
+                mcu_number = self.restart_interval * self.restart_offset
+            else:
+                mcu_number = 0
+            initial_mcu = mcu_number
+            while True:
+                if (self.restart_interval and mcu_number != initial_mcu and mcu_number % self.restart_interval == 0) or\
+                   mcu_number == mcu_height * mcu_width:
+                    padding = paddingSize(self.current_size, 8)
+                    if padding:
+                        yield PaddingBits(self, "padding[]", padding) # all 1s
+                    last_byte = self.stream.readBytes(self.absolute_address + self.current_size - 8, 1)
+                    if last_byte == '\xFF':
+                        next_byte = self.stream.readBytes(self.absolute_address + self.current_size, 1)
+                        if next_byte != '\x00':
+                            raise FieldError("Unexpected byte sequence %r!"%(last_byte + next_byte))
+                        yield NullBytes(self, "stuffed_byte[]", 1)
+                    break
+                for sos_comp, num_units in components:
+                    for interleave_count in range(num_units):
+                        yield JpegHuffmanImageUnit(self, "block[%i]component[%i][]" % (mcu_number, sos_comp["component_id"].value),
+                                              self.huffman_tables[0, sos_comp["dc_coding_table"].value],
+                                              self.huffman_tables[1, sos_comp["ac_coding_table"].value])
+                mcu_number += 1
+        else:
+            self.warning("Sorry, only supporting Baseline & Extended Sequential JPEG images so far!")
+            return
+
 class JpegChunk(FieldSet):
     TAG_SOI = 0xD8
     TAG_EOI = 0xD9
@@ -224,10 +417,18 @@ class JpegChunk(FieldSet):
     TAG_DQT = 0xDB
     TAG_DRI = 0xDD
     TAG_INFO = {
-        0xC4: ("huffman[]", "Define Huffman Table (DHT)", None),
+        0xC4: ("huffman[]", "Define Huffman Table (DHT)", DefineHuffmanTable),
         0xD8: ("start_image", "Start of image (SOI)", None),
         0xD9: ("end_image", "End of image (EOI)", None),
-        0xDA: ("start_scan", "Start Of Scan (SOS)", StartOfScan),
+        0xD0: ("restart_marker_0[]", "Restart Marker (RST0)", None),
+        0xD1: ("restart_marker_1[]", "Restart Marker (RST1)", None),
+        0xD2: ("restart_marker_2[]", "Restart Marker (RST2)", None),
+        0xD3: ("restart_marker_3[]", "Restart Marker (RST3)", None),
+        0xD4: ("restart_marker_4[]", "Restart Marker (RST4)", None),
+        0xD5: ("restart_marker_5[]", "Restart Marker (RST5)", None),
+        0xD6: ("restart_marker_6[]", "Restart Marker (RST6)", None),
+        0xD7: ("restart_marker_7[]", "Restart Marker (RST7)", None),
+        0xDA: ("start_scan[]", "Start Of Scan (SOS)", StartOfScan),
         0xDB: ("quantization[]", "Define Quantization Table (DQT)", DefineQuantizationTable),
         0xDC: ("nb_line", "Define number of Lines (DNL)", None),
         0xDD: ("restart_interval", "Define Restart Interval (DRI)", RestartInterval),
@@ -280,7 +481,7 @@ class JpegChunk(FieldSet):
             raise ParserError("JPEG: Invalid chunk header!")
         yield textHandler(UInt8(self, "type", "Type"), hexadecimal)
         tag = self["type"].value
-        if tag in (self.TAG_SOI, self.TAG_EOI):
+        if tag in [self.TAG_SOI, self.TAG_EOI] + range(0xD0, 0xD8): # D0 - D7 inclusive are the restart markers
             return
         yield UInt16(self, "size", "Size")
         size = (self["size"].value - 2)
@@ -326,12 +527,31 @@ class JpegFile(Parser):
         return True
 
     def createFields(self):
+        frame = None
+        scan = None
+        restart_interval = None
+        restart_offset = 0
         while not self.eof:
             chunk = JpegChunk(self, "chunk[]")
             yield chunk
+            if chunk["type"].value in JpegChunk.START_OF_FRAME:
+                if chunk["type"].value not in [0xC0, 0xC1]: # SOF0 [Baseline], SOF1 [Extended Sequential]
+                    self.warning("Only supporting Baseline & Extended Sequential JPEG images so far!")
+                frame = chunk["content"]
             if chunk["type"].value == JpegChunk.TAG_SOS:
-                # TODO: Read JPEG image data...
-                break
+                if not frame:
+                    self.warning("Missing or invalid SOF marker before SOS!")
+                    continue
+                scan = chunk["content"]
+                # hack: scan only the fields seen so far (in _fields): don't use the generator
+                if "restart_interval" in self._fields:
+                    restart_interval = self["restart_interval/content/interval"].value
+                else:
+                    restart_interval = None
+                yield JpegImageData(self, "image_data[]", frame, scan, restart_interval)
+            elif chunk["type"].value in range(0xD0, 0xD8):
+                restart_offset += 1
+                yield JpegImageData(self, "image_data[]", frame, scan, restart_interval, restart_offset)
 
         # TODO: is it possible to handle piped input?
         if self._size is None:
@@ -350,8 +570,8 @@ class JpegFile(Parser):
 
     def createDescription(self):
         desc = "JPEG picture"
-        if "sof/content" in self:
-            header = self["sof/content"]
+        if "start_frame/content" in self:
+            header = self["start_frame/content"]
             desc += ": %ux%u pixels" % (header["width"].value, header["height"].value)
         return desc
 
@@ -365,4 +585,3 @@ class JpegFile(Parser):
         if end is not None:
             return end + 16
         return None
-
diff --git a/lib/hachoir_parser/image/photoshop_metadata.py b/lib/hachoir_parser/image/photoshop_metadata.py
index be660cec0c76fcecbd950bd85cb959400c9093f9..15fed72691850cebbdb5c105532f2c9358fb77b9 100644
--- a/lib/hachoir_parser/image/photoshop_metadata.py
+++ b/lib/hachoir_parser/image/photoshop_metadata.py
@@ -1,12 +1,20 @@
+""" Photoshop metadata parser.
+
+References:
+- http://www.scribd.com/doc/32900475/Photoshop-File-Formats
+"""
+
 from hachoir_core.field import (FieldSet, ParserError,
-    UInt8, UInt16, UInt32,
-    String, CString, PascalString8,
+    UInt8, UInt16, UInt32, Float32, Enum,
+    SubFile, String, CString, PascalString8,
     NullBytes, RawBytes)
 from hachoir_core.text_handler import textHandler, hexadecimal
 from hachoir_core.tools import alignValue, createDict
 from hachoir_parser.image.iptc import IPTC
 from hachoir_parser.common.win32 import PascalStringWin32
 
+BOOL = {0: False, 1: True}
+
 class Version(FieldSet):
     def createFields(self):
         yield UInt32(self, "version")
@@ -18,25 +26,102 @@ class Version(FieldSet):
         if size:
             yield NullBytes(self, "padding", size)
 
+class FixedFloat32(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "int_part")
+        yield UInt16(self, "float_part")
+
+    def createValue(self):
+        return self["int_part"].value +  float(self["float_part"].value) / (1<<16)
+
+class ResolutionInfo(FieldSet):
+    def createFields(self):
+        yield FixedFloat32(self, "horiz_res")
+        yield Enum(UInt16(self, "horiz_res_unit"), {1:'px/in', 2:'px/cm'})
+        yield Enum(UInt16(self, "width_unit"), {1:'inches', 2:'cm', 3:'points', 4:'picas', 5:'columns'})
+        yield FixedFloat32(self, "vert_res")
+        yield Enum(UInt16(self, "vert_res_unit"), {1:'px/in', 2:'px/cm'})
+        yield Enum(UInt16(self, "height_unit"), {1:'inches', 2:'cm', 3:'points', 4:'picas', 5:'columns'})
+
+class PrintScale(FieldSet):
+    def createFields(self):
+        yield Enum(UInt16(self, "style"), {0:'centered', 1:'size to fit', 2:'user defined'})
+        yield Float32(self, "x_location")
+        yield Float32(self, "y_location")
+        yield Float32(self, "scale")
+
+class PrintFlags(FieldSet):
+    def createFields(self):
+        yield Enum(UInt8(self, "labels"), BOOL)
+        yield Enum(UInt8(self, "crop_marks"), BOOL)
+        yield Enum(UInt8(self, "color_bars"), BOOL)
+        yield Enum(UInt8(self, "reg_marks"), BOOL)
+        yield Enum(UInt8(self, "negative"), BOOL)
+        yield Enum(UInt8(self, "flip"), BOOL)
+        yield Enum(UInt8(self, "interpolate"), BOOL)
+        yield Enum(UInt8(self, "caption"), BOOL)
+        yield Enum(UInt8(self, "print_flags"), BOOL)
+        yield Enum(UInt8(self, "unknown"), BOOL)
+
+    def createValue(self):
+        return [field.name for field in self if field.value]
+
+    def createDisplay(self):
+        return ', '.join(self.value)
+
+class PrintFlags2(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "version")
+        yield UInt8(self, "center_crop_marks")
+        yield UInt8(self, "reserved")
+        yield UInt32(self, "bleed_width")
+        yield UInt16(self, "bleed_width_scale")
+
+class GridGuides(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "version")
+        yield UInt32(self, "horiz_cycle", "Horizontal grid spacing, in quarter inches")
+        yield UInt32(self, "vert_cycle", "Vertical grid spacing, in quarter inches")
+        yield UInt32(self, "guide_count", "Number of guide resource blocks (can be 0)")
+
+class Thumbnail(FieldSet):
+    def createFields(self):
+        yield Enum(UInt32(self, "format"), {0:'Raw RGB', 1:'JPEG RGB'})
+        yield UInt32(self, "width", "Width of thumbnail in pixels")
+        yield UInt32(self, "height", "Height of thumbnail in pixels")
+        yield UInt32(self, "widthbytes", "Padded row bytes = (width * bits per pixel + 31) / 32 * 4")
+        yield UInt32(self, "uncompressed_size", "Total size = widthbytes * height * planes")
+        yield UInt32(self, "compressed_size", "Size after compression. Used for consistency check")
+        yield UInt16(self, "bits_per_pixel")
+        yield UInt16(self, "num_planes")
+        yield SubFile(self, "thumbnail", self['compressed_size'].value, "Thumbnail (JPEG file)", mime_type="image/jpeg")
+
 class Photoshop8BIM(FieldSet):
     TAG_INFO = {
-        0x03ed: ("res_info", None, "Resolution information"),
-        0x03f3: ("print_flag", None, "Print flags: labels, crop marks, colour bars, etc."),
+        0x03ed: ("res_info", ResolutionInfo, "Resolution information"),
+        0x03f3: ("print_flag", PrintFlags, "Print flags: labels, crop marks, colour bars, etc."),
         0x03f5: ("col_half_info", None, "Colour half-toning information"),
         0x03f8: ("color_trans_func", None, "Colour transfer function"),
         0x0404: ("iptc", IPTC, "IPTC/NAA"),
         0x0406: ("jpeg_qual", None, "JPEG quality"),
-        0x0408: ("grid_guide", None, "Grid guides informations"),
-        0x040a: ("copyright_flag", None, "Copyright flag"),
-        0x040c: ("thumb_res2", None, "Thumbnail resource (2)"),
-        0x040d: ("glob_angle", None, "Global lighting angle for effects"),
+        0x0408: ("grid_guide", GridGuides, "Grid guides informations"),
+        0x0409: ("thumb_res", Thumbnail, "Thumbnail resource (PS 4.0)"),
+        0x0410: ("watermark", UInt8, "Watermark"),
+        0x040a: ("copyright_flag", UInt8, "Copyright flag"),
+        0x040b: ("url", None, "URL"),
+        0x040c: ("thumb_res2", Thumbnail, "Thumbnail resource (PS 5.0)"),
+        0x040d: ("glob_angle", UInt32, "Global lighting angle for effects"),
         0x0411: ("icc_tagged", None, "ICC untagged (1 means intentionally untagged)"),
-        0x0414: ("base_layer_id", None, "Base value for new layers ID's"),
-        0x0419: ("glob_altitude", None, "Global altitude"),
+        0x0414: ("base_layer_id", UInt32, "Base value for new layers ID's"),
+        0x0416: ("indexed_colors", UInt16, "Number of colors in table that are actually defined"),
+        0x0417: ("transparency_index", UInt16, "Index of transparent color"),
+        0x0419: ("glob_altitude", UInt32, "Global altitude"),
         0x041a: ("slices", None, "Slices"),
-        0x041e: ("url_list", None, "Unicode URL's"),
+        0x041e: ("url_list", None, "Unicode URLs"),
         0x0421: ("version", Version, "Version information"),
-        0x2710: ("print_flag2", None, "Print flags (2)"),
+        0x0425: ("caption_digest", None, "16-byte MD5 caption digest"),
+        0x0426: ("printscale", PrintScale, "Printer scaling"),
+        0x2710: ("print_flag2", PrintFlags2, "Print flags (2)"),
     }
     TAG_NAME = createDict(TAG_INFO, 0)
     CONTENT_HANDLER = createDict(TAG_INFO, 1)
@@ -67,7 +152,10 @@ class Photoshop8BIM(FieldSet):
         if not size:
             return
         if self.handler:
-            yield self.handler(self, "content", size=size*8)
+            if issubclass(self.handler, FieldSet):
+                yield self.handler(self, "content", size=size*8)
+            else:
+                yield self.handler(self, "content")
         else:
             yield RawBytes(self, "content", size)
 
diff --git a/lib/hachoir_parser/image/png.py b/lib/hachoir_parser/image/png.py
index 66f1688e5ca630a2c7f116a984d0629ed77cd543..acbfc8500488db527337ca7f3234cf6488d194a3 100644
--- a/lib/hachoir_parser/image/png.py
+++ b/lib/hachoir_parser/image/png.py
@@ -22,7 +22,7 @@ from hachoir_core.endian import NETWORK_ENDIAN
 from hachoir_core.tools import humanFilesize
 from datetime import datetime
 
-MAX_FILESIZE = 500 * 1024 * 1024
+MAX_FILESIZE = 500 * 1024 * 1024 # 500 MB
 
 try:
     from zlib import decompressobj
@@ -44,7 +44,7 @@ UNIT_NAME = {1: "Meter"}
 COMPRESSION_NAME = {
     0: u"deflate" # with 32K sliding window
 }
-MAX_CHUNK_SIZE = 500 * 1024 # Maximum chunk size (500 KB)
+MAX_CHUNK_SIZE = 5 * 1024 * 1024 # Maximum chunk size (5 MB)
 
 def headerParse(parent):
     yield UInt32(parent, "width", "Width (pixels)")
diff --git a/lib/hachoir_parser/image/tiff.py b/lib/hachoir_parser/image/tiff.py
index a096212f50ba279f1188f4cf449e4f46c913e936..30dedd8b127a3dffa0354f81aa9cca7268d03394 100644
--- a/lib/hachoir_parser/image/tiff.py
+++ b/lib/hachoir_parser/image/tiff.py
@@ -1,165 +1,35 @@
 """
 TIFF image parser.
 
-Authors: Victor Stinner and Sebastien Ponce
+Authors: Victor Stinner, Sebastien Ponce, Robert Xiao
 Creation date: 30 september 2006
 """
 
 from hachoir_parser import Parser
-from hachoir_core.field import (FieldSet, SeekableFieldSet, ParserError, RootSeekableFieldSet,
-    UInt16, UInt32, Bytes, String)
+from hachoir_core.field import FieldSet, SeekableFieldSet, RootSeekableFieldSet, Bytes
 from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
-from hachoir_parser.image.exif import BasicIFDEntry
-from hachoir_core.tools import createDict
-
-MAX_COUNT = 250
-
-class IFDEntry(BasicIFDEntry):
-    static_size = 12*8
-
-    TAG_INFO = {
-        254: ("new_subfile_type", "New subfile type"),
-        255: ("subfile_type", "Subfile type"),
-        256: ("img_width", "Image width in pixels"),
-        257: ("img_height", "Image height in pixels"),
-        258: ("bits_per_sample", "Bits per sample"),
-        259: ("compression", "Compression method"),
-        262: ("photo_interpret", "Photometric interpretation"),
-        263: ("thres", "Thresholding"),
-        264: ("cell_width", "Cellule width"),
-        265: ("cell_height", "Cellule height"),
-        266: ("fill_order", "Fill order"),
-        269: ("doc_name", "Document name"),
-        270: ("description", "Image description"),
-        271: ("make", "Make"),
-        272: ("model", "Model"),
-        273: ("strip_ofs", "Strip offsets"),
-        274: ("orientation", "Orientation"),
-        277: ("sample_pixel", "Samples per pixel"),
-        278: ("row_per_strip", "Rows per strip"),
-        279: ("strip_byte", "Strip byte counts"),
-        280: ("min_sample_value", "Min sample value"),
-        281: ("max_sample_value", "Max sample value"),
-        282: ("xres", "X resolution"),
-        283: ("yres", "Y resolution"),
-        284: ("planar_conf", "Planar configuration"),
-        285: ("page_name", "Page name"),
-        286: ("xpos", "X position"),
-        287: ("ypos", "Y position"),
-        288: ("free_ofs", "Free offsets"),
-        289: ("free_byte", "Free byte counts"),
-        290: ("gray_resp_unit", "Gray response unit"),
-        291: ("gray_resp_curve", "Gray response curve"),
-        292: ("group3_opt", "Group 3 options"),
-        293: ("group4_opt", "Group 4 options"),
-        296: ("res_unit", "Resolution unit"),
-        297: ("page_nb", "Page number"),
-        301: ("color_respt_curve", "Color response curves"),
-        305: ("software", "Software"),
-        306: ("date_time", "Date time"),
-        315: ("artist", "Artist"),
-        316: ("host_computer", "Host computer"),
-        317: ("predicator", "Predicator"),
-        318: ("white_pt", "White point"),
-        319: ("prim_chomat", "Primary chromaticities"),
-        320: ("color_map", "Color map"),
-        321: ("half_tone_hints", "Halftone Hints"),
-        322: ("tile_width", "TileWidth"),
-        323: ("tile_length", "TileLength"),
-        324: ("tile_offsets", "TileOffsets"),
-        325: ("tile_byte_counts", "TileByteCounts"),
-        332: ("ink_set", "InkSet"),
-        333: ("ink_names", "InkNames"),
-        334: ("number_of_inks", "NumberOfInks"),
-        336: ("dot_range", "DotRange"),
-        337: ("target_printer", "TargetPrinter"),
-        338: ("extra_samples", "ExtraSamples"),
-        339: ("sample_format", "SampleFormat"),
-        340: ("smin_sample_value", "SMinSampleValue"),
-        341: ("smax_sample_value", "SMaxSampleValue"),
-        342: ("transfer_range", "TransferRange"),
-        512: ("jpeg_proc", "JPEGProc"),
-        513: ("jpeg_interchange_format", "JPEGInterchangeFormat"),
-        514: ("jpeg_interchange_format_length", "JPEGInterchangeFormatLength"),
-        515: ("jpeg_restart_interval", "JPEGRestartInterval"),
-        517: ("jpeg_lossless_predictors", "JPEGLosslessPredictors"),
-        518: ("jpeg_point_transforms", "JPEGPointTransforms"),
-        519: ("jpeg_qtables", "JPEGQTables"),
-        520: ("jpeg_dctables", "JPEGDCTables"),
-        521: ("jpeg_actables", "JPEGACTables"),
-        529: ("ycbcr_coefficients", "YCbCrCoefficients"),
-        530: ("ycbcr_subsampling", "YCbCrSubSampling"),
-        531: ("ycbcr_positioning", "YCbCrPositioning"),
-        532: ("reference_blackwhite", "ReferenceBlackWhite"),
-        33432: ("copyright", "Copyright"),
-        0x8769: ("ifd_pointer", "Pointer to next IFD entry"),
-    }
-    TAG_NAME = createDict(TAG_INFO, 0)
-
-    def __init__(self, *args):
-        FieldSet.__init__(self, *args)
-        tag = self["tag"].value
-        if tag in self.TAG_INFO:
-            self._name, self._description = self.TAG_INFO[tag]
-        else:
-            self._parser = None
-
-class IFD(FieldSet):
-    def __init__(self, *args):
-        FieldSet.__init__(self, *args)
-        self._size = 16 + self["count"].value * IFDEntry.static_size
-        self._has_offset = False
-
-    def createFields(self):
-        yield UInt16(self, "count")
-        if MAX_COUNT < self["count"].value:
-            raise ParserError("TIFF IFD: Invalid count (%s)"
-                % self["count"].value)
-        for index in xrange(self["count"].value):
-            yield IFDEntry(self, "entry[]")
+from hachoir_parser.image.exif import TIFF
+
+def getStrips(ifd):
+    data = {}
+    for i, entry in enumerate(ifd.array('entry')):
+        data[entry['tag'].display] = entry
+    # image data
+    if "StripOffsets" in data and "StripByteCounts" in data:
+        offs = ifd.getEntryValues(data["StripOffsets"])
+        bytes = ifd.getEntryValues(data["StripByteCounts"])
+        for off, byte in zip(offs, bytes):
+            yield off.value, byte.value
 
 class ImageFile(SeekableFieldSet):
     def __init__(self, parent, name, description, ifd):
         SeekableFieldSet.__init__(self, parent, name, description, None)
-        self._has_offset = False
         self._ifd = ifd
 
     def createFields(self):
-        datas = {}
-        for entry in self._ifd:
-            if type(entry) != IFDEntry:
-                continue
-            for c in entry:
-                if c.name != "offset":
-                    continue
-                self.seekByte(c.value, False)
-                desc = "data of ifd entry " + entry.name,
-                entryType = BasicIFDEntry.ENTRY_FORMAT[entry["type"].value]
-                count = entry["count"].value
-                if entryType == String:
-                    yield String(self, entry.name, count, desc, "\0", "ISO-8859-1")
-                else:    
-                    d = Data(self, entry.name, desc, entryType, count)
-                    datas[d.name] = d
-                    yield d
-                break
-        # image data
-        if "strip_ofs" in datas and "strip_byte" in datas:
-            for i in xrange(datas["strip_byte"]._count):
-                self.seekByte(datas["strip_ofs"]["value["+str(i)+"]"].value, False)
-                yield Bytes(self, "strip[]", datas["strip_byte"]["value["+str(i)+"]"].value)
-
-class Data(FieldSet):
-
-    def __init__(self, parent, name, desc, type, count):
-        size = type.static_size * count
-        FieldSet.__init__(self, parent, name, desc, size)
-        self._count = count
-        self._type = type
-
-    def createFields(self):
-        for i in xrange(self._count):
-            yield self._type(self, "value[]")
+        for off, byte in getStrips(self._ifd):
+            self.seekByte(off, relative=False)
+            yield Bytes(self, "strip[]", byte)
 
 class TiffFile(RootSeekableFieldSet, Parser):
     PARSER_TAGS = {
@@ -168,7 +38,6 @@ class TiffFile(RootSeekableFieldSet, Parser):
         "file_ext": ("tif", "tiff"),
         "mime": (u"image/tiff",),
         "min_size": 8*8,
-# TODO: Re-enable magic
         "magic": (("II\x2A\0", 0), ("MM\0\x2A", 0)),
         "description": "TIFF picture"
     }
@@ -191,21 +60,11 @@ class TiffFile(RootSeekableFieldSet, Parser):
         return True
 
     def createFields(self):
-        yield String(self, "endian", 2, 'Endian ("II" or "MM")', charset="ASCII")
-        yield UInt16(self, "version", "TIFF version number")
-        offset = UInt32(self, "img_dir_ofs[]", "Next image directory offset (in bytes from the beginning)")
-        yield offset
-        ifds = []
-        while True:
-            if offset.value == 0:
-                break
+        for field in TIFF(self):
+            yield field
 
-            self.seekByte(offset.value, relative=False)
-            ifd = IFD(self, "ifd[]", "Image File Directory", None)
-            ifds.append(ifd)
-            yield ifd
-            offset = UInt32(self, "img_dir_ofs[]", "Next image directory offset (in bytes from the beginning)")
-            yield offset
-        for ifd in ifds:
+        for ifd in self.array('ifd'):
+            offs = (off for off, byte in getStrips(ifd))
+            self.seekByte(min(offs), relative=False)
             image = ImageFile(self, "image[]", "Image File", ifd)
             yield image
diff --git a/lib/hachoir_parser/misc/__init__.py b/lib/hachoir_parser/misc/__init__.py
index 10e98bb2f4020e721d9a74891ad6989244cefadd..f139201596779dd8fff3c2c9e44d8df5f2cf6e11 100644
--- a/lib/hachoir_parser/misc/__init__.py
+++ b/lib/hachoir_parser/misc/__init__.py
@@ -11,4 +11,8 @@ from hachoir_parser.misc.pifv import PIFVFile
 from hachoir_parser.misc.hlp import HlpFile
 from hachoir_parser.misc.gnome_keyring import GnomeKeyring
 from hachoir_parser.misc.bplist import BPList
-
+from hachoir_parser.misc.dsstore import DSStore
+from hachoir_parser.misc.word_doc import WordDocumentParser
+from hachoir_parser.misc.word_2 import Word2DocumentParser
+from hachoir_parser.misc.mstask import MSTaskFile
+from hachoir_parser.misc.mapsforge_map import MapsforgeMapFile
diff --git a/lib/hachoir_parser/misc/bplist.py b/lib/hachoir_parser/misc/bplist.py
index c46345e37ebd1513beb532155f61c904c4cccaa0..5411b4882bf3ecae5136995fdc896302f929f992 100644
--- a/lib/hachoir_parser/misc/bplist.py
+++ b/lib/hachoir_parser/misc/bplist.py
@@ -157,9 +157,16 @@ class BPListObject(FieldSet):
         elif markertype == 3:
             # Date
             yield Bits(self, "extra", 4, "Extra value, should be 3")
-            cvt_time=lambda v:datetime(2001,1,1) + timedelta(seconds=v)
+            # Use a heuristic to determine which epoch to use
+            def cvt_time(v):
+                v=timedelta(seconds=v)
+                epoch2001 = datetime(2001,1,1)
+                epoch1970 = datetime(1970,1,1)
+                if (epoch2001 + v - datetime.today()).days > 5*365:
+                    return epoch1970 + v
+                return epoch2001 + v
             yield displayHandler(Float64(self, "value"),lambda x:humanDatetime(cvt_time(x)))
-            self.xml=lambda prefix:prefix + "<date>%s</date>"%(cvt_time(self['value'].value).isoformat())
+            self.xml=lambda prefix:prefix + "<date>%sZ</date>"%(cvt_time(self['value'].value).isoformat())
 
         elif markertype == 4:
             # Data
@@ -175,7 +182,7 @@ class BPListObject(FieldSet):
             yield BPListSize(self, "size")
             if self['size'].value:
                 yield String(self, "value", self['size'].value, charset="ASCII")
-                self.xml=lambda prefix:prefix + "<string>%s</string>"%(self['value'].value.encode('iso-8859-1'))
+                self.xml=lambda prefix:prefix + "<string>%s</string>"%(self['value'].value.replace('&','&amp;').encode('iso-8859-1'))
             else:
                 self.xml=lambda prefix:prefix + '<string></string>'
 
@@ -184,7 +191,7 @@ class BPListObject(FieldSet):
             yield BPListSize(self, "size")
             if self['size'].value:
                 yield String(self, "value", self['size'].value*2, charset="UTF-16-BE")
-                self.xml=lambda prefix:prefix + "<string>%s</string>"%(self['value'].value.encode('utf-8'))
+                self.xml=lambda prefix:prefix + "<string>%s</string>"%(self['value'].value.replace('&','&amp;').encode('utf-8'))
             else:
                 self.xml=lambda prefix:prefix + '<string></string>'
 
diff --git a/lib/hachoir_parser/misc/chm.py b/lib/hachoir_parser/misc/chm.py
index 6bff555098b99696b1f60495873ab8932b60e776..37c5cae02ba27fb4f0010ba2feb5087caecc7091 100644
--- a/lib/hachoir_parser/misc/chm.py
+++ b/lib/hachoir_parser/misc/chm.py
@@ -6,17 +6,21 @@ Document:
   http://www.wotsit.org (search "chm")
 - chmlib library
   http://www.jedrea.com/chmlib/
+- Unofficial CHM Spec
+  http://savannah.nongnu.org/projects/chmspec
+- Microsoft's HTML Help (.chm) format
+  http://www.speakeasy.org/~russotto/chm/chmformat.html
 
 Author: Victor Stinner
 Creation date: 2007-03-04
 """
 
-from hachoir_parser import Parser
-from hachoir_core.field import (Field, FieldSet, ParserError,
-    Int32, UInt32, UInt64,
+from hachoir_core.field import (Field, FieldSet, ParserError, RootSeekableFieldSet,
+    Int32, UInt16, UInt32, UInt64,
     RawBytes, PaddingBytes,
     Enum, String)
 from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_parser import HachoirParser
 from hachoir_parser.common.win32 import GUID
 from hachoir_parser.common.win32_lang_id import LANGUAGE_ID
 from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
@@ -42,6 +46,7 @@ class CWord(Field):
                 raise ParserError("CHM: CWord is limited to 64 bits")
             addr += 8
             byte = stream.readBits(addr, 8, endian)
+        value <<= 7
         value += byte
         self.createValue = lambda: value
 
@@ -84,7 +89,7 @@ class ITSF(FieldSet):
         yield UInt32(self, "version")
         yield UInt32(self, "header_size", "Total header length (in bytes)")
         yield UInt32(self, "one")
-        yield UInt32(self, "last_modified")
+        yield UInt32(self, "last_modified", "Lower 32 bits of the time expressed in units of 0.1 us")
         yield Enum(UInt32(self, "lang_id", "Windows Language ID"), LANGUAGE_ID)
         yield GUID(self, "dir_uuid", "{7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC}")
         yield GUID(self, "stream_uuid", "{7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC}")
@@ -99,9 +104,9 @@ class PMGL_Entry(FieldSet):
     def createFields(self):
         yield CWord(self, "name_len")
         yield String(self, "name", self["name_len"].value, charset="UTF-8")
-        yield CWord(self, "space")
-        yield CWord(self, "start")
-        yield filesizeHandler(CWord(self, "length"))
+        yield CWord(self, "section", "Section number that the entry data is in.")
+        yield CWord(self, "start", "Start offset of the data")
+        yield filesizeHandler(CWord(self, "length", "Length of the data"))
 
     def createDescription(self):
         return "%s (%s)" % (self["name"].value, self["length"].display)
@@ -118,13 +123,23 @@ class PMGL(FieldSet):
 
         # Entries
         stop = self.size - self["free_space"].value * 8
+        entry_count = 0
         while self.current_size < stop:
             yield PMGL_Entry(self, "entry[]")
+            entry_count+=1
 
         # Padding
-        padding = (self.size - self.current_size) // 8
+        quickref_frequency = 1 + (1 << self["/dir/itsp/density"].value)
+        num_quickref = (entry_count // quickref_frequency)
+        if entry_count % quickref_frequency == 0:
+            num_quickref -= 1
+        print self.current_size//8, quickref_frequency, num_quickref
+        padding = (self["free_space"].value - (num_quickref*2+2))
         if padding:
             yield PaddingBytes(self, "padding", padding)
+        for i in range(num_quickref*quickref_frequency, 0, -quickref_frequency):
+            yield UInt16(self, "quickref[%i]"%i)
+        yield UInt16(self, "entry_count")
 
 class PMGI_Entry(FieldSet):
     def createFields(self):
@@ -164,36 +179,145 @@ class Directory(FieldSet):
         if self.current_size < self.size:
             yield PMGI(self, "pmgi", size=block_size)
 
-class ChmFile(Parser):
+class NameList(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "length", "Length of name list in 2-byte blocks")
+        yield UInt16(self, "count", "Number of entries in name list")
+        for index in range(self["count"].value):
+            length=UInt16(self, "name_len[]", "Length of name in 2-byte blocks, excluding terminating null")
+            yield length
+            yield String(self, "name[]", length.value*2+2, charset="UTF-16-LE")
+
+class ControlData(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "count", "Number of DWORDS in this struct")
+        yield String(self, "type", 4, "Type of compression")
+        if self["type"].value!='LZXC': return
+        yield UInt32(self, "version", "Compression version")
+        version=self["version"].value
+        if version==1: block='bytes'
+        else: block='32KB blocks'
+        yield UInt32(self, "reset_interval", "LZX: Reset interval in %s"%block)
+        yield UInt32(self, "window_size", "LZX: Window size in %s"%block)
+        yield UInt32(self, "cache_size", "LZX: Cache size in %s"%block)
+        yield UInt32(self, "unknown[]")
+
+class ResetTable(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "unknown[]", "Version number?")
+        yield UInt32(self, "count", "Number of entries")
+        yield UInt32(self, "entry_size", "Size of each entry")
+        yield UInt32(self, "header_size", "Size of this header")
+        yield UInt64(self, "uncompressed_size")
+        yield UInt64(self, "compressed_size")
+        yield UInt64(self, "block_size", "Block size in bytes")
+        for i in xrange(self["count"].value):
+            yield UInt64(self, "block_location[]", "location in compressed data of 1st block boundary in uncompressed data")
+
+class SystemEntry(FieldSet):
+    ENTRY_TYPE={0:"HHP: [OPTIONS]: Contents File",
+                1:"HHP: [OPTIONS]: Index File",
+                2:"HHP: [OPTIONS]: Default Topic",
+                3:"HHP: [OPTIONS]: Title",
+                4:"File Metadata",
+                5:"HHP: [OPTIONS]: Default Window",
+                6:"HHP: [OPTIONS]: Compiled file",
+                # 7 present only in files with Binary Index; unknown function
+                # 8 unknown function
+                9: "Version",
+                10: "Timestamp",
+                # 11 only in Binary TOC files
+                12: "Number of Info Types",
+                13: "#IDXHDR file",
+                # 14 unknown function
+                # 15 checksum??
+                16:"HHP: [OPTIONS]: Default Font",
+    }
+    def createFields(self):
+        yield Enum(UInt16(self, "type", "Type of entry"),self.ENTRY_TYPE)
+        yield UInt16(self, "length", "Length of entry")
+        yield RawBytes(self, "data", self["length"].value)
+    def createDescription(self):
+        return '#SYSTEM Entry, Type %s'%self["type"].display
+        
+class SystemFile(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "version", "Either 2 or 3")
+        while self.current_size < self.size:
+            yield SystemEntry(self, "entry[]")
+
+class ChmFile(HachoirParser, RootSeekableFieldSet):
+    MAGIC = "ITSF\3\0\0\0"
     PARSER_TAGS = {
         "id": "chm",
         "category": "misc",
         "file_ext": ("chm",),
         "min_size": 4*8,
-        "magic": (("ITSF\3\0\0\0", 0),),
+        "magic": ((MAGIC, 0),),
         "description": "Microsoft's HTML Help (.chm)",
     }
     endian = LITTLE_ENDIAN
 
+    def __init__(self, stream, **args):
+        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
+        HachoirParser.__init__(self, stream, **args)
+
     def validate(self):
-        if self.stream.readBytes(0, 4) != "ITSF":
+        if self.stream.readBytes(0, len(self.MAGIC)) != self.MAGIC:
             return "Invalid magic"
-        if self["itsf/version"].value != 3:
-            return "Invalid version"
         return True
 
     def createFields(self):
         yield ITSF(self, "itsf")
         yield Filesize_Header(self, "file_size", size=self["itsf/filesize_len"].value*8)
 
-        padding = self.seekByte(self["itsf/dir_offset"].value)
-        if padding:
-            yield padding
-        yield Directory(self, "dir", size=self["itsf/dir_len"].value*8)
+        self.seekByte(self["itsf/dir_offset"].value)
+        directory=Directory(self, "dir", size=self["itsf/dir_len"].value*8)
+        yield directory
+
+        otherentries = {}
+        for pmgl in directory.array("pmgl"):
+            for entry in pmgl.array("entry"):
+                if entry["section"].value != 0:
+                    otherentries.setdefault(entry["section"].value,[]).append(entry)
+                    continue
+                if entry["length"].value == 0:
+                    continue
+                self.seekByte(self["itsf/data_offset"].value+entry["start"].value)
+                name = entry["name"].value
+                if name == "::DataSpace/NameList":
+                    yield NameList(self, "name_list")
+                elif name.startswith('::DataSpace/Storage/'):
+                    sectname = str(name.split('/')[2])
+                    if name.endswith('/SpanInfo'):
+                        yield UInt64(self, "%s_spaninfo"%sectname, "Size of uncompressed data in the %s section"%sectname)
+                    elif name.endswith('/ControlData'):
+                        yield ControlData(self, "%s_controldata"%sectname, "Data about the compression scheme", size=entry["length"].value*8)
+                    elif name.endswith('/Transform/List'):
+                        yield String(self, "%s_transform_list"%sectname, 38, description="Transform/List element", charset="UTF-16-LE")
+                    elif name.endswith('/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable'):
+                        yield ResetTable(self, "%s_reset_table"%sectname, "LZX Reset Table", size=entry["length"].value*8)
+                    elif name.endswith('/Content'):
+                        # eventually, a LZX wrapper will appear here, we hope!
+                        yield RawBytes(self, "%s_content"%sectname, entry["length"].value, "Content for the %s section"%sectname)
+                    else:
+                        yield RawBytes(self, "entry_data[]", entry["length"].value, name)
+                elif name=="/#SYSTEM":
+                    yield SystemFile(self, "system_file", size=entry["length"].value*8)
+                else:
+                    yield RawBytes(self, "entry_data[]", entry["length"].value, name)
 
-        size = (self.size - self.current_size) // 8
-        if size:
-            yield RawBytes(self, "raw_end", size)
+    def getFile(self, filename):
+        page=0
+        if 'pmgi' in self['/dir']:
+            for entry in self['/dir/pmgi'].array('entry'):
+                if entry['name'].value <= filename:
+                    page=entry['page'].value
+        pmgl=self['/dir/pmgl[%i]'%page]
+        for entry in pmgl.array('entry'):
+            if entry['name'].value == filename:
+                return entry
+        raise ParserError("File '%s' not found!"%filename)
 
     def createContentSize(self):
         return self["file_size/file_size"].value * 8
diff --git a/lib/hachoir_parser/misc/dsstore.py b/lib/hachoir_parser/misc/dsstore.py
new file mode 100644
index 0000000000000000000000000000000000000000..02792ad580b7b35cdce181199d5b0fc79e2dc48f
--- /dev/null
+++ b/lib/hachoir_parser/misc/dsstore.py
@@ -0,0 +1,211 @@
+"""
+Mac OS X .DS_Store parser.
+
+Documents:
+- http://search.cpan.org/~wiml/Mac-Finder-DSStore-0.95/DSStoreFormat.pod
+Author: Robert Xiao
+Created: 2010-09-01
+"""
+
+from hachoir_parser import HachoirParser
+from hachoir_core.field import (RootSeekableFieldSet, FieldSet,
+    NullBytes, RawBytes, PaddingBytes, Bytes, SubFile, String, PascalString8,
+    Bits, UInt8, UInt16, UInt32,
+    Link,
+    ParserError)
+from hachoir_core.endian import BIG_ENDIAN
+from hachoir_core.text_handler import displayHandler, hexadecimal
+from hachoir_core.tools import paddingSize
+
+class BlockAddress(FieldSet):
+    static_size = 32
+
+    def createFields(self):
+        yield displayHandler(Bits(self, "offset", 27, description="Offset into file divided by 32"), lambda x: hex(x*32).strip('L'))
+        yield displayHandler(Bits(self, "size", 5, description="Power-of-2 size of the block"), lambda x: hex(1<<x).strip('L'))
+
+    def createValue(self):
+        return (self['offset'].value*32, 1<<self['size'].value)
+
+    def createDisplay(self):
+        if self['size'].value == 0:
+            return 'null block'
+        return self['offset'].display + '/' + self['size'].display
+
+class BudHeader(FieldSet):
+    static_size = 32*8
+
+    def createFields(self):
+        yield Bytes(self, "magic", 4, description="Always Bud1")
+        yield UInt32(self, "allocator_offset")
+        yield UInt32(self, "allocator_size")
+        yield UInt32(self, "allocator_offset_backup", description="Finder will refuse to read the file if this does not match the first copy")
+        for i in xrange(4):
+            yield BlockAddress(self, "block_address_copy[]", description="Copies of block addresses defined in the allocator")
+
+class BudDirectory(FieldSet):
+    def createFields(self):
+        yield PascalString8(self, "name", charset="MacRoman")
+        yield UInt32(self, "block")
+
+    def createValue(self):
+        return (self['name'].value, self['block'].value)
+
+    def createDisplay(self):
+        return self['name'].display
+
+class FreeList(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "count")
+        for i in xrange(self['count'].value):
+            yield UInt32(self, "offset[]")
+
+class BudAllocator(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "nblocks")
+        yield UInt32(self, "unknown", description="Always 0")
+        for i in xrange(self['nblocks'].value):
+            yield BlockAddress(self, "block[]")
+        padding = paddingSize(self['nblocks'].value, 256)
+        if padding:
+            yield NullBytes(self, "padding", padding*4, description="padding to make the number of blocks a multiple of 256")
+        yield UInt32(self, "ndirs")
+        for i in xrange(self['ndirs'].value):
+            yield BudDirectory(self, "dir[]")
+        for i in xrange(32):
+            yield FreeList(self, "freelist[]")
+        if self.current_size < self.size:
+            yield PaddingBytes(self, "slack", (self.size-self.current_size)//8, description="slack space")
+
+class DSDB(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "root_block", "The block number of the root node of the B-tree")
+        yield UInt32(self, "tree_levels", "The number of levels of internal nodes (tree height minus one)")
+        yield UInt32(self, "num_records", "The number of records in the tree")
+        yield UInt32(self, "num_nodes", "The number of nodes in the tree (tree nodes, not including this header block)")
+        yield UInt32(self, "unknown", "Always 0x1000, probably the tree node page size")
+        if self.current_size < self.size:
+            yield PaddingBytes(self, "slack", (self.size-self.current_size)//8, description="slack space")
+
+class PascalString32UTF16(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "size")
+        yield String(self, "value", self['size'].value*2, charset="UTF-16-BE")
+    def createValue(self):
+        return self['value'].value
+    def createDisplay(self):
+        return unicode(self['value'].display)
+
+class DSRecord(FieldSet):
+    def createFields(self):
+        yield PascalString32UTF16(self, "filename")
+        yield Bytes(self, "property", 4)
+        yield Bytes(self, "type", 4)
+        type = self['type'].value
+        if type == 'long':
+            yield UInt32(self, "value")
+        elif type == 'shor':
+            yield NullBytes(self, "padding", 2)
+            yield UInt16(self, "value")
+        elif type == 'bool':
+            yield UInt8(self, "value")
+        elif type == 'blob':
+            yield UInt32(self, "size")
+            yield SubFile(self, "value", self['size'].value)
+        elif type == 'type':
+            yield Bytes(self, "value", 4)
+        elif type == 'ustr':
+            yield PascalString32UTF16(self, "value")
+        else:
+            raise ParserError("Unknown record type %s"%type)
+
+    def createValue(self):
+        return (self['filename'].value, self['property'].value, self['value'].value)
+
+    def createDisplay(self):
+        return self['filename'].display + ':' + self['property'].value
+
+class BTNode(FieldSet):
+    def linkValue(self, block):
+        return (lambda: self['/node[%d]'%block.value])
+
+    def createFields(self):
+        yield UInt32(self, "last_block")
+        yield UInt32(self, "count")
+        if self['last_block'].value != 0:
+            for i in xrange(self['count'].value):
+                block = UInt32(self, "child_block[]")
+                yield block
+                link = Link(self, "child_link[]")
+                link.createValue = self.linkValue(block)
+                yield link
+                yield DSRecord(self, "record[]")
+            link = Link(self, "child_link[]")
+            link.createValue = self.linkValue(self['last_block'])
+            yield link
+        else:
+            for i in xrange(self['count'].value):
+                yield DSRecord(self, "record[]")
+        if self.current_size < self.size:
+            yield PaddingBytes(self, "slack", (self.size-self.current_size)//8, description="slack space")
+
+class DSStore(HachoirParser, RootSeekableFieldSet):
+    endian = BIG_ENDIAN
+    MAGIC = '\0\0\0\1Bud1'
+    PARSER_TAGS = {
+        "id": "dsstore",
+        "category": "misc",
+        "file_ext": ("DS_Store",),
+        "magic": ((MAGIC, 0),),
+        "min_size": 4+32, # \0\0\0\1 + 32-byte header
+        "description": "Mac OS X DS_Store",
+    }
+
+    def __init__(self, stream, **args):
+        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
+        HachoirParser.__init__(self, stream, **args)
+
+    def validate(self):
+        if self.stream.readBytes(0, len(self.MAGIC)) != self.MAGIC:
+            return "Invalid magic"
+        return True
+
+    def getBlock(self, block_number):
+        return self['allocator'].array('block')[block_number].value
+
+    def createFields(self):
+        yield UInt32(self, "unknown", description="Always 1")
+        yield BudHeader(self, "header")
+        self.seekByte(self['header/allocator_offset'].value+4)
+        yield BudAllocator(self, "allocator", size=self['header/allocator_size'].value*8)
+        for dir in self['allocator'].array('dir'):
+            if dir['name'].value == 'DSDB':
+                break
+        else:
+            raise ParserError("DSDB not found.")
+        offs, size = self.getBlock(dir['block'].value)
+        self.seekByte(offs+4)
+        yield DSDB(self, "dsdb", size=size*8)
+
+        blocks = [self['dsdb/root_block'].value]
+        while blocks:
+            block = blocks.pop()
+            offs, size = self.getBlock(block)
+            self.seekByte(offs+4)
+            node = BTNode(self, "node[%d]"%block, size=size*8)
+            yield node
+            if node['last_block'].value != 0:
+                new_blocks = []
+                for block in node.array('child_block'):
+                    new_blocks.append(block.value)
+                new_blocks.append(node['last_block'].value)
+                blocks.extend(reversed(new_blocks)) # dfs
+                #blocks = new_blocks[::-1] + blocks # bfs
+
+        for i, fl in enumerate(self['allocator'].array('freelist')):
+            if fl['count'].value == 0: continue
+            for offs in fl.array('offset'):
+                size = min(1<<i, self.size//8-offs.value-4)
+                if size > 0:
+                    self.seekByte(offs.value+4)
+                    yield RawBytes(self, "free[]", size)
diff --git a/lib/hachoir_parser/misc/lnk.py b/lib/hachoir_parser/misc/lnk.py
index 6e67bf1b07d06972ad04f212e9055d8f450cbd12..3844d37fce34ce0dc6132fa8c7d8f3e5984cac0d 100644
--- a/lib/hachoir_parser/misc/lnk.py
+++ b/lib/hachoir_parser/misc/lnk.py
@@ -56,7 +56,7 @@ class ItemId(FieldSet):
         0x23: "Drive",
         0x25: "Drive",
         0x29: "Drive",
-        0x2E: "GUID",
+        0x2E: "Shell Extension",
         0x2F: "Drive",
         0x30: "Dir/File",
         0x31: "Directory",
@@ -66,6 +66,7 @@ class ItemId(FieldSet):
         0x42: "Computer",
         0x46: "Net Provider",
         0x47: "Whole Network",
+        0x4C: "Web Folder",
         0x61: "MSITStore",
         0x70: "Printer/RAS Connection",
         0xB1: "History/Favorite",
@@ -86,16 +87,26 @@ class ItemId(FieldSet):
 
         yield Enum(UInt8(self, "type"),self.ITEM_TYPE)
         entrytype=self["type"].value
-        if entrytype in (0x1F, 0x2E, 0x70):
+        if entrytype in (0x1F, 0x70):
             # GUID
             yield RawBytes(self, "dummy", 1, "should be 0x50")
             yield GUID(self, "guid")
 
+        elif entrytype == 0x2E:
+            # Shell extension
+            yield RawBytes(self, "dummy", 1, "should be 0x50")
+            if self["dummy"].value == '\0':
+                yield UInt16(self, "length_data", "Length of shell extension-specific data")
+                if self["length_data"].value:
+                    yield RawBytes(self, "data", self["length_data"].value, "Shell extension-specific data")
+                yield GUID(self, "handler_guid")
+            yield GUID(self, "guid")
+
         elif entrytype in (0x23, 0x25, 0x29, 0x2F):
             # Drive
             yield String(self, "drive", self["length"].value-3, strip="\0")
 
-        elif entrytype in (0x30, 0x31, 0x32):
+        elif entrytype in (0x30, 0x31, 0x32, 0x61, 0xb1):
             yield RawBytes(self, "dummy", 1, "should be 0x00")
             yield UInt32(self, "size", "size of file; 0 for folders")
             yield DateTimeMSDOS32(self, "date_time", "File/folder date and time")
@@ -111,8 +122,11 @@ class ItemId(FieldSet):
                 yield RawBytes(self, "unknown[]", 6)
                 yield DateTimeMSDOS32(self, "creation_date_time", "File/folder creation date and time")
                 yield DateTimeMSDOS32(self, "access_date_time", "File/folder last access date and time")
-                yield RawBytes(self, "unknown[]", 4)
+                yield RawBytes(self, "unknown[]", 2)
+                yield UInt16(self, "length_next", "Length of next two strings (if zero, ignore this field)")
                 yield CString(self, "unicode_name", "File/folder name", charset="UTF-16-LE")
+                if self["length_next"].value:
+                    yield CString(self, "localized_name", "Localized name")
                 yield RawBytes(self, "unknown[]", 2)
             else:
                 yield CString(self, "name_short", "File/folder short name")
@@ -136,6 +150,19 @@ class ItemId(FieldSet):
             yield CString(self, "description")
             yield RawBytes(self, "unknown[]", 2)
 
+        elif entrytype == 0x4C:
+            # Web Folder
+            yield RawBytes(self, "unknown[]", 5)
+            yield TimestampWin64(self, "modification_time")
+            yield UInt32(self, "unknown[]")
+            yield UInt32(self, "unknown[]")
+            yield UInt32(self, "unknown[]")
+            yield LnkString(self, "name")
+            yield RawBytes(self, "padding[]", 2)
+            yield LnkString(self, "address")
+            if self["address/length"].value:
+                yield RawBytes(self, "padding[]", 2)
+
         else:
             yield RawBytes(self, "raw", self["length"].value-3)
 
@@ -249,13 +276,17 @@ class FileLocationInfo(FieldSet):
 class LnkString(FieldSet):
     def createFields(self):
         yield UInt16(self, "length", "Length of this string")
-        if self.root.hasUnicodeNames():
-            yield String(self, "data", self["length"].value*2, charset="UTF-16-LE")
-        else:
-            yield String(self, "data", self["length"].value, charset="ASCII")
+        if self["length"].value:
+            if self.root.hasUnicodeNames():
+                yield String(self, "data", self["length"].value*2, charset="UTF-16-LE")
+            else:
+                yield String(self, "data", self["length"].value, charset="ASCII")
 
     def createValue(self):
-        return self["data"].value
+        if self["length"].value:
+            return self["data"].value
+        else:
+            return ""
 
 class ColorRef(FieldSet):
     ''' COLORREF struct, 0x00bbggrr '''
diff --git a/lib/hachoir_parser/misc/mapsforge_map.py b/lib/hachoir_parser/misc/mapsforge_map.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b99653ab491622972e387e3b5d2c7bb79055f9b
--- /dev/null
+++ b/lib/hachoir_parser/misc/mapsforge_map.py
@@ -0,0 +1,357 @@
+"""
+Mapsforge map file parser (for version 3 files).
+
+Author: Oliver Gerlich
+
+References:
+- http://code.google.com/p/mapsforge/wiki/SpecificationBinaryMapFile
+- http://mapsforge.org/
+"""
+
+from hachoir_parser import Parser
+from hachoir_core.field import (ParserError,
+    Bit, Bits, UInt8, UInt16, UInt32, UInt64, String, RawBytes,
+    PaddingBits, PaddingBytes,
+    Enum, Field, FieldSet, SeekableFieldSet, RootSeekableFieldSet)
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
+
+
+# micro-degrees factor:
+UDEG = float(1000*1000)
+
+
+CoordinateEncoding = {
+    0: "single delta encoding",
+    1: "double delta encoding",
+}
+
+
+class UIntVbe(Field):
+    def __init__(self, parent, name, description=None):
+        Field.__init__(self, parent, name, description=description)
+
+        value = 0
+        size = 0
+        while True:
+            byteValue = ord( self._parent.stream.readBytes(self.absolute_address + (size*8), 1) )
+
+            haveMoreData = (byteValue & 0x80)
+            value = value | ((byteValue & 0x7f) << (size*7))
+            size += 1
+            assert size < 100, "UIntVBE is too large"
+
+            if not(haveMoreData):
+                break
+
+        self._size = size*8
+        self.createValue = lambda: value
+
+
+class IntVbe(Field):
+    def __init__(self, parent, name, description=None):
+        Field.__init__(self, parent, name, description=description)
+
+        value = 0
+        size = 0
+        shift = 0
+        while True:
+            byteValue = ord( self._parent.stream.readBytes(self.absolute_address + (size*8), 1) )
+
+            haveMoreData = (byteValue & 0x80)
+            if size == 0:
+                isNegative = (byteValue & 0x40)
+                value = (byteValue & 0x3f)
+                shift += 6
+            else:
+                value = value | ((byteValue & 0x7f) << shift)
+                shift += 7
+            size += 1
+            assert size < 100, "IntVBE is too large"
+
+            if not(haveMoreData):
+                break
+
+        if isNegative:
+            value *= -1
+
+        self._size = size*8
+        self.createValue = lambda: value
+
+
+class VbeString(FieldSet):
+    def createFields(self):
+        yield UIntVbe(self, "length")
+        yield String(self, "chars", self["length"].value, charset="UTF-8")
+
+    def createDescription (self):
+        return '(%d B) "%s"' % (self["length"].value, self["chars"].value)
+
+
+class TagStringList(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "num_tags")
+        for i in range(self["num_tags"].value):
+            yield VbeString(self, "tag[]")
+
+    def createDescription (self):
+        return "%d tag strings" % self["num_tags"].value
+
+
+class ZoomIntervalCfg(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "base_zoom_level")
+        yield UInt8(self, "min_zoom_level")
+        yield UInt8(self, "max_zoom_level")
+        yield UInt64(self, "subfile_start")
+        yield UInt64(self, "subfile_size")
+
+    def createDescription (self):
+        return "zoom level around %d (%d - %d)" % (self["base_zoom_level"].value,
+            self["min_zoom_level"].value, self["max_zoom_level"].value)
+
+
+class TileIndexEntry(FieldSet):
+    def createFields(self):
+        yield Bit(self, "is_water_tile")
+        yield Bits(self, "offset", 39)
+
+
+class TileZoomTable(FieldSet):
+    def createFields(self):
+        yield UIntVbe(self, "num_pois")
+        yield UIntVbe(self, "num_ways")
+
+    def createDescription (self):
+        return "%d POIs, %d ways" % (self["num_pois"].value, self["num_ways"].value)
+
+
+class TileHeader(FieldSet):
+    def __init__ (self, parent, name, zoomIntervalCfg, **kw):
+        FieldSet.__init__(self, parent, name, **kw)
+        self.zoomIntervalCfg = zoomIntervalCfg
+
+    def createFields(self):
+        numLevels = int(self.zoomIntervalCfg["max_zoom_level"].value - self.zoomIntervalCfg["min_zoom_level"].value) +1
+        assert(numLevels < 50)
+        for i in range(numLevels):
+            yield TileZoomTable(self, "zoom_table_entry[]")
+        yield UIntVbe(self, "first_way_offset")
+
+
+class POIData(FieldSet):
+    def createFields(self):
+        yield IntVbe(self, "lat_diff")
+        yield IntVbe(self, "lon_diff")
+        yield Bits(self, "layer", 4)
+        yield Bits(self, "num_tags", 4)
+
+        for i in range(self["num_tags"].value):
+            yield UIntVbe(self, "tag_id[]")
+
+        yield Bit(self, "have_name")
+        yield Bit(self, "have_house_number")
+        yield Bit(self, "have_ele")
+        yield PaddingBits(self, "pad[]", 5)
+
+        if self["have_name"].value:
+            yield VbeString(self, "name")
+        if self["have_house_number"].value:
+            yield VbeString(self, "house_number")
+        if self["have_ele"].value:
+            yield IntVbe(self, "ele")
+
+    def createDescription (self):
+        s = "POI"
+        if self["have_name"].value:
+            s += ' "%s"' % self["name"]["chars"].value
+        s += " @ %f/%f" % (self["lat_diff"].value / UDEG, self["lon_diff"].value / UDEG)
+        return s
+
+
+
+class SubTileBitmap(FieldSet):
+    static_size = 2*8
+    def createFields(self):
+        for y in range(4):
+            for x in range(4):
+                yield Bit(self, "is_used[%d,%d]" % (x,y))
+
+
+class WayProperties(FieldSet):
+    def createFields(self):
+        yield UIntVbe(self, "way_data_size")
+
+        # WayProperties is split into an outer and an inner field, to allow specifying data size for inner part:
+        yield WayPropertiesInner(self, "inner", size=self["way_data_size"].value * 8)
+
+
+class WayPropertiesInner(FieldSet):
+    def createFields(self):
+        yield SubTileBitmap(self, "sub_tile_bitmap")
+        #yield Bits(self, "sub_tile_bitmap", 16)
+
+        yield Bits(self, "layer", 4)
+        yield Bits(self, "num_tags", 4)
+
+        for i in range(self["num_tags"].value):
+            yield UIntVbe(self, "tag_id[]")
+
+        yield Bit(self, "have_name")
+        yield Bit(self, "have_house_number")
+        yield Bit(self, "have_ref")
+        yield Bit(self, "have_label_position")
+        yield Bit(self, "have_num_way_blocks")
+        yield Enum(Bit(self, "coord_encoding"), CoordinateEncoding)
+        yield PaddingBits(self, "pad[]", 2)
+
+        if self["have_name"].value:
+            yield VbeString(self, "name")
+        if self["have_house_number"].value:
+            yield VbeString(self, "house_number")
+        if self["have_ref"].value:
+            yield VbeString(self, "ref")
+        if self["have_label_position"].value:
+            yield IntVbe(self, "label_lat_diff")
+            yield IntVbe(self, "label_lon_diff")
+        numWayDataBlocks = 1
+        if self["have_num_way_blocks"].value:
+            yield UIntVbe(self, "num_way_blocks")
+            numWayDataBlocks = self["num_way_blocks"].value
+
+        for i in range(numWayDataBlocks):
+            yield WayData(self, "way_data[]")
+
+    def createDescription (self):
+        s = "way"
+        if self["have_name"].value:
+            s += ' "%s"' % self["name"]["chars"].value
+        return s
+
+
+class WayData(FieldSet):
+    def createFields(self):
+        yield UIntVbe(self, "num_coord_blocks")
+        for i in range(self["num_coord_blocks"].value):
+            yield WayCoordBlock(self, "way_coord_block[]")
+
+class WayCoordBlock(FieldSet):
+    def createFields(self):
+        yield UIntVbe(self, "num_way_nodes")
+        yield IntVbe(self, "first_lat_diff")
+        yield IntVbe(self, "first_lon_diff")
+
+        for i in range(self["num_way_nodes"].value-1):
+            yield IntVbe(self, "lat_diff[]")
+            yield IntVbe(self, "lon_diff[]")
+
+
+class TileData(FieldSet):
+    def __init__ (self, parent, name, zoomIntervalCfg, **kw):
+        FieldSet.__init__(self, parent, name, **kw)
+        self.zoomIntervalCfg = zoomIntervalCfg
+
+    def createFields(self):
+        yield TileHeader(self, "tile_header", self.zoomIntervalCfg)
+
+        numLevels = int(self.zoomIntervalCfg["max_zoom_level"].value - self.zoomIntervalCfg["min_zoom_level"].value) +1
+        for zoomLevel in range(numLevels):
+            zoomTableEntry = self["tile_header"]["zoom_table_entry[%d]" % zoomLevel]
+            for poiIndex in range(zoomTableEntry["num_pois"].value):
+                yield POIData(self, "poi_data[%d,%d]" % (zoomLevel, poiIndex))
+
+        for zoomLevel in range(numLevels):
+            zoomTableEntry = self["tile_header"]["zoom_table_entry[%d]" % zoomLevel]
+            for wayIndex in range(zoomTableEntry["num_ways"].value):
+                yield WayProperties(self, "way_props[%d,%d]" % (zoomLevel, wayIndex))
+        
+
+
+class ZoomSubFile(SeekableFieldSet):
+    def __init__ (self, parent, name, zoomIntervalCfg, **kw):
+        SeekableFieldSet.__init__(self, parent, name, **kw)
+        self.zoomIntervalCfg = zoomIntervalCfg
+
+    def createFields(self):
+        indexEntries = []
+        numTiles = None
+        i = 0
+        while True:
+            entry = TileIndexEntry(self, "tile_index_entry[]")
+            indexEntries.append(entry)
+            yield entry
+
+            i+=1
+            if numTiles is None:
+                # calculate number of tiles (TODO: better calc this from map bounding box)
+                firstOffset = self["tile_index_entry[0]"]["offset"].value
+                numTiles = firstOffset / 5
+            if i >= numTiles:
+                break
+
+        for indexEntry in indexEntries:
+            self.seekByte(indexEntry["offset"].value, relative=True)
+            yield TileData(self, "tile_data[]", zoomIntervalCfg=self.zoomIntervalCfg)
+
+
+
+class MapsforgeMapFile(Parser, RootSeekableFieldSet):
+    PARSER_TAGS = {
+        "id": "mapsforge_map",
+        "category": "misc",
+        "file_ext": ("map",),
+        "min_size": 62*8,
+        "description": "Mapsforge map file",
+    }
+
+    endian = BIG_ENDIAN
+
+    def validate(self):
+        return self["file_magic"].value == "mapsforge binary OSM" and self["file_version"].value == 3
+
+    def createFields(self):
+        yield String(self, "file_magic", 20)
+        yield UInt32(self, "header_size")
+        yield UInt32(self, "file_version")
+        yield UInt64(self, "file_size")
+        yield UInt64(self, "creation_date")
+        yield UInt32(self, "min_lat")
+        yield UInt32(self, "min_lon")
+        yield UInt32(self, "max_lat")
+        yield UInt32(self, "max_lon")
+        yield UInt16(self, "tile_size")
+        yield VbeString(self, "projection")
+
+        # flags
+        yield Bit(self, "have_debug")
+        yield Bit(self, "have_map_start")
+        yield Bit(self, "have_start_zoom")
+        yield Bit(self, "have_language_preference")
+        yield Bit(self, "have_comment")
+        yield Bit(self, "have_created_by")
+        yield Bits(self, "reserved[]", 2)
+
+        if self["have_map_start"].value:
+            yield UInt32(self, "start_lat")
+            yield UInt32(self, "start_lon")
+        if self["have_start_zoom"].value:
+            yield UInt8(self, "start_zoom")
+        if self["have_language_preference"].value:
+            yield VbeString(self, "language_preference")
+        if self["have_comment"].value:
+            yield VbeString(self, "comment")
+        if self["have_created_by"].value:
+            yield VbeString(self, "created_by")
+
+        yield TagStringList(self, "poi_tags")
+        yield TagStringList(self, "way_tags")
+
+
+        yield UInt8(self, "num_zoom_intervals")
+        for i in range(self["num_zoom_intervals"].value):
+            yield ZoomIntervalCfg(self, "zoom_interval_cfg[]")
+
+        for i in range(self["num_zoom_intervals"].value):
+            zoomIntervalCfg = self["zoom_interval_cfg[%d]" % i]
+            self.seekByte(zoomIntervalCfg["subfile_start"].value, relative=False)
+            yield ZoomSubFile(self, "subfile[]", size=zoomIntervalCfg["subfile_size"].value * 8, zoomIntervalCfg=zoomIntervalCfg)
+
diff --git a/lib/hachoir_parser/misc/msoffice.py b/lib/hachoir_parser/misc/msoffice.py
index 90ca1ca7add823678877e04a3cbadc7c5106ccb3..825c563772b379d9bcc45db14be96fc680accfe0 100644
--- a/lib/hachoir_parser/misc/msoffice.py
+++ b/lib/hachoir_parser/misc/msoffice.py
@@ -3,49 +3,40 @@ Parsers for the different streams and fragments found in an OLE2 file.
 
 Documents:
  - goffice source code
+ - Microsoft Office PowerPoint 97-2007 Binary File Format (.ppt) Specification
+    http://download.microsoft.com/download/0/B/E/0BE8BDD7-E5E8-422A-ABFD-4342ED7AD886/PowerPoint97-2007BinaryFileFormat(ppt)Specification.pdf
 
 Author: Robert Xiao, Victor Stinner
-Creation: 2006-04-23
+Creation: 8 january 2005
 """
 
-from hachoir_parser import HachoirParser
-from hachoir_core.field import FieldSet, RootSeekableFieldSet, RawBytes
-from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_core.field import (SubFile, FieldSet,
+    UInt8, UInt16, Int32, UInt32, Enum, String, CString,
+    Bits, RawBytes)
+from hachoir_core.text_handler import textHandler, hexadecimal
+from hachoir_parser.misc.ole2_util import OLE2FragmentParser, RawParser
 from hachoir_core.stream import StringInputStream
-from hachoir_parser.misc.msoffice_summary import SummaryFieldSet, CompObj
-from hachoir_parser.misc.word_doc import WordDocumentFieldSet
+from hachoir_parser.misc.msoffice_summary import Summary, CompObj
+from hachoir_parser.misc.word_doc import WordDocumentParser, WordTableParser
 
-PROPERTY_NAME = {
-    u"\5DocumentSummaryInformation": "doc_summary",
-    u"\5SummaryInformation": "summary",
-    u"WordDocument": "word_doc",
-}
-
-class OfficeRootEntry(HachoirParser, RootSeekableFieldSet):
-    PARSER_TAGS = {
-        "description": "Microsoft Office document subfragments",
-    }
-    endian = LITTLE_ENDIAN
-
-    def __init__(self, stream, **args):
-        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
-        HachoirParser.__init__(self, stream, **args)
-
-    def validate(self):
-        return True
+class RootEntry(OLE2FragmentParser):
+    ENDIAN_CHECK=False
 
     def createFields(self):
         for index, property in enumerate(self.ole2.properties):
             if index == 0:
                 continue
             try:
-                name = PROPERTY_NAME[property["name"].value]
+                name,parser = PROPERTY_NAME[property["name"].value]
             except LookupError:
                 name = property.name+"content"
-            for field in self.parseProperty(index, property, name):
+                parser = RawParser
+            for field in self.parseProperty(property, name, parser):
                 yield field
+    def seekSBlock(self, block):
+        self.seekBit(block * self.ole2.ss_size)
 
-    def parseProperty(self, property_index, property, name_prefix):
+    def parseProperty(self, property, name_prefix, parser=RawParser):
         ole2 = self.ole2
         if not property["size"].value:
             return
@@ -55,49 +46,45 @@ class OfficeRootEntry(HachoirParser, RootSeekableFieldSet):
         first = None
         previous = None
         size = 0
-        start = property["start"].value
-        chain = ole2.getChain(start, True)
-        blocksize = ole2.ss_size
-        desc_format = "Small blocks %s..%s (%s)"
+        fragment_group = None
+        chain = ole2.getChain(property["start"].value, ole2.ss_fat)
         while True:
             try:
                 block = chain.next()
                 contiguous = False
-                if not first:
+                if first is None:
                     first = block
                     contiguous = True
-                if previous and block == (previous+1):
+                if previous is not None and block == (previous+1):
                     contiguous = True
                 if contiguous:
                     previous = block
-                    size += blocksize
+                    size += ole2.ss_size
                     continue
             except StopIteration:
                 block = None
+            if first is None:
+                break
             self.seekSBlock(first)
-            desc = desc_format % (first, previous, previous-first+1)
-            size = min(size, property["size"].value*8)
-            if name_prefix in ("summary", "doc_summary"):
-                yield SummaryFieldSet(self, name, desc, size=size)
-            elif name_prefix == "word_doc":
-                yield WordDocumentFieldSet(self, name, desc, size=size)
-            elif property_index == 1:
-                yield CompObj(self, "comp_obj", desc, size=size)
-            else:
-                yield RawBytes(self, name, size//8, desc)
+            desc = "Small blocks %s..%s (%s)" % (first, previous, previous-first+1)
+            desc += " of %s bytes" % (ole2.ss_size//8)
+            field = CustomFragment(self, name, size, parser, desc, fragment_group)
+            yield field
+            if not fragment_group:
+                fragment_group = field.group
+                fragment_group.args["datasize"] = property["size"].value
+                fragment_group.args["ole2name"] = property["name"].value
             if block is None:
                 break
             first = block
             previous = block
-            size = ole2.sector_size
-
-    def seekSBlock(self, block):
-        self.seekBit(block * self.ole2.ss_size)
+            size = ole2.ss_size
 
 class FragmentGroup:
     def __init__(self, parser):
         self.items = []
         self.parser = parser
+        self.args = {}
 
     def add(self, item):
         self.items.append(item)
@@ -110,8 +97,8 @@ class FragmentGroup:
         data = "".join(data)
 
         # FIXME: Use smarter code to send arguments
-        args = {"ole2": self.items[0].root}
-        tags = {"class": self.parser, "args": args}
+        self.args["ole2"] = self.items[0].root
+        tags = {"class": self.parser, "args": self.args}
         tags = tags.iteritems()
         return StringInputStream(data, "<fragment group>", tags=tags)
 
@@ -129,3 +116,660 @@ class CustomFragment(FieldSet):
     def _createInputStream(self, **args):
         return self.group.createInputStream()
 
+class Pictures(OLE2FragmentParser):
+    class Picture(FieldSet):
+        def createFields(self):
+            yield RawBytes(self, "identifier", 4, "some kind of marker (A0461DF0)")
+            yield UInt32(self, "size")
+            yield RawBytes(self, "unknown[]", 16)
+            yield RawBytes(self, "unknown[]", 1)
+            yield SubFile(self, "image", self["size"].value-17, "Image Data")
+    ENDIAN_CHECK=False
+
+    def createFields(self):
+        pos=0
+        while pos//8 < self.datasize:
+            newpic=Pictures.Picture(self, "picture[]")
+            yield newpic
+            pos+=newpic.size
+
+class PowerPointDocument(OLE2FragmentParser):
+    OBJ_TYPES={ 0:"Unknown",
+                1000:"Document",
+                1001:"DocumentAtom",
+                1002:"EndDocument",
+                1003:"SlidePersist",
+                1004:"SlideBase",
+                1005:"SlideBaseAtom",
+                1006:"Slide",
+                1007:"SlideAtom",
+                1008:"Notes",
+                1009:"NotesAtom",
+                1010:"Environment",
+                1011:"SlidePersistAtom",
+                1012:"Scheme",
+                1013:"SchemeAtom",
+                1014:"DocViewInfo",
+                1015:"SSlideLayoutAtom",
+                1016:"MainMaster",
+                1017:"SSSlideInfoAtom",
+                1018:"SlideViewInfo",
+                1019:"GuideAtom",
+                1020:"ViewInfo",
+                1021:"ViewInfoAtom",
+                1022:"SlideViewInfoAtom",
+                1023:"VBAInfo",
+                1024:"VBAInfoAtom",
+                1025:"SSDocInfoAtom",
+                1026:"Summary",
+                1027:"Texture",
+                1028:"VBASlideInfo",
+                1029:"VBASlideInfoAtom",
+                1030:"DocRoutingSlip",
+                1031:"OutlineViewInfo",
+                1032:"SorterViewInfo",
+                1033:"ExObjList",
+                1034:"ExObjListAtom",
+                1035:"PPDrawingGroup", #FIXME: Office Art File Format Docu
+                1036:"PPDrawing", #FIXME: Office Art File Format Docu
+                1038:"Theme",
+                1039:"ColorMapping",
+                1040:"NamedShows", # don't know if container
+                1041:"NamedShow",
+                1042:"NamedShowSlides", # don't know if container
+                1052:"OriginalMainMasterId",
+                1053:"CompositeMasterId",
+                1054:"RoundTripContentMasterInfo12",
+                1055:"RoundTripShapeId12",
+                1056:"RoundTripHFPlaceholder12",
+                1058:"RoundTripContentMasterId12",
+                1059:"RoundTripOArtTextStyles12",
+                1060:"HeaderFooterDefaults12",
+                1061:"DocFlags12",
+                1062:"RoundTripShapeCheckSumForCustomLayouts12",
+                1063:"RoundTripNotesMasterTextStyles12",
+                1064:"RoundTripCustomTableStyles12",
+                2000:"List",
+                2005:"FontCollection",
+                2017:"ListPlaceholder",
+                2019:"BookmarkCollection",
+                2020:"SoundCollection",
+                2021:"SoundCollAtom",
+                2022:"Sound",
+                2023:"SoundData",
+                2025:"BookmarkSeedAtom",
+                2026:"GuideList",
+                2028:"RunArray",
+                2029:"RunArrayAtom",
+                2030:"ArrayElementAtom",
+                2031:"Int4ArrayAtom",
+                2032:"ColorSchemeAtom",
+                3008:"OEShape",
+                3009:"ExObjRefAtom",
+                3011:"OEPlaceholderAtom",
+                3020:"GrColor",
+                3024:"GPointAtom",
+                3025:"GrectAtom",
+                3031:"GRatioAtom",
+                3032:"Gscaling",
+                3034:"GpointAtom",
+                3035:"OEShapeAtom",
+                3037:"OEPlaceholderNewPlaceholderId12",
+                3998:"OutlineTextRefAtom",
+                3999:"TextHeaderAtom",
+                4000:"TextCharsAtom",
+                4001:"StyleTextPropAtom",
+                4002:"BaseTextPropAtom",
+                4003:"TxMasterStyleAtom",
+                4004:"TxCFStyleAtom",
+                4005:"TxPFStyleAtom",
+                4006:"TextRulerAtom",
+                4007:"TextBookmarkAtom",
+                4008:"TextBytesAtom",
+                4009:"TxSIStyleAtom",
+                4010:"TextSpecInfoAtom",
+                4011:"DefaultRulerAtom",
+                4023:"FontEntityAtom",
+                4024:"FontEmbeddedData",
+                4025:"TypeFace",
+                4026:"CString",
+                4027:"ExternalObject",
+                4033:"MetaFile",
+                4034:"ExOleObj",
+                4035:"ExOleObjAtom",
+                4036:"ExPlainLinkAtom",
+                4037:"CorePict",
+                4038:"CorePictAtom",
+                4039:"ExPlainAtom",
+                4040:"SrKinsoku",
+                4041:"HandOut",
+                4044:"ExEmbed",
+                4045:"ExEmbedAtom",
+                4046:"ExLink",
+                4047:"ExLinkAtom_old",
+                4048:"BookmarkEntityAtom",
+                4049:"ExLinkAtom",
+                4050:"SrKinsokuAtom",
+                4051:"ExHyperlinkAtom",
+                4053:"ExPlain",
+                4054:"ExPlainLink",
+                4055:"ExHyperlink",
+                4056:"SlideNumberMCAtom",
+                4057:"HeadersFooters",
+                4058:"HeadersFootersAtom",
+                4062:"RecolorEntryAtom",
+                4063:"TxInteractiveInfoAtom",
+                4065:"EmFormatAtom",
+                4066:"CharFormatAtom",
+                4067:"ParaFormatAtom",
+                4068:"MasterText",
+                4071:"RecolorInfoAtom",
+                4073:"ExQuickTime",
+                4074:"ExQuickTimeMovie",
+                4075:"ExQuickTimeMovieData",
+                4076:"ExSubscription",
+                4077:"ExSubscriptionSection",
+                4078:"ExControl",
+                4080:"SlideListWithText",
+                4081:"AnimationInfoAtom",
+                4082:"InteractiveInfo",
+                4083:"InteractiveInfoAtom",
+                4084:"SlideList",
+                4085:"UserEditAtom",
+                4086:"CurrentUserAtom",
+                4087:"DateTimeMCAtom",
+                4088:"GenericDateMCAtom",
+                4090:"FooterMCAtom",
+                4091:"ExControlAtom",
+                4100:"ExMediaAtom",
+                4101:"ExVideo",
+                4102:"ExAviMovie",
+                4103:"ExMCIMovie",
+                4109:"ExMIDIAudio",
+                4110:"ExCDAudio",
+                4111:"ExWAVAudioEmbedded",
+                4112:"ExWAVAudioLink",
+                4113:"ExOleObjStg",
+                4114:"ExCDAudioAtom",
+                4115:"ExWAVAudioEmbeddedAtom",
+                4116:"AnimationInfoAtom",
+                4117:"RTFDateTimeMCAtom",
+                5000:"ProgTags", # don't know if container
+                5001:"ProgStringTag",
+                5002:"ProgBinaryTag",
+                5003:"BinaryTagData",
+                6000:"PrintOptions",
+                6001:"PersistPtrFullBlock", # don't know if container
+                6002:"PersistPtrIncrementalBlock", # don't know if container
+                10000:"RulerIndentAtom",
+                10001:"GScalingAtom",
+                10002:"GRColorAtom",
+                10003:"GLPointAtom",
+                10004:"GlineAtom",
+                11019:"AnimationAtom12",
+                11021:"AnimationHashAtom12",
+                14100:"SlideSyncInfo12",
+                14101:"SlideSyncInfoAtom12",
+                0xf000:"EscherDggContainer", # Drawing Group Container 
+                0xf006:"EscherDgg",
+                0xf016:"EscherCLSID",
+                0xf00b:"EscherOPT",
+                0xf001:"EscherBStoreContainer",
+                0xf007:"EscherBSE",
+                0xf018:"EscherBlip_START", # Blip types are between 
+                0xf117:"EscherBlip_END", # these two values 
+                0xf002:"EscherDgContainer", # Drawing Container 
+                0xf008:"EscherDg",
+                0xf118:"EscherRegroupItems",
+                0xf120:"EscherColorScheme", # bug in docs 
+                0xf003:"EscherSpgrContainer",
+                0xf004:"EscherSpContainer",
+                0xf009:"EscherSpgr",
+                0xf00a:"EscherSp",
+                0xf00c:"EscherTextbox",
+                0xf00d:"EscherClientTextbox",
+                0xf00e:"EscherAnchor",
+                0xf00f:"EscherChildAnchor",
+                0xf010:"EscherClientAnchor",
+                0xf011:"EscherClientData",
+                0xf005:"EscherSolverContainer",
+                0xf012:"EscherConnectorRule", # bug in docs 
+                0xf013:"EscherAlignRule",
+                0xf014:"EscherArcRule",
+                0xf015:"EscherClientRule",
+                0xf017:"EscherCalloutRule",
+                0xf119:"EscherSelection",
+                0xf11a:"EscherColorMRU",
+                0xf11d:"EscherDeletedPspl", # bug in docs 
+                0xf11e:"EscherSplitMenuColors",
+                0xf11f:"EscherOleObject",
+                0xf122:"EscherUserDefined"}
+    class CurrentUserAtom(FieldSet):
+        def createFields(self):
+            yield UInt32(self, "size")
+            yield textHandler(UInt32(self, "magic", "0xe391c05f for normal PPT, 0xf3d1c4df for encrypted PPT"), hexadecimal)
+            yield UInt32(self, "offsetToCurrentEdit", "Offset in main stream to current edit field")
+            yield UInt16(self, "lenUserName", "Length of user name")
+            yield UInt16(self, "docFileVersion", "1012 for PP97+")
+            yield UInt8(self, "majorVersion", "3 for PP97+")
+            yield UInt8(self, "minorVersion", "0 for PP97+")
+            yield UInt16(self, "unknown")
+            yield String(self, "userName", self["lenUserName"].value, "ANSI version of the username")
+            yield UInt32(self, "relVersion", "Release version: 8 for regular PPT file, 9 for multiple-master PPT file")
+
+    class PowerPointObject(FieldSet):
+        def createFields(self):
+            yield Bits(self, "version", 4)
+            yield Bits(self, "instance", 12)
+            yield Enum(UInt16(self, "type"),PowerPointDocument.OBJ_TYPES)
+            yield UInt32(self, "length")
+            self._size = self["length"].value * 8 + 64
+            obj_type = self["type"].display
+            obj_len = self["length"].value
+            # type 1064 (RoundTripCustomTableStyles12) may appear to be a container, but it is not.
+            if self["version"].value==0xF and self["type"].value != 1064:
+                while (self.current_size)//8 < obj_len+8:
+                    yield PowerPointDocument.PowerPointObject(self, "object[]")
+            elif obj_len:
+                if obj_type=="FontEntityAtom":
+                    yield String(self, "data", obj_len, charset="UTF-16-LE", truncate="\0", strip="\0")
+                elif obj_type=="TextCharsAtom":
+                    yield String(self, "data", obj_len, charset="UTF-16-LE")
+                elif obj_type=="TextBytesAtom":
+                    yield String(self, "data", obj_len, charset="ASCII")
+                elif hasattr(PowerPointDocument, obj_type):
+                    field = getattr(PowerPointDocument, obj_type)(self, "data")
+                    field._size = obj_len * 8
+                    yield field
+                else:
+                    yield RawBytes(self, "data", obj_len)
+        def createDescription(self):
+            if self["version"].value==0xF:
+                return "PowerPoint Object Container; instance %s, type %s"%(self["instance"].value,self["type"].display)
+            return "PowerPoint Object; version %s, instance %s, type %s"%(self["version"].value,self["instance"].value,self["type"].display)
+    ENDIAN_CHECK=False
+    OS_CHECK=False
+    def createFields(self):
+        pos=0
+        while pos//8 < self.datasize:
+            newobj=PowerPointDocument.PowerPointObject(self, "object[]")
+            yield newobj
+            pos+=newobj.size
+
+class CurrentUser(OLE2FragmentParser):
+    def createFields(self):
+        yield PowerPointDocument.PowerPointObject(self, "current_user")
+        if self.current_size < self.size:
+            yield String(self, "unicode_name", self["current_user/data/lenUserName"].value * 2, charset="UTF-16-LE")
+        
+
+class ExcelWorkbook(OLE2FragmentParser):
+    BIFF_TYPES={0x000:"DIMENSIONS_v0",
+                0x200:"DIMENSIONS_v2",
+                0x001:"BLANK_v0",
+                0x201:"BLANK_v2",
+                0x002:"INTEGER",
+                0x003:"NUMBER_v0",
+                0x203:"NUMBER_v2",
+                0x004:"LABEL_v0",
+                0x204:"LABEL_v2",
+                0x005:"BOOLERR_v0",
+                0x205:"BOOLERR_v2",
+                0x006:"FORMULA_v0",
+                0x206:"FORMULA_v2",
+                0x406:"FORMULA_v4",
+                0x007:"STRING_v0",
+                0x207:"STRING_v2",
+                0x008:"ROW_v0",
+                0x208:"ROW_v2",
+                0x009:"BOF_v0",
+                0x209:"BOF_v2",
+                0x409:"BOF_v4",
+                0x809:"BOF_v8",
+                0x00a:"EOF",
+                0x00b:"INDEX_v0",
+                0x20b:"INDEX_v2",
+                0x00c:"CALCCOUNT",
+                0x00d:"CALCMODE",
+                0x00e:"PRECISION",
+                0x00f:"REFMODE",
+                0x010:"DELTA",
+                0x011:"ITERATION",
+                0x012:"PROTECT",
+                0x013:"PASSWORD",
+                0x014:"HEADER",
+                0x015:"FOOTER",
+                0x016:"EXTERNCOUNT",
+                0x017:"EXTERNSHEET",
+                0x018:"NAME_v0",
+                0x218:"NAME_v2",
+                0x019:"WINDOWPROTECT",
+                0x01a:"VERTICALPAGEBREAKS",
+                0x01b:"HORIZONTALPAGEBREAKS",
+                0x01c:"NOTE",
+                0x01d:"SELECTION",
+                0x01e:"FORMAT_v0",
+                0x41e:"FORMAT_v4",
+                0x01f:"FORMATCOUNT",	# Undocumented 
+                0x020:"COLUMNDEFAULT",	# Undocumented 
+                0x021:"ARRAY_v0",
+                0x221:"ARRAY_v2",
+                0x022:"1904",
+                0x023:"EXTERNNAME_v0",
+                0x223:"EXTERNNAME_v2",
+                0x024:"COLWIDTH",	# Undocumented 
+                0x025:"DEFAULTROWHEIGHT_v0",
+                0x225:"DEFAULTROWHEIGHT_v2",
+                0x026:"LEFT_MARGIN",
+                0x027:"RIGHT_MARGIN",
+                0x028:"TOP_MARGIN",
+                0x029:"BOTTOM_MARGIN",
+                0x02a:"PRINTHEADERS",
+                0x02b:"PRINTGRIDLINES",
+                0x02f:"FILEPASS",
+                0x031:"FONT_v0",
+                0x231:"FONT_v2",
+                0x032:"FONTCOUNT",	# Undocumented 
+                0x033:"PRINTSIZE",	# Undocumented 
+                0x036:"TABLE_v0",
+                0x236:"TABLE_v2",
+                0x037:"TABLE2",	# OOo has docs 
+                0x038:"WNDESK",	# Undocumented 
+                0x039:"ZOOM",	# Undocumented 
+                0x03a:"BEGINPREF",	# Undocumented 
+                0x03b:"ENDPREF",	# Undocumented 
+                0x03c:"CONTINUE",
+                0x03d:"WINDOW1",
+                0x03e:"WINDOW2_v0",
+                0x23e:"WINDOW2_v2",
+                0x03f:"PANE_V2",	# Undocumented 
+                0x040:"BACKUP",
+                0x041:"PANE",
+                0x042:"CODEPAGE",
+                0x043:"XF_OLD_v0",
+                0x243:"XF_OLD_v2",
+                0x443:"XF_OLD_v4",
+                0x044:"XF_INDEX",
+                0x045:"FONT_COLOR",
+                0x04d:"PLS",
+                0x050:"DCON",
+                0x051:"DCONREF",
+                0x052:"DCONNAME",
+                0x055:"DEFCOLWIDTH",
+                0x059:"XCT",
+                0x05a:"CRN",
+                0x05b:"FILESHARING",
+                0x05c:"WRITEACCESS",
+                0x05d:"OBJ",
+                0x05e:"UNCALCED",
+                0x05f:"SAVERECALC",
+                0x060:"TEMPLATE",
+                0x061:"INTL",	# Undocumented 
+                0x862:"TAB_COLOR",	# Undocumented, OO calls it SHEETLAYOUT 
+                0x063:"OBJPROTECT",
+                0x07d:"COLINFO",
+                0x27e:"RK", # Odd that there is no 0x7e 
+                0x07f:"IMDATA",
+                0x080:"GUTS",
+                0x081:"WSBOOL",
+                0x082:"GRIDSET",
+                0x083:"HCENTER",
+                0x084:"VCENTER",
+                0x085:"BOUNDSHEET",
+                0x086:"WRITEPROT",
+                0x087:"ADDIN",
+                0x088:"EDG",
+                0x089:"PUB",
+                0x08c:"COUNTRY",
+                0x08d:"HIDEOBJ",
+                0x08e:"BUNDLESOFFSET",	# Undocumented 
+                0x08f:"BUNDLEHEADER",	# Undocumented 
+                0x090:"SORT",
+                0x091:"SUB",
+                0x092:"PALETTE",
+                0x293:"STYLE", # Odd that there is no 0x93 
+                0x094:"LHRECORD",
+                0x095:"LHNGRAPH",
+                0x096:"SOUND",
+                0x097:"SYNC",	# Undocumented 
+                0x098:"LPR",
+                0x099:"STANDARDWIDTH",
+                0x09a:"FNGROUPNAME",
+                0x09b:"FILTERMODE",
+                0x09c:"FNGROUPCOUNT",
+                0x09d:"AUTOFILTERINFO",
+                0x09e:"AUTOFILTER",
+                0x0a0:"SCL",
+                0x0a1:"SETUP",
+                0x0a4:"TOOLBARVER",	# Undocumented 
+                0x0a9:"COORDLIST",
+                0x0ab:"GCW",
+                0x0ae:"SCENMAN",
+                0x0af:"SCENARIO",
+                0x0b0:"SXVIEW",
+                0x0b1:"SXVD",
+                0x0b2:"SXVI",
+                0x0b3:"SXSI",	# Undocumented 
+                0x0b4:"SXIVD",
+                0x0b5:"SXLI",
+                0x0b6:"SXPI",
+                0x0b7:"FACENUM",	# Undocumented
+                0x0b8:"DOCROUTE",
+                0x0b9:"RECIPNAME",
+                0x0ba:"SSLIST",	# Undocumented 
+                0x0bb:"MASKIMDATA",	# Undocumented 
+                0x4bc:"SHRFMLA",
+                0x0bd:"MULRK",
+                0x0be:"MULBLANK",
+                0x0bf:"TOOLBARHDR",	# Undocumented 
+                0x0c0:"TOOLBAREND",	# Undocumented 
+                0x0c1:"MMS",
+                0x0c2:"ADDMENU",
+                0x0c3:"DELMENU",
+                0x0c4:"TIPHISTORY",	# Undocumented 
+                0x0c5:"SXDI",
+                0x0c6:"SXDB",
+                0x0c7:"SXFDB",	# guessed 
+                0x0c8:"SXDDB",	# guessed 
+                0x0c9:"SXNUM",	# guessed 
+                0x0ca:"SXBOOL",	# guessed 
+                0x0cb:"SXERR",	# guessed 
+                0x0cc:"SXINT",	# guessed 
+                0x0cd:"SXSTRING",
+                0x0ce:"SXDTR",	# guessed 
+                0x0cf:"SXNIL",	# guessed 
+                0x0d0:"SXTBL",
+                0x0d1:"SXTBRGIITM",
+                0x0d2:"SXTBPG",
+                0x0d3:"OBPROJ",
+                0x0d5:"SXIDSTM",
+                0x0d6:"RSTRING",
+                0x0d7:"DBCELL",
+                0x0d8:"SXNUMGROUP",	# from OO : numerical grouping in pivot cache field 
+                0x0da:"BOOKBOOL",
+                0x0dc:"PARAMQRY",	# DUPLICATE dc 
+                0x0dc:"SXEXT",	# DUPLICATE dc 
+                0x0dd:"SCENPROTECT",
+                0x0de:"OLESIZE",
+                0x0df:"UDDESC",
+                0x0e0:"XF",
+                0x0e1:"INTERFACEHDR",
+                0x0e2:"INTERFACEEND",
+                0x0e3:"SXVS",
+                0x0e5:"MERGECELLS",	# guessed 
+                0x0e9:"BG_PIC",	# Undocumented 
+                0x0ea:"TABIDCONF",
+                0x0eb:"MS_O_DRAWING_GROUP",
+                0x0ec:"MS_O_DRAWING",
+                0x0ed:"MS_O_DRAWING_SELECTION",
+                0x0ef:"PHONETIC",	# semi-Undocumented 
+                0x0f0:"SXRULE",
+                0x0f1:"SXEX",
+                0x0f2:"SXFILT",
+                0x0f6:"SXNAME",
+                0x0f7:"SXSELECT",
+                0x0f8:"SXPAIR",
+                0x0f9:"SXFMLA",
+                0x0fb:"SXFORMAT",
+                0x0fc:"SST",
+                0x0fd:"LABELSST",
+                0x0ff:"EXTSST",
+                0x100:"SXVDEX",
+                0x103:"SXFORMULA",
+                0x122:"SXDBEX",
+                0x137:"CHTRINSERT",
+                0x138:"CHTRINFO",
+                0x13B:"CHTRCELLCONTENT",
+                0x13d:"TABID",
+                0x140:"CHTRMOVERANGE",
+                0x14D:"CHTRINSERTTAB",
+                0x15F:"LABELRANGES",
+                0x160:"USESELFS",
+                0x161:"DSF",
+                0x162:"XL5MODIFY",
+                0x196:"CHTRHEADER",
+                0x1a5:"FILESHARING2",
+                0x1a9:"USERDBVIEW",
+                0x1aa:"USERSVIEWBEGIN",
+                0x1ab:"USERSVIEWEND",
+                0x1ad:"QSI",
+                0x1ae:"SUPBOOK",
+                0x1af:"PROT4REV",
+                0x1b0:"CONDFMT",
+                0x1b1:"CF",
+                0x1b2:"DVAL",
+                0x1b5:"DCONBIN",
+                0x1b6:"TXO",
+                0x1b7:"REFRESHALL",
+                0x1b8:"HLINK",
+                0x1ba:"CODENAME",	# TYPO in MS Docs 
+                0x1bb:"SXFDBTYPE",
+                0x1bc:"PROT4REVPASS",
+                0x1be:"DV",
+                0x1c0:"XL9FILE",
+                0x1c1:"RECALCID",
+                0x800:"LINK_TIP",	# follows an hlink 
+                0x802:"UNKNOWN_802",	# OO exports it but has not name or docs 
+                0x803:"WQSETT",	# OO named it and can export it, but does not include it in the docs 
+                0x804:"WQTABLES",	# OO named it and can export it, but does not include it in the docs 
+                0x805:"UNKNOWN_805",	# No name or docs, seems related to web query see #153260 for sample 
+                0x810:"PIVOT_AUTOFORMAT",	# Seems to contain pivot table autoformat indicies, plus ?? 
+                0x864:"UNKNOWN_864",	# seems related to pivot tables 
+                0x867:"SHEETPROTECTION",	# OO named it, and has docs 
+                0x868:"RANGEPROTECTION",	# OO named it, no docs yet 
+
+                0x1001:"CHART_units",
+                0x1002:"CHART_chart",
+                0x1003:"CHART_series",
+                0x1006:"CHART_dataformat",
+                0x1007:"CHART_lineformat",
+                0x1009:"CHART_markerformat",
+                0x100a:"CHART_areaformat",
+                0x100b:"CHART_pieformat",
+                0x100c:"CHART_attachedlabel",
+                0x100d:"CHART_seriestext",
+                0x1014:"CHART_chartformat",
+                0x1015:"CHART_legend",
+                0x1016:"CHART_serieslist",
+                0x1017:"CHART_bar",
+                0x1018:"CHART_line",
+                0x1019:"CHART_pie",
+                0x101a:"CHART_area",
+                0x101b:"CHART_scatter",
+                0x101c:"CHART_chartline",
+                0x101d:"CHART_axis",
+                0x101e:"CHART_tick",
+                0x101f:"CHART_valuerange",
+                0x1020:"CHART_catserrange",
+                0x1021:"CHART_axislineformat",
+                0x1022:"CHART_chartformatlink",
+                0x1024:"CHART_defaulttext",
+                0x1025:"CHART_text",
+                0x1026:"CHART_fontx",
+                0x1027:"CHART_objectlink",
+                0x1032:"CHART_frame",
+                0x1033:"CHART_begin",
+                0x1034:"CHART_end",
+                0x1035:"CHART_plotarea",
+                0x103a:"CHART_3d",
+                0x103c:"CHART_picf",
+                0x103d:"CHART_dropbar",
+                0x103e:"CHART_radar",
+                0x103f:"CHART_surf",
+                0x1040:"CHART_radararea",
+                0x1041:"CHART_axisparent",
+                0x1043:"CHART_legendxn",
+                0x1044:"CHART_shtprops",
+                0x1045:"CHART_sertocrt",
+                0x1046:"CHART_axesused",
+                0x1048:"CHART_sbaseref",
+                0x104a:"CHART_serparent",
+                0x104b:"CHART_serauxtrend",
+                0x104e:"CHART_ifmt",
+                0x104f:"CHART_pos",
+                0x1050:"CHART_alruns",
+                0x1051:"CHART_ai",
+                0x105b:"CHART_serauxerrbar",
+                0x105c:"CHART_clrtclient",	# Undocumented 
+                0x105d:"CHART_serfmt",
+                0x105f:"CHART_3dbarshape",	# Undocumented 
+                0x1060:"CHART_fbi",
+                0x1061:"CHART_boppop",
+                0x1062:"CHART_axcext",
+                0x1063:"CHART_dat",
+                0x1064:"CHART_plotgrowth",
+                0x1065:"CHART_siindex",
+                0x1066:"CHART_gelframe",
+                0x1067:"CHART_boppopcustom",}
+    class BIFF(FieldSet):
+        def createFields(self):
+            yield Enum(UInt16(self, "type"),ExcelWorkbook.BIFF_TYPES)
+            yield UInt16(self, "length")
+            if self["length"].value:
+                yield RawBytes(self, "data", self["length"].value)
+        def createDescription(self):
+            return "Excel BIFF; type %s"%self["type"].display
+    def createFields(self):
+        pos=0
+        while pos//8 < self.datasize:
+            newobj=ExcelWorkbook.BIFF(self, "BIFF[]")
+            yield newobj
+            pos+=newobj.size
+
+class ThumbsCatalog(OLE2FragmentParser):
+    class ThumbsEntry(FieldSet):
+        def createFields(self):
+            yield UInt32(self, "size")
+            yield UInt32(self, "index")
+            yield Bits(self, "flags", 8)
+            yield RawBytes(self, "unknown[]", 5)
+            yield UInt16(self, "unknown[]")
+            yield CString(self, "name", charset="UTF-16-LE")
+            if self.current_size // 8 != self['size'].value:
+                yield RawBytes(self, "padding", self['size'].value - self.current_size // 8)
+        def createDescription(self):
+            return "Thumbnail entry for %s"%self["name"].display
+
+    def createFields(self):
+        yield UInt16(self, "unknown[]")
+        yield UInt16(self, "unknown[]")
+        yield UInt32(self, "count")
+        yield UInt32(self, "unknown[]")
+        yield UInt32(self, "unknown[]")
+        for i in xrange(self['count'].value):
+            yield ThumbsCatalog.ThumbsEntry(self, "entry[]")
+
+PROPERTY_NAME = {
+    u"Root Entry": ("root",RootEntry),
+    u"\5DocumentSummaryInformation": ("doc_summary",Summary),
+    u"\5SummaryInformation": ("summary",Summary),
+    u"\1CompObj": ("compobj",CompObj),
+    u"Pictures": ("pictures",Pictures),
+    u"PowerPoint Document": ("powerpointdoc",PowerPointDocument),
+    u"Current User": ("current_user",CurrentUser),
+    u"Workbook": ("workbook",ExcelWorkbook),
+    u"Catalog": ("catalog",ThumbsCatalog),
+    u"WordDocument": ("word_doc",WordDocumentParser),
+    u"0Table": ("table0",WordTableParser),
+    u"1Table": ("table1",WordTableParser),
+}
diff --git a/lib/hachoir_parser/misc/msoffice_summary.py b/lib/hachoir_parser/misc/msoffice_summary.py
index dd3234af12b321e01833b977d5aa50a616526546..e3ded4cffcefa18ade1d051185e096b92301c8ae 100644
--- a/lib/hachoir_parser/misc/msoffice_summary.py
+++ b/lib/hachoir_parser/misc/msoffice_summary.py
@@ -7,18 +7,19 @@ Documents
  - Apache POI (HPSF Internals):
    http://poi.apache.org/hpsf/internals.html
 """
+from hachoir_core.endian import BIG_ENDIAN,LITTLE_ENDIAN
 from hachoir_parser import HachoirParser
 from hachoir_core.field import (FieldSet, ParserError,
-    RootSeekableFieldSet, SeekableFieldSet,
+    SeekableFieldSet,
     Bit, Bits, NullBits,
     UInt8, UInt16, UInt32, TimestampWin64, TimedeltaWin64, Enum,
-    Bytes, RawBytes, NullBytes, String,
+    Bytes, RawBytes, NullBytes, PaddingBits, String,
     Int8, Int32, Float32, Float64, PascalString32)
 from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
-from hachoir_core.tools import createDict
-from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
+from hachoir_core.tools import createDict, paddingSize
 from hachoir_parser.common.win32 import GUID, PascalStringWin32, CODEPAGE_CHARSET
 from hachoir_parser.image.bmp import BmpHeader, parseImageData
+from hachoir_parser.misc.ole2_util import OLE2FragmentParser
 
 MAX_SECTION_COUNT = 100
 
@@ -165,10 +166,37 @@ class Thumbnail(FieldSet):
             yield RawBytes(self, "data", size)
 
 class PropertyContent(FieldSet):
+    class NullHandler(FieldSet):
+        def createFields(self):
+            yield UInt32(self, "unknown[]")
+            yield PascalString32(self, "data")
+        def createValue(self):
+            return self["data"].value
+    class BlobHandler(FieldSet):
+        def createFields(self):
+            self.osconfig = self.parent.osconfig
+            yield UInt32(self, "size")
+            yield UInt32(self, "count")
+            for i in range(self["count"].value):
+                yield PropertyContent(self, "item[]")
+                n=paddingSize(self.current_size,32)
+                if n: yield PaddingBits(self, "padding[]", n)
+    class WidePascalString32(FieldSet):
+        ''' uses number of characters instead of number of bytes '''
+        def __init__(self,parent,name,charset='ASCII'):
+            FieldSet.__init__(self,parent,name)
+            self.charset=charset
+        def createFields(self):
+            yield UInt32(self, "length", "Length of this string")
+            yield String(self, "data", self["length"].value*2, charset=self.charset)
+        def createValue(self):
+            return self["data"].value
+        def createDisplay(self):
+            return 'u'+self["data"].display
     TYPE_LPSTR = 30
     TYPE_INFO = {
         0: ("EMPTY", None),
-        1: ("NULL", None),
+        1: ("NULL", NullHandler),
         2: ("UInt16", UInt16),
         3: ("UInt32", UInt32),
         4: ("Float32", Float32),
@@ -197,9 +225,9 @@ class PropertyContent(FieldSet):
         28: ("CARRAY", None),
         29: ("USERDEFINED", None),
         30: ("LPSTR", PascalString32),
-        31: ("LPWSTR", PascalString32),
+        31: ("LPWSTR", WidePascalString32),
         64: ("FILETIME", TimestampWin64),
-        65: ("BLOB", None),
+        65: ("BLOB", BlobHandler),
         66: ("STREAM", None),
         67: ("STORAGE", None),
         68: ("STREAMED_OBJECT", None),
@@ -223,8 +251,13 @@ class PropertyContent(FieldSet):
         kw = {}
         try:
             handler = self.TYPE_INFO[tag][1]
-            if handler == PascalString32:
-                osconfig = self.osconfig
+            if handler in (self.WidePascalString32,PascalString32):
+                cur = self
+                while not hasattr(cur,'osconfig'):
+                    cur=cur.parent
+                    if cur is None:
+                        raise LookupError('Cannot find osconfig')
+                osconfig = cur.osconfig
                 if tag == self.TYPE_LPSTR:
                     kw["charset"] = osconfig.charset
                 else:
@@ -235,9 +268,10 @@ class PropertyContent(FieldSet):
         except LookupError:
             handler = None
         if not handler:
-            raise ParserError("OLE2: Unable to parse property of type %s" \
+            self.warning("OLE2: Unable to parse property of type %s" \
                 % self["type"].display)
-        if self["is_vector"].value:
+            # raise ParserError(
+        elif self["is_vector"].value:
             yield UInt32(self, "count")
             for index in xrange(self["count"].value):
                 yield handler(self, "item[]", **kw)
@@ -276,20 +310,16 @@ class SummaryIndex(FieldSet):
         yield String(self, "name", 16)
         yield UInt32(self, "offset")
 
-class BaseSummary:
-    endian = LITTLE_ENDIAN
+class Summary(OLE2FragmentParser):
+    ENDIAN_CHECK=True
 
-    def __init__(self):
-        if self["endian"].value == "\xFF\xFE":
-            self.endian = BIG_ENDIAN
-        elif self["endian"].value == "\xFE\xFF":
-            self.endian = LITTLE_ENDIAN
-        else:
-            raise ParserError("OLE2: Invalid endian value")
-        self.osconfig = OSConfig(self["os_type"].value == OS_MAC)
+    def __init__(self, stream, **args):
+        OLE2FragmentParser.__init__(self, stream, **args)
+        #self.osconfig = OSConfig(self["os_type"].value == OS_MAC)
+        self.osconfig = OSConfig(self.endian == BIG_ENDIAN)
 
     def createFields(self):
-        yield Bytes(self, "endian", 2, "Endian (0xFF 0xFE for Intel)")
+        yield Bytes(self, "endian", 2, "Endian (\\xfe\\xff for little endian)")
         yield UInt16(self, "format", "Format (0)")
         yield UInt8(self, "os_version")
         yield UInt8(self, "os_revision")
@@ -313,35 +343,20 @@ class BaseSummary:
         if 0 < size:
             yield NullBytes(self, "end_padding", size)
 
-class SummaryParser(BaseSummary, HachoirParser, RootSeekableFieldSet):
-    PARSER_TAGS = {
-        "description": "Microsoft Office summary",
-    }
-
-    def __init__(self, stream, **kw):
-        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
-        HachoirParser.__init__(self, stream, **kw)
-        BaseSummary.__init__(self)
-
-    def validate(self):
-        return True
+class CompObj(OLE2FragmentParser):
+    ENDIAN_CHECK=True
 
-class SummaryFieldSet(BaseSummary, FieldSet):
-    def __init__(self, parent, name, description=None, size=None):
-        FieldSet.__init__(self, parent, name, description=description, size=size)
-        BaseSummary.__init__(self)
-
-class CompObj(FieldSet):
-    OS_VERSION = {
-        0x0a03: "Windows 3.1",
-    }
+    def __init__(self, stream, **args):
+        OLE2FragmentParser.__init__(self, stream, **args)
+        self.osconfig = OSConfig(self["os"].value == OS_MAC)
+        
     def createFields(self):
         # Header
         yield UInt16(self, "version", "Version (=1)")
-        yield textHandler(UInt16(self, "endian", "Endian (0xFF 0xFE for Intel)"), hexadecimal)
+        yield Bytes(self, "endian", 2, "Endian (\\xfe\\xff for little endian)")
         yield UInt8(self, "os_version")
         yield UInt8(self, "os_revision")
-        yield Enum(UInt16(self, "os_type"), OS_NAME)
+        yield Enum(UInt16(self, "os"), OS_NAME)
         yield Int32(self, "unused", "(=-1)")
         yield GUID(self, "clsid")
 
@@ -349,12 +364,12 @@ class CompObj(FieldSet):
         yield PascalString32(self, "user_type", strip="\0")
 
         # Clipboard format
-        if self["os_type"].value == OS_MAC:
+        if self["os"].value == OS_MAC:
             yield Int32(self, "unused[]", "(=-2)")
             yield String(self, "clipboard_format", 4)
         else:
             yield PascalString32(self, "clipboard_format", strip="\0")
-        if self.current_size == self.size:
+        if self._current_size // 8 == self.datasize:
             return
 
         #-- OLE 2.01 ---
@@ -362,7 +377,7 @@ class CompObj(FieldSet):
         # Program ID
         yield PascalString32(self, "prog_id", strip="\0")
 
-        if self["os_type"].value != OS_MAC:
+        if self["os"].value != OS_MAC:
             # Magic number
             yield textHandler(UInt32(self, "magic", "Magic number (0x71B239F4)"), hexadecimal)
 
@@ -371,7 +386,8 @@ class CompObj(FieldSet):
             yield PascalStringWin32(self, "clipboard_format_unicode", strip="\0")
             yield PascalStringWin32(self, "prog_id_unicode", strip="\0")
 
-        size = (self.size - self.current_size) // 8
+        size = self.datasize - (self._current_size // 8) # _current_size because current_size returns _current_max_size
         if size:
             yield NullBytes(self, "end_padding", size)
 
+        if self.datasize<self.size//8: yield RawBytes(self,"slack_space",(self.size//8)-self.datasize)
diff --git a/lib/hachoir_parser/misc/mstask.py b/lib/hachoir_parser/misc/mstask.py
new file mode 100644
index 0000000000000000000000000000000000000000..10e073b4f69f0e3e667024354f6e9ca630f9084f
--- /dev/null
+++ b/lib/hachoir_parser/misc/mstask.py
@@ -0,0 +1,168 @@
+"""
+ms task/job file parser
+
+Author: Jeff Bryner
+Creation date: 2010-11
+References: 
+http://msdn.microsoft.com/en-us/library/cc248286%28v=PROT.13%29.aspx
+http://msdn.microsoft.com/en-us/library/cc248287%28v=PROT.13%29.aspx
+http://technet.microsoft.com/en-us/library/bb490996.aspx
+"""
+
+
+from hachoir_parser import Parser
+from hachoir_core.field import (FieldSet, RootSeekableFieldSet,
+    CString, String, PascalString16,
+    UInt32, UInt16, UInt8,
+    Bit, Bits, PaddingBits,
+    TimestampWin64, DateTimeMSDOS32,
+    NullBytes, PaddingBytes, RawBits, RawBytes, Enum)
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
+from hachoir_core.text_handler import textHandler, hexadecimal
+from hachoir_parser.common.win32 import PascalStringWin16, GUID
+from hachoir_parser.common.msdos import MSDOSFileAttr16, MSDOSFileAttr32
+from hachoir_core.text_handler import filesizeHandler
+
+class TaskTrigger(FieldSet):
+    TRIGGER_TYPE = {
+        0x00000000: "ONCE",
+        0x00000001: "DAILY",
+        0x00000002: "WEEKLY",
+        0x00000003: "MONTHLYDATE",
+        0x00000004: "MONTHLYDOW",
+        0x00000005: "EVENT_ON_IDLE",
+        0x00000006: "EVENT_AT_SYSTEMSTART",
+        0x00000007: "EVENT_AT_LOGON"
+    }
+
+    def __init__(self, *args, **kwargs):
+        FieldSet.__init__(self, *args, **kwargs)
+        self._size = self["TriggerSize"].value * 8
+
+    def createFields(self):
+        yield UInt16(self, "TriggerSize")
+        yield UInt16(self, "Reserved[]")
+        yield UInt16(self, "BeginYear")
+        yield UInt16(self, "BeginMonth")
+        yield UInt16(self, "BeginDay")
+        yield UInt16(self, "EndYear")
+        yield UInt16(self, "EndMonth")
+        yield UInt16(self, "EndDay")
+        yield UInt16(self, "StartHour")
+        yield UInt16(self, "StartMinute")
+        yield UInt32(self, "MinutesDuration")
+        yield UInt32(self, "MinutesInterval","Time period between repeated trigger firings.")
+        yield Bit(self, "HasEndDate","Can task stop at some point in time?")
+        yield Bit(self, "KillAtDurationEnd","Can task be stopped at the end of the repetition period?")
+        yield Bit(self, "TriggerDisabled","Is this trigger disabled?")
+        yield RawBits(self, "Unused[]", 29)
+        yield Enum(UInt32(self, "TriggerType"),self.TRIGGER_TYPE)
+        yield UInt16(self, "TriggerSpecific0")
+        yield UInt16(self, "TriggerSpecific1")
+        yield UInt16(self, "TriggerSpecific2")
+        yield UInt16(self, "Padding")
+        yield UInt16(self, "Reserved[]")
+        yield UInt16(self, "Reserved[]")
+
+class MSTaskFile(Parser, RootSeekableFieldSet):
+    PARSER_TAGS = {
+        "id": "mstask",
+        "category": "misc",    # "archive", "audio", "container", ...
+        "file_ext": ("job",), # TODO: Example ("bmp",) to parse the file "image.bmp"
+        "min_size": 100,         # TODO: Minimum file size (x bits, or x*8 in bytes)
+        "description": ".job 'at' file parser from ms windows", # TODO: Example: "A bitmap picture",
+    }
+
+    endian = LITTLE_ENDIAN
+
+    PRODUCT_VERSION = {
+        0x0400: "Windows NT 4.0",
+        0x0500: "Windows 2000",
+        0x0501: "Windows XP",
+        0x0600: "Windows Vista",
+        0x0601: "Windows 7"
+    }
+
+    TASK_STATUS = {
+        0x00041300: "Task Ready",
+        0x00041301: "Task running",
+        0x00041302: "Task disabled",
+        0x00041303: "Task has not run",
+        0x00041304: "Task has no more runs",
+        0x00041305: "Task not scheduled",
+        0x00041306: "Task terminated",
+        0x00041307: "Task has no valid triggers",
+        0x00041308: "Task contains only event triggers that do not have set run times",
+        0x00041309: "Task trigger not found",
+        0x0004130A: "One or more of the properties that are required to run this task have not been set.",
+        0x0004130B: "There is no running instance of the task",
+        0x0004130C: "Task Schedule Remoting Protocol service is not installed",
+        0x0004130D: "Task object cannot be opened",
+        0x0004130E: "Task object is invalid",
+        0x0004130F: "No Account information could be found in Task Scheduler Remoting Protocol security database for the task indicated."
+    }
+
+    def validate(self):
+        # The MAGIC for a task file is the windows version that created it
+        # http://msdn.microsoft.com/en-us/library/2d1fbbab-fe6c-4ae5-bdf5-41dc526b2439%28v=PROT.13%29#id11
+        if self['WindowsVersion'].value not in self.PRODUCT_VERSION:
+            return "Invalid Product Version Field"
+        return True
+
+    def createFields(self):
+        yield Enum(UInt16(self, "WindowsVersion"), self.PRODUCT_VERSION)
+        yield UInt16(self, "FileVersion")
+        yield GUID(self, "JobUUID")
+        yield UInt16(self, "AppNameOffset", "App Name Length Offset")
+        yield UInt16(self, "TriggerOffset", "Contains the offset in bytes within the .JOB file where the task triggers are located.")
+        yield UInt16(self, "ErrorRetryCount", "Contains the number of execute attempts that are attempted for the task if the task fails to start.")
+        yield UInt16(self, "ErrorRetryInterval", "Contains the interval, in minutes, between successive retries")
+        yield UInt16(self, "IdleDeadline", "Contains a maximum time in minutes to wait for the machine to become idle for Idle Wait minutes.")
+        yield UInt16(self, "IdleWait", "Contains a value in minutes. The machine remains idle for this many minutes before it runs the task")
+        yield UInt32(self, "Priority")
+        yield UInt32(self, "MaxRunTime", "Maximum run time in milliseconds")
+        yield UInt32(self, "ExitCode", "This contains the exit code of the executed task upon the completion of that task.")
+        yield Enum(UInt32(self, "Status"), self.TASK_STATUS)
+        yield Bit(self, "Interactive", "Can Task interact with user?")
+        yield Bit(self, "DeleteWhenDone", "Remove the task file when done?")
+        yield Bit(self, "Disabled", "Is Task disabled?")
+        yield Bit(self, "StartOnlyIfIdle", "Task begins only if computer is not in use at the scheduled time")
+        yield Bit(self, "KillOnIdleEnd", "Kill task if user input is detected, terminating idle state?")
+        yield Bit(self, "DontStartIfOnBatteries")
+        yield Bit(self, "KillIfGoingOnBatteries")
+        yield Bit(self, "RunOnlyIfDocked")
+        yield Bit(self, "HiddenTask")
+        yield Bit(self, "RunIfConnectedToInternet")
+        yield Bit(self, "RestartOnIdleResume")
+        yield Bit(self, "SystemRequired", "Can task cause system to resume or awaken if system is sleeping?")
+        yield Bit(self, "OnlyIfUserLoggedOn")
+        yield Bit(self, "ApplicationNameExists", "Does task have an application name defined?")
+        yield Bit(self, "Unused[]")
+        yield Bit(self, "Unused[]")
+        yield RawBytes(self, "flags", 2)
+        yield UInt16(self, "LastRunYear")
+        yield UInt16(self, "LastRunMonth")
+        yield UInt16(self, "LastRunWeekday", "Sunday=0,Saturday=6")
+        yield UInt16(self, "LastRunDay")
+        yield UInt16(self, "LastRunHour")
+        yield UInt16(self, "LastRunMinute")
+        yield UInt16(self, "LastRunSecond")
+        yield UInt16(self, "LastRunMillisecond")
+        yield UInt16(self, "RunningInstanceCount")
+        yield PascalStringWin16(self, "AppNameLength", strip='\0')
+        yield PascalStringWin16(self, "Parameters", strip='\0')
+        yield PascalStringWin16(self, "WorkingDirectory", strip='\0')
+        yield PascalStringWin16(self, "Author", strip='\0')
+        yield PascalStringWin16(self, "Comment", strip='\0')
+
+        yield UInt16(self, "UserDataSize")
+        #todo: read optional userdata
+        yield UInt16(self, "ReservedDataSize")
+        if self["ReservedDataSize"].value==8:
+            yield Enum(UInt32(self, "StartError", "contains the HRESULT error from the most recent attempt to start the task"), self.TASK_STATUS)
+            yield UInt32(self, "TaskFlags")
+        elif self["ReservedDataSize"].value:
+            yield RawBytes(self, "Reserved", self["ReservedDataSize"].value)
+        yield UInt16(self, "TriggerCount", "size of the array of triggers")
+        for i in xrange(self["TriggerCount"].value):
+            yield TaskTrigger(self, "Trigger[]")
diff --git a/lib/hachoir_parser/misc/ole2.py b/lib/hachoir_parser/misc/ole2.py
index 112b22b2d8bec1e3f7d8694eccacd6533cb67b12..9a59c726962739c0780ec630b6444fc308d42d39 100644
--- a/lib/hachoir_parser/misc/ole2.py
+++ b/lib/hachoir_parser/misc/ole2.py
@@ -1,5 +1,6 @@
 """
 Microsoft Office documents parser.
+OLE2 files are also used by many other programs to store data.
 
 Informations:
 * wordole.c of AntiWord program (v0.35)
@@ -23,13 +24,11 @@ from hachoir_parser import HachoirParser
 from hachoir_core.field import (
     FieldSet, ParserError, SeekableFieldSet, RootSeekableFieldSet,
     UInt8, UInt16, UInt32, UInt64, TimestampWin64, Enum,
-    Bytes, RawBytes, NullBytes, String)
+    Bytes, NullBytes, String)
 from hachoir_core.text_handler import filesizeHandler
-from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 from hachoir_parser.common.win32 import GUID
-from hachoir_parser.misc.msoffice import CustomFragment, OfficeRootEntry, PROPERTY_NAME
-from hachoir_parser.misc.word_doc import WordDocumentParser
-from hachoir_parser.misc.msoffice_summary import SummaryParser
+from hachoir_parser.misc.msoffice import PROPERTY_NAME, RootEntry, RawParser, CustomFragment
 
 MIN_BIG_BLOCK_LOG2 = 6   # 512 bytes
 MAX_BIG_BLOCK_LOG2 = 14  # 64 kB
@@ -112,19 +111,26 @@ class DIFat(SeekableFieldSet):
         for index in xrange(NB_DIFAT):
             yield SECT(self, "index[%u]" % index)
 
-        for index in xrange(self.count):
+        difat_sect = self.start
+        index = NB_DIFAT
+        entries_per_sect = self.parent.sector_size / 32 - 1
+        for ctr in xrange(self.count):
             # this is relative to real DIFAT start
-            self.seekBit(NB_DIFAT * SECT.static_size+self.parent.sector_size*(self.start+index))
-            for sect_index in xrange(NB_DIFAT*(index+1),NB_DIFAT*(index+2)):
-                yield SECT(self, "index[%u]" % sect_index)
+            self.seekBit(NB_DIFAT*SECT.static_size + self.parent.sector_size*difat_sect)
+            for sect_index in xrange(entries_per_sect):
+                yield SECT(self, "index[%u]" % (index+sect_index))
+            index += entries_per_sect
+            next = SECT(self, "difat[%u]" % ctr)
+            yield next
+            difat_sect = next.value
 
 class Header(FieldSet):
     static_size = 68 * 8
     def createFields(self):
         yield GUID(self, "clsid", "16 bytes GUID used by some apps")
         yield UInt16(self, "ver_min", "Minor version")
-        yield UInt16(self, "ver_maj", "Minor version")
-        yield Bytes(self, "endian", 2, "Endian (0xFFFE for Intel)")
+        yield UInt16(self, "ver_maj", "Major version")
+        yield Bytes(self, "endian", 2, "Endian (\\xfe\\xff for little endian)")
         yield UInt16(self, "bb_shift", "Log, base 2, of the big block size")
         yield UInt16(self, "sb_shift", "Log, base 2, of the small block size")
         yield NullBytes(self, "reserved[]", 6, "(reserved)")
@@ -156,6 +162,7 @@ class OLE2_File(HachoirParser, RootSeekableFieldSet):
         "id": "ole2",
         "category": "misc",
         "file_ext": (
+            "db",                        # Thumbs.db
             "doc", "dot",                # Microsoft Word
             "ppt", "ppz", "pps", "pot",  # Microsoft Powerpoint
             "xls", "xla",                # Microsoft Excel
@@ -229,21 +236,20 @@ class OLE2_File(HachoirParser, RootSeekableFieldSet):
         # Parse first property
         for index, property in enumerate(self.properties):
             if index == 0:
-                name = "root"
+                name, parser = 'root', RootEntry
             else:
                 try:
-                    name = PROPERTY_NAME[property["name"].value]
+                    name, parser = PROPERTY_NAME[property["name"].value]
                 except LookupError:
                     name = property.name+"content"
-            for field in self.parseProperty(property, name):
+                    parser = RawParser
+            for field in self.parseProperty(property, name, parser):
                 yield field
 
-    def parseProperty(self, property, name_prefix):
+    def parseProperty(self, property, name_prefix, parser=RawParser):
         if not property["size"].value:
             return
-        if property.name != "property[0]" \
-        and (property["size"].value < self["header/threshold"].value):
-            # Field is stored in the ministream, skip it
+        if property["size"].value < self["header/threshold"].value and name_prefix!='root':
             return
         name = "%s[]" % name_prefix
         first = None
@@ -255,10 +261,10 @@ class OLE2_File(HachoirParser, RootSeekableFieldSet):
             try:
                 block = chain.next()
                 contiguous = False
-                if not first:
+                if first is None:
                     first = block
                     contiguous = True
-                if previous and block == (previous+1):
+                if previous is not None and block == (previous+1):
                     contiguous = True
                 if contiguous:
                     previous = block
@@ -271,19 +277,12 @@ class OLE2_File(HachoirParser, RootSeekableFieldSet):
             self.seekBlock(first)
             desc = "Big blocks %s..%s (%s)" % (first, previous, previous-first+1)
             desc += " of %s bytes" % (self.sector_size // 8)
-            if name_prefix in set(("root", "summary", "doc_summary", "word_doc")):
-                if name_prefix == "root":
-                    parser = OfficeRootEntry
-                elif name_prefix == "word_doc":
-                    parser = WordDocumentParser
-                else:
-                    parser = SummaryParser
-                field = CustomFragment(self, name, size, parser, desc, fragment_group)
-                yield field
-                if not fragment_group:
-                    fragment_group = field.group
-            else:
-                yield RawBytes(self, name, size//8, desc)
+            field = CustomFragment(self, name, size, parser, desc, fragment_group)
+            if not fragment_group:
+                fragment_group = field.group
+                fragment_group.args["datasize"] = property["size"].value
+                fragment_group.args["ole2name"] = property["name"].value
+            yield field
             if block is None:
                 break
             first = block
@@ -313,7 +312,7 @@ class OLE2_File(HachoirParser, RootSeekableFieldSet):
             index = block // items_per_fat
             try:
                 block = fat[index]["index[%u]" % block].value
-            except LookupError:
+            except LookupError, err:
                 break
 
     def readBFAT(self):
diff --git a/lib/hachoir_parser/misc/ole2_util.py b/lib/hachoir_parser/misc/ole2_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..a164c7e44b21c583fedffdda873d7cfb36516fa6
--- /dev/null
+++ b/lib/hachoir_parser/misc/ole2_util.py
@@ -0,0 +1,35 @@
+from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
+from hachoir_core.field import RawBytes, RootSeekableFieldSet, ParserError
+from hachoir_parser import HachoirParser
+
+class OLE2FragmentParser(HachoirParser,RootSeekableFieldSet):
+    tags = {
+        "description": "Microsoft Office document subfragments",
+    }
+    endian = LITTLE_ENDIAN
+
+    ENDIAN_CHECK=False
+
+    def __init__(self, stream, **args):
+        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
+        HachoirParser.__init__(self, stream, **args)
+        if self.ENDIAN_CHECK:
+            if self["endian"].value == "\xFF\xFE":
+                self.endian = BIG_ENDIAN
+            elif self["endian"].value == "\xFE\xFF":
+                self.endian = LITTLE_ENDIAN
+            else:
+                raise ParserError("OLE2: Invalid endian value")
+
+    def validate(self):
+        if self.ENDIAN_CHECK:
+            if self["endian"].value not in ["\xFF\xFE", "\xFE\xFF"]:
+                return "Unknown endian value %s"%self["endian"].value.encode('hex')
+        return True
+
+class RawParser(OLE2FragmentParser):
+    ENDIAN_CHECK=False
+    OS_CHECK=False
+    def createFields(self):
+        yield RawBytes(self,"rawdata",self.datasize)
+        if self.datasize<self.size//8: yield RawBytes(self,"slack_space",(self.size//8)-self.datasize)
diff --git a/lib/hachoir_parser/misc/torrent.py b/lib/hachoir_parser/misc/torrent.py
index 88a1bea2f349e60e01f8f836f8b320035e5fc1ef..0c32a785277d76eac4f166a94b1c3a233e2466b7 100644
--- a/lib/hachoir_parser/misc/torrent.py
+++ b/lib/hachoir_parser/misc/torrent.py
@@ -128,7 +128,7 @@ class DictionaryItem(FieldSet):
 
 # Map first chunk byte => type
 TAGS = {'d': Dictionary, 'i': Integer, 'l': List}
-for index in xrange(1, 9+1):
+for index in xrange(0, 9+1):
     TAGS[str(index)] = TorrentString
 
 # Create an entry
diff --git a/lib/hachoir_parser/misc/word_2.py b/lib/hachoir_parser/misc/word_2.py
new file mode 100644
index 0000000000000000000000000000000000000000..aec727b1f3bd0038356981c3e675f8e501c057fd
--- /dev/null
+++ b/lib/hachoir_parser/misc/word_2.py
@@ -0,0 +1,168 @@
+"""
+Documents:
+
+* "Microsoft Word for Windows 2.0 Binary Format"
+   http://www.wotsit.org/download.asp?f=word2&sc=275927573
+"""
+
+from hachoir_core.field import (FieldSet, Enum,
+    Bit, Bits,
+    UInt8, Int16, UInt16, UInt32, Int32,
+    NullBytes, Bytes, RawBytes, PascalString16,
+    DateTimeMSDOS32, TimeDateMSDOS32)
+from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_parser.misc.ole2_util import OLE2FragmentParser
+from hachoir_core.tools import paddingSize
+from hachoir_parser.common.win32_lang_id import LANGUAGE_ID
+TIMESTAMP = DateTimeMSDOS32
+
+class FC_CB(FieldSet):
+    def createFields(self):
+        yield Int32(self, "fc", "File Offset")
+        yield UInt16(self, "cb", "Byte Count")
+    def createValue(self):
+        return (self['fc'].value,self['cb'].value)
+
+class FIB(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "wIdent", "Magic Number")
+        yield UInt16(self, "nFib", "File Information Block (FIB) Version")
+        yield UInt16(self, "nProduct", "Product Version")
+        yield Enum(UInt16(self, "lid", "Language ID"), LANGUAGE_ID)
+        yield Int16(self, "pnNext")
+
+        yield Bit(self, "fDot", "Is the document a document template?")
+        yield Bit(self, "fGlsy", "Is the document a glossary?")
+        yield Bit(self, "fComplex", "Is the document in Complex format?")
+        yield Bit(self, "fHasPic", "Does the document have embedded images?")
+        yield Bits(self, "cQuickSaves", 4, "Number of times the document was quick-saved")
+        yield Bit(self, "fEncrypted", "Is the document encrypted?")
+        yield Bits(self, "reserved[]", 7)
+
+        yield UInt16(self, "nFibBack")
+        yield UInt32(self, "reserved[]")
+        yield NullBytes(self, "rgwSpare", 6)
+
+        yield UInt32(self, "fcMin", "File offset of first text character")
+        yield UInt32(self, "fcMax", "File offset of last text character + 1")
+        yield Int32(self, "cbMax", "File offset of last byte + 1")
+        yield NullBytes(self, "fcSpare", 16)
+
+        yield UInt32(self, "ccpText", "Length of main document text stream")
+        yield Int32(self, "ccpFtn", "Length of footnote subdocument text stream")
+        yield Int32(self, "ccpHdr", "Length of header subdocument text stream")
+        yield Int32(self, "ccpMcr", "Length of macro subdocument text stream")
+        yield Int32(self, "ccpAtn", "Length of annotation subdocument text stream")
+        yield NullBytes(self, "ccpSpare", 16)
+
+        yield FC_CB(self, "StshfOrig", "Original STSH allocation")
+        yield FC_CB(self, "Stshf", "Current STSH allocation")
+        yield FC_CB(self, "PlcffndRef", "Footnote reference PLC")
+        yield FC_CB(self, "PlcffndTxt", "Footnote text PLC")
+        yield FC_CB(self, "PlcfandRef", "Annotation reference PLC")
+        yield FC_CB(self, "PlcfandTxt", "Annotation text PLC")
+        yield FC_CB(self, "Plcfsed", "Section descriptor PLC")
+        yield FC_CB(self, "Plcfpgd", "Page descriptor PLC")
+        yield FC_CB(self, "Plcfphe", "Paragraph heights PLC")
+        yield FC_CB(self, "Sttbfglsy", "Glossary string table")
+        yield FC_CB(self, "Plcfglsy", "Glossary PLC")
+        yield FC_CB(self, "Plcfhdd", "Header PLC")
+        yield FC_CB(self, "PlcfbteChpx", "Character property bin table PLC")
+        yield FC_CB(self, "PlcfbtePapx", "Paragraph property bin table PLC")
+        yield FC_CB(self, "Plcfsea", "Private Use PLC")
+        yield FC_CB(self, "Sttbfffn")
+        yield FC_CB(self, "PlcffldMom")
+        yield FC_CB(self, "PlcffldHdr")
+        yield FC_CB(self, "PlcffldFtn")
+        yield FC_CB(self, "PlcffldAtn")
+        yield FC_CB(self, "PlcffldMcr")
+        yield FC_CB(self, "Sttbfbkmk")
+        yield FC_CB(self, "Plcfbkf")
+        yield FC_CB(self, "Plcfbkl")
+        yield FC_CB(self, "Cmds")
+        yield FC_CB(self, "Plcmcr")
+        yield FC_CB(self, "Sttbfmcr")
+        yield FC_CB(self, "PrDrvr", "Printer Driver information")
+        yield FC_CB(self, "PrEnvPort", "Printer environment for Portrait mode")
+        yield FC_CB(self, "PrEnvLand", "Printer environment for Landscape mode")
+        yield FC_CB(self, "Wss", "Window Save State")
+        yield FC_CB(self, "Dop", "Document Property data")
+        yield FC_CB(self, "SttbfAssoc")
+        yield FC_CB(self, "Clx", "'Complex' file format data")
+        yield FC_CB(self, "PlcfpgdFtn", "Footnote page descriptor PLC")
+        yield FC_CB(self, "AutosaveSource", "Original filename for Autosave purposes")
+        yield FC_CB(self, "Spare5")
+        yield FC_CB(self, "Spare6")
+
+        yield Int16(self, "wSpare4")
+        yield UInt16(self, "pnChpFirst")
+        yield UInt16(self, "pnPapFirst")
+        yield UInt16(self, "cpnBteChp", "Count of CHPX FKPs recorded in file")
+        yield UInt16(self, "cpnBtePap", "Count of PAPX FKPs recorded in file")
+
+class SEPX(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "size")
+        self._size=(self['size'].value+1)*8
+        yield RawBytes(self, "raw[]", self['size'].value)
+
+class SEPXGroup(FieldSet):
+    def __init__(self, parent, name, size, description=None):
+        FieldSet.__init__(self, parent, name, description=description)
+        self._size=size*8
+    def createFields(self):
+        while self.current_size < self.size:
+            next=self.stream.readBytes(self.absolute_address+self.current_size,1)
+            if next=='\x00':
+                padding = paddingSize((self.absolute_address + self.current_size)//8, 512)
+                if padding:
+                    yield NullBytes(self, "padding[]", padding)
+                if self.current_size >= self.size: break
+            yield SEPX(self, "sepx[]")
+
+class Word2DocumentParser(OLE2FragmentParser):
+    MAGIC='\xdb\xa5' # 42459
+    PARSER_TAGS = {
+        "id": "word_v2_document",
+        "min_size": 8,
+        "magic": ((MAGIC, 0),),
+        "file_ext": ("doc",),
+        "description": "Microsoft Office Word Version 2.0 document",
+    }
+    endian = LITTLE_ENDIAN
+
+    def __init__(self, stream, **args):
+        OLE2FragmentParser.__init__(self, stream, **args)
+
+    def validate(self):
+        if self.stream.readBytes(0,2) != self.MAGIC:
+            return "Invalid magic."
+        if self['FIB/nFib'].value not in (45,):
+            return "Unknown FIB version."
+        return True
+
+    def createFields(self):
+        yield FIB(self, "FIB", "File Information Block")
+        
+        padding = (self['FIB/fcMin'].value - self.current_size//8)
+        if padding:
+            yield NullBytes(self, "padding[]", padding)
+        if self['FIB/ccpText'].value:
+            yield Bytes(self, "text", self['FIB/ccpText'].value)
+        if self['FIB/ccpFtn'].value:
+            yield Bytes(self, "text_footnote", self['FIB/ccpFtn'].value)
+        if self['FIB/ccpHdr'].value:
+            yield Bytes(self, "text_header", self['FIB/ccpHdr'].value)
+        if self['FIB/ccpMcr'].value:
+            yield Bytes(self, "text_macro", self['FIB/ccpMcr'].value)
+        if self['FIB/ccpAtn'].value:
+            yield Bytes(self, "text_annotation", self['FIB/ccpAtn'].value)
+
+        padding = (self['FIB/fcMax'].value - self.current_size//8)
+        if padding:
+            yield RawBytes(self, "padding[]", padding)
+        
+        sepx_size = (self['FIB/pnChpFirst'].value*512 - self.current_size//8)
+        if sepx_size:
+            yield SEPXGroup(self, "sepx", sepx_size)
+
diff --git a/lib/hachoir_parser/misc/word_doc.py b/lib/hachoir_parser/misc/word_doc.py
index 88de4c297ec4d4ba071a6ba447e382f5dba7d9c5..36929d83ba1b5822f6561e88f1d9b25a5797a642 100644
--- a/lib/hachoir_parser/misc/word_doc.py
+++ b/lib/hachoir_parser/misc/word_doc.py
@@ -11,289 +11,417 @@ Documents:
    section. Revised Dec 21 1998, added missing Document Properties (section).
 """
 
-from hachoir_parser import Parser
-from hachoir_core.field import (FieldSet,
+from hachoir_core.field import (FieldSet, Enum,
     Bit, Bits,
     UInt8, Int16, UInt16, UInt32, Int32,
-    NullBytes, RawBytes, PascalString16,
-    DateTimeMSDOS32)
+    NullBytes, Bytes, RawBytes, PascalString8, PascalString16, CString, String,
+    TimestampMac32, TimestampWin64)
+from hachoir_core.text_handler import displayHandler
 from hachoir_core.endian import LITTLE_ENDIAN
+from hachoir_parser import guessParser
+from hachoir_parser.misc.ole2_util import OLE2FragmentParser
+from hachoir_parser.common.win32_lang_id import LANGUAGE_ID
 
-TIMESTAMP = DateTimeMSDOS32
+CREATOR_ID={0x6A62: "Microsoft Word"}
+class ShortArray(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "csw", "Count of fields in the array of shorts")
+        self._size = self['csw'].value*16+16
+        yield Enum(UInt16(self, "wMagicCreated", "File creator ID"), CREATOR_ID)
+        yield Enum(UInt16(self, "wMagicRevised", "File last modifier ID"), CREATOR_ID)
+        yield UInt16(self, "wMagicCreatePrivate")
+        yield UInt16(self, "wMagicCreatedPrivate")
+        yield UInt16(self, "pnFbpChpFirst_W6")
+        yield UInt16(self, "pnChpFirst_W6")
+        yield UInt16(self, "cpnBteChp_W6")
+        yield UInt16(self, "pnFbpPapFirst_W6")
+        yield UInt16(self, "pnPapFirst_W6")
+        yield UInt16(self, "cpnBtePap_W6")
+        yield UInt16(self, "pnFbpLvcFirst_W6")
+        yield UInt16(self, "pnLvcFirst_W6")
+        yield UInt16(self, "cpnBteLvc_W6")
+        yield Enum(UInt16(self, "lidFE", "Language ID if a Far East version of Word was used"), LANGUAGE_ID)
+        while self.current_size < self.size:
+            yield Int16(self, "unknown[]")
+
+def buildDateHandler(v):
+    md,y=divmod(v,100)
+    m,d=divmod(md,100)
+    if y < 60: y=2000+y
+    else: y=1900+y
+    return "%04i-%02i-%02i"%(y,m,d)
+
+class LongArray(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "clw", "Count of fields in the array of longs")
+        self._size = self['clw'].value*32+16
+        yield Int32(self, "cbMax", "Stream offset of last byte + 1")
+        yield displayHandler(UInt32(self, "lProductCreated", "Date when the creator program was built"),buildDateHandler)
+        yield displayHandler(UInt32(self, "lProductRevised", "Date when the last modifier program was built"),buildDateHandler)
+
+        yield UInt32(self, "ccpText", "Length of main document text stream")
+        yield Int32(self, "ccpFtn", "Length of footnote subdocument text stream")
+        yield Int32(self, "ccpHdr", "Length of header subdocument text stream")
+        yield Int32(self, "ccpMcr", "Length of macro subdocument text stream")
+        yield Int32(self, "ccpAtn", "Length of annotation subdocument text stream")
+        yield Int32(self, "ccpEdn", "Length of endnote subdocument text stream")
+        yield Int32(self, "ccpTxbx", "Length of textbox subdocument text stream")
+        yield Int32(self, "ccpHdrTxbx", "Length of header textbox subdocument text stream")
+        yield Int32(self, "pnFbpChpFirst", "Start of CHPX (Character Property) sector chain (sector = 512-byte 'page')")
+        yield Int32(self, "pnChpFirst", "First CHPX sector")
+        yield Int32(self, "cpnBteChp", "Number of CHPX sectors in the file")
+        yield Int32(self, "pnFbpPapFirst", "Start of PAPX (Paragraph Property) sector chain")
+        yield Int32(self, "pnPapFirst", "First PAPX sector")
+        yield Int32(self, "cpnBtePap", "Number of PAPX sectors in the file")
+        yield Int32(self, "pnFbpLvcFirst", "Start of LVC sector chain")
+        yield Int32(self, "pnLvcFirst", "First LVC sector")
+        yield Int32(self, "cpnBteLvc", "Number of LVC sectors in the file")
+        yield Int32(self, "fcIslandFirst")
+        yield Int32(self, "fcIslandLim")
+        while self.current_size < self.size:
+            yield Int32(self, "unknown[]")
+
+class FCLCB(FieldSet):
+    static_size=64
+    def createFields(self):
+        yield Int32(self, "fc", "Table Stream Offset")
+        yield UInt32(self, "lcb", "Byte Count")
+    def createValue(self):
+        return (self['fc'].value,self['lcb'].value)
+
+class FCLCBArray(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "cfclcb", "Count of fields in the array of FC/LCB pairs")
+        self._size = self['cfclcb'].value*64+16
+        
+        yield FCLCB(self, "StshfOrig", "Original STSH allocation")
+        yield FCLCB(self, "Stshf", "Current STSH allocation")
+        yield FCLCB(self, "PlcffndRef", "Footnote reference (FRD) PLC")
+        yield FCLCB(self, "PlcffndTxt", "Footnote text PLC")
+        yield FCLCB(self, "PlcfandRef", "Annotation reference (ATRD) PLC")
+        yield FCLCB(self, "PlcfandTxt", "Annotation text PLC")
+        yield FCLCB(self, "Plcfsed", "Section descriptor (SED) PLC")
+        yield FCLCB(self, "Plcpad", "No longer used; used to be Plcfpgd (Page descriptor PLC)")
+        yield FCLCB(self, "Plcfphe", "Paragraph heights (PHE) PLC (only for Complex files)")
+        yield FCLCB(self, "Sttbfglsy", "Glossary string table")
+        yield FCLCB(self, "Plcfglsy", "Glossary PLC")
+        yield FCLCB(self, "Plcfhdd", "Header (HDD) PLC")
+        yield FCLCB(self, "PlcfbteChpx", "Character property bin table PLC")
+        yield FCLCB(self, "PlcfbtePapx", "Paragraph property bin table PLC")
+        yield FCLCB(self, "Plcfsea", "Private Use PLC")
+        yield FCLCB(self, "Sttbfffn", "Font information STTB")
+        yield FCLCB(self, "PlcffldMom", "Main document field position (FLD) PLC")
+        yield FCLCB(self, "PlcffldHdr", "Header subdocument field position (FLD) PLC")
+        yield FCLCB(self, "PlcffldFtn", "Footnote subdocument field position (FLD) PLC")
+        yield FCLCB(self, "PlcffldAtn", "Annotation subdocument field position (FLD) PLC")
+        yield FCLCB(self, "PlcffldMcr", "No longer used")
+        yield FCLCB(self, "Sttbfbkmk", "Bookmark names STTB")
+        yield FCLCB(self, "Plcfbkf", "Bookmark begin position (BKF) PLC")
+        yield FCLCB(self, "Plcfbkl", "Bookmark end position (BKL) PLC")
+        yield FCLCB(self, "Cmds", "Macro commands")
+        yield FCLCB(self, "Plcmcr", "No longer used")
+        yield FCLCB(self, "Sttbfmcr", "No longer used")
+        yield FCLCB(self, "PrDrvr", "Printer Driver information")
+        yield FCLCB(self, "PrEnvPort", "Printer environment for Portrait mode")
+        yield FCLCB(self, "PrEnvLand", "Printer environment for Landscape mode")
+        yield FCLCB(self, "Wss", "Window Save State")
+        yield FCLCB(self, "Dop", "Document Property data")
+        yield FCLCB(self, "SttbfAssoc", "Associated strings STTB")
+        yield FCLCB(self, "Clx", "Complex file information")
+        yield FCLCB(self, "PlcfpgdFtn", "Not used")
+        yield FCLCB(self, "AutosaveSource", "Original filename for Autosave purposes")
+        yield FCLCB(self, "GrpXstAtnOwners", "String Group for Annotation Owner Names")
+        yield FCLCB(self, "SttbfAtnbkmk", "Annotation subdocument bookmark names STTB")
+        yield FCLCB(self, "PlcdoaMom", "No longer used")
+        yield FCLCB(self, "PlcdoaHdr", "No longer used")
+        yield FCLCB(self, "PlcspaMom", "Main document File Shape (FSPA) PLC")
+        yield FCLCB(self, "PlcspaHdr", "Header subdocument FSPA PLC")
+        yield FCLCB(self, "PlcfAtnbkf", "Annotation subdocument bookmark begin position (BKF) PLC")
+        yield FCLCB(self, "PlcfAtnbkl", "Annotation subdocument bookmark end position (BKL) PLC")
+        yield FCLCB(self, "Pms", "Print Merge State")
+        yield FCLCB(self, "FormFldSttbs", "Form field values STTB")
+        yield FCLCB(self, "PlcfendRef", "Endnote Reference (FRD) PLC")
+        yield FCLCB(self, "PlcfendTxt", "Endnote Text PLC")
+        yield FCLCB(self, "PlcffldEdn", "Endnote subdocument field position (FLD) PLC)")
+        yield FCLCB(self, "PlcfpgdEdn", "not used")
+        yield FCLCB(self, "DggInfo", "Office Art Object Table Data")
+        yield FCLCB(self, "SttbfRMark", "Editor Author Abbreviations STTB")
+        yield FCLCB(self, "SttbCaption", "Caption Title STTB")
+        yield FCLCB(self, "SttbAutoCaption", "Auto Caption Title STTB")
+        yield FCLCB(self, "Plcfwkb", "WKB PLC")
+        yield FCLCB(self, "Plcfspl", "Spell Check State PLC")
+        yield FCLCB(self, "PlcftxbxTxt", "Text Box Text PLC")
+        yield FCLCB(self, "PlcffldTxbx", "Text Box Reference (FLD) PLC")
+        yield FCLCB(self, "PlcfhdrtxbxTxt", "Header Text Box Text PLC")
+        yield FCLCB(self, "PlcffldHdrTxbx", "Header Text Box Reference (FLD) PLC")
+        yield FCLCB(self, "StwUser", "Macro User storage")
+        yield FCLCB(self, "Sttbttmbd", "Embedded TrueType Font Data")
+        yield FCLCB(self, "Unused")
+        yield FCLCB(self, "PgdMother", "Main text page descriptors PLF")
+        yield FCLCB(self, "BkdMother", "Main text break descriptors PLF")
+        yield FCLCB(self, "PgdFtn", "Footnote text page descriptors PLF")
+        yield FCLCB(self, "BkdFtn", "Footnote text break descriptors PLF")
+        yield FCLCB(self, "PgdEdn", "Endnote text page descriptors PLF")
+        yield FCLCB(self, "BkdEdn", "Endnote text break descriptors PLF")
+        yield FCLCB(self, "SttbfIntlFld", "Field keywords STTB")
+        yield FCLCB(self, "RouteSlip", "Mailer Routing Slip")
+        yield FCLCB(self, "SttbSavedBy", "STTB of names of users who have saved the document")
+        yield FCLCB(self, "SttbFnm", "STTB of filenames of documents referenced by this one")
+        yield FCLCB(self, "PlcfLst", "List Format information PLC")
+        yield FCLCB(self, "PlfLfo", "List Format Override information PLC")
+        yield FCLCB(self, "PlcftxbxBkd", "Main document textbox break table (BKD) PLC")
+        yield FCLCB(self, "PlcftxbxHdrBkd", "Header subdocument textbox break table (BKD) PLC")
+        yield FCLCB(self, "DocUndo", "Undo/Versioning data")
+        yield FCLCB(self, "Rgbuse", "Undo/Versioning data")
+        yield FCLCB(self, "Usp", "Undo/Versioning data")
+        yield FCLCB(self, "Uskf", "Undo/Versioning data")
+        yield FCLCB(self, "PlcupcRgbuse", "Undo/Versioning data")
+        yield FCLCB(self, "PlcupcUsp", "Undo/Versioning data")
+        yield FCLCB(self, "SttbGlsyStyle", "Glossary entry style names STTB")
+        yield FCLCB(self, "Plgosl", "Grammar options PL")
+        yield FCLCB(self, "Plcocx", "OCX data PLC")
+        yield FCLCB(self, "PlcfbteLvc", "Character property bin table PLC")
+        if self['../fMac'].value:
+            yield TimestampMac32(self, "ftModified", "Date last modified")
+            yield Int32(self, "padding[]")
+        else:
+            yield TimestampWin64(self, "ftModified", "Date last modified")
+        yield FCLCB(self, "Plcflvc", "LVC PLC")
+        yield FCLCB(self, "Plcasumy", "Autosummary PLC")
+        yield FCLCB(self, "Plcfgram", "Grammar check PLC")
+        yield FCLCB(self, "SttbListNames", "List names STTB")
+        yield FCLCB(self, "SttbfUssr", "Undo/Versioning data")
+        while self.current_size < self.size:
+            yield FCLCB(self, "unknown[]")
 
-class BaseWordDocument:
+class FIB(FieldSet):
     def createFields(self):
-        yield UInt16(self, "wIdent", 2)
-        yield UInt16(self, "nFib")
-        yield UInt16(self, "nProduct")
-        yield UInt16(self, "lid")
+        yield UInt16(self, "wIdent", "Magic Number")
+        yield UInt16(self, "nFib", "File Information Block (FIB) Version")
+        yield UInt16(self, "nProduct", "Product Version")
+        yield Enum(UInt16(self, "lid", "Language ID"), LANGUAGE_ID)
         yield Int16(self, "pnNext")
 
-        yield Bit(self, "fDot")
-        yield Bit(self, "fGlsy")
-        yield Bit(self, "fComplex")
-        yield Bit(self, "fHasPic")
-        yield Bits(self, "cQuickSaves", 4)
-        yield Bit(self, "fEncrypted")
-        yield Bit(self, "fWhichTblStm")
-        yield Bit(self, "fReadOnlyRecommanded")
-        yield Bit(self, "fWriteReservation")
-        yield Bit(self, "fExtChar")
+        yield Bit(self, "fDot", "Is the document a document template?")
+        yield Bit(self, "fGlsy", "Is the document a glossary?")
+        yield Bit(self, "fComplex", "Is the document in Complex format?")
+        yield Bit(self, "fHasPic", "Does the document have embedded images?")
+        yield Bits(self, "cQuickSaves", 4, "Number of times the document was quick-saved")
+        yield Bit(self, "fEncrypted", "Is the document encrypted?")
+        yield Bits(self, "fWhichTblStm", 1, "Which table stream (0Table or 1Table) to use")
+        yield Bit(self, "fReadOnlyRecommended", "Should the file be opened read-only?")
+        yield Bit(self, "fWriteReservation", "Is the file write-reserved?")
+        yield Bit(self, "fExtChar", "Does the file use an extended character set?")
         yield Bit(self, "fLoadOverride")
-        yield Bit(self, "fFarEeast")
+        yield Bit(self, "fFarEast")
         yield Bit(self, "fCrypto")
 
-        yield UInt16(self, "nFibBack")
-        yield UInt32(self, "lKey")
-        yield UInt8(self, "envr")
+        yield UInt16(self, "nFibBack", "Document is backwards compatible down to this FIB version")
+        yield UInt32(self, "lKey", "File encryption key (only if fEncrypted)")
+        yield Enum(UInt8(self, "envr", "Document creation environment"), {0:'Word for Windows',1:'Macintosh Word'})
 
-        yield Bit(self, "fMac")
+        yield Bit(self, "fMac", "Was this file last saved on a Mac?")
         yield Bit(self, "fEmptySpecial")
         yield Bit(self, "fLoadOverridePage")
         yield Bit(self, "fFutureSavedUndo")
         yield Bit(self, "fWord97Save")
         yield Bits(self, "fSpare0", 3)
+        CHARSET={0:'Windows ANSI',256:'Macintosh'}
+        yield Enum(UInt16(self, "chse", "Character set for document text"),CHARSET)
+        yield Enum(UInt16(self, "chsTables", "Character set for internal table text"),CHARSET)
+        yield UInt32(self, "fcMin", "File offset for the first character of text")
+        yield UInt32(self, "fcMax", "File offset for the last character of text + 1")
 
-        yield UInt16(self, "chse")
-        yield UInt16(self, "chsTables")
-        yield UInt32(self, "fcMin")
-        yield UInt32(self, "fcMac")
-
-        yield PascalString16(self, "file_creator", strip="\0")
+        yield ShortArray(self, "array1", "Array of shorts")
+        yield LongArray(self, "array2", "Array of longs")
+        yield FCLCBArray(self, "array3", "Array of File Offset/Byte Count (FC/LCB) pairs")
 
-        yield NullBytes(self, "reserved[]", 12)
-
-        yield Int16(self, "lidFE")
-        yield UInt16(self, "clw")
-        yield Int32(self, "cbMac")
-        yield UInt32(self, "lProductCreated")
-        yield TIMESTAMP(self, "lProductRevised")
-
-        yield UInt32(self, "ccpText")
-        yield Int32(self, "ccpFtn")
-        yield Int32(self, "ccpHdr")
-        yield Int32(self, "ccpMcr")
-        yield Int32(self, "ccpAtn")
-        yield Int32(self, "ccpEdn")
-        yield Int32(self, "ccpTxbx")
-        yield Int32(self, "ccpHdrTxbx")
-        yield Int32(self, "pnFbpChpFirst")
-        yield Int32(self, "pnChpFirst")
-        yield Int32(self, "cpnBteChp")
-        yield Int32(self, "pnFbpPapFirst")
-        yield Int32(self, "pnPapFirst")
-        yield Int32(self, "cpnBtePap")
-        yield Int32(self, "pnFbpLvcFirst")
-        yield Int32(self, "pnLvcFirst")
-        yield Int32(self, "cpnBteLvc")
-        yield Int32(self, "fcIslandFirst")
-        yield Int32(self, "fcIslandLim")
-        yield UInt16(self, "cfclcb")
-        yield Int32(self, "fcStshfOrig")
-        yield UInt32(self, "lcbStshfOrig")
-        yield Int32(self, "fcStshf")
-        yield UInt32(self, "lcbStshf")
+def getRootParser(ole2):
+    return guessParser(ole2["root[0]"].getSubIStream())
 
-        yield Int32(self, "fcPlcffndRef")
-        yield UInt32(self, "lcbPlcffndRef")
-        yield Int32(self, "fcPlcffndTxt")
-        yield UInt32(self, "lcbPlcffndTxt")
-        yield Int32(self, "fcPlcfandRef")
-        yield UInt32(self, "lcbPlcfandRef")
-        yield Int32(self, "fcPlcfandTxt")
-        yield UInt32(self, "lcbPlcfandTxt")
-        yield Int32(self, "fcPlcfsed")
-        yield UInt32(self, "lcbPlcfsed")
-        yield Int32(self, "fcPlcpad")
-        yield UInt32(self, "lcbPlcpad")
-        yield Int32(self, "fcPlcfphe")
-        yield UInt32(self, "lcbPlcfphe")
-        yield Int32(self, "fcSttbfglsy")
-        yield UInt32(self, "lcbSttbfglsy")
-        yield Int32(self, "fcPlcfglsy")
-        yield UInt32(self, "lcbPlcfglsy")
-        yield Int32(self, "fcPlcfhdd")
-        yield UInt32(self, "lcbPlcfhdd")
-        yield Int32(self, "fcPlcfbteChpx")
-        yield UInt32(self, "lcbPlcfbteChpx")
-        yield Int32(self, "fcPlcfbtePapx")
-        yield UInt32(self, "lcbPlcfbtePapx")
-        yield Int32(self, "fcPlcfsea")
-        yield UInt32(self, "lcbPlcfsea")
-        yield Int32(self, "fcSttbfffn")
-        yield UInt32(self, "lcbSttbfffn")
-        yield Int32(self, "fcPlcffldMom")
-        yield UInt32(self, "lcbPlcffldMom")
-        yield Int32(self, "fcPlcffldHdr")
-        yield UInt32(self, "lcbPlcffldHdr")
-        yield Int32(self, "fcPlcffldFtn")
-        yield UInt32(self, "lcbPlcffldFtn")
-        yield Int32(self, "fcPlcffldAtn")
-        yield UInt32(self, "lcbPlcffldAtn")
-        yield Int32(self, "fcPlcffldMcr")
-        yield UInt32(self, "lcbPlcffldMcr")
-        yield Int32(self, "fcSttbfbkmk")
-        yield UInt32(self, "lcbSttbfbkmk")
-        yield Int32(self, "fcPlcfbkf")
-        yield UInt32(self, "lcbPlcfbkf")
-        yield Int32(self, "fcPlcfbkl")
-        yield UInt32(self, "lcbPlcfbkl")
-        yield Int32(self, "fcCmds")
-        yield UInt32(self, "lcbCmds")
-        yield Int32(self, "fcPlcmcr")
-        yield UInt32(self, "lcbPlcmcr")
-        yield Int32(self, "fcSttbfmcr")
-        yield UInt32(self, "lcbSttbfmcr")
-        yield Int32(self, "fcPrDrvr")
-        yield UInt32(self, "lcbPrDrvr")
-        yield Int32(self, "fcPrEnvPort")
-        yield UInt32(self, "lcbPrEnvPort")
-        yield Int32(self, "fcPrEnvLand")
-        yield UInt32(self, "lcbPrEnvLand")
-        yield Int32(self, "fcWss")
-        yield UInt32(self, "lcbWss")
-        yield Int32(self, "fcDop")
-        yield UInt32(self, "lcbDop")
-        yield Int32(self, "fcSttbfAssoc")
-        yield UInt32(self, "lcbSttbfAssoc")
-        yield Int32(self, "fcClx")
-        yield UInt32(self, "lcbClx")
-        yield Int32(self, "fcPlcfpgdFtn")
-        yield UInt32(self, "lcbPlcfpgdFtn")
-        yield Int32(self, "fcAutosaveSource")
-        yield UInt32(self, "lcbAutosaveSource")
-        yield Int32(self, "fcGrpXstAtnOwners")
-        yield UInt32(self, "lcbGrpXstAtnOwners")
-        yield Int32(self, "fcSttbfAtnbkmk")
-        yield UInt32(self, "lcbSttbfAtnbkmk")
-        yield Int32(self, "fcPlcdoaMom")
-        yield UInt32(self, "lcbPlcdoaMom")
-        yield Int32(self, "fcPlcdoaHdr")
-        yield UInt32(self, "lcbPlcdoaHdr")
-        yield Int32(self, "fcPlcspaMom")
-        yield UInt32(self, "lcbPlcspaMom")
-        yield Int32(self, "fcPlcspaHdr")
-        yield UInt32(self, "lcbPlcspaHdr")
-        yield Int32(self, "fcPlcfAtnbkf")
-        yield UInt32(self, "lcbPlcfAtnbkf")
-        yield Int32(self, "fcPlcfAtnbkl")
-        yield UInt32(self, "lcbPlcfAtnbkl")
-        yield Int32(self, "fcPms")
-        yield UInt32(self, "lcbPms")
-        yield Int32(self, "fcFormFldSttbs")
-        yield UInt32(self, "lcbFormFldSttbs")
-        yield Int32(self, "fcPlcfendRef")
-        yield UInt32(self, "lcbPlcfendRef")
-        yield Int32(self, "fcPlcfendTxt")
-        yield UInt32(self, "lcbPlcfendTxt")
-        yield Int32(self, "fcPlcffldEdn")
-        yield UInt32(self, "lcbPlcffldEdn")
-        yield Int32(self, "fcPlcfpgdEdn")
-        yield UInt32(self, "lcbPlcfpgdEdn")
-        yield Int32(self, "fcDggInfo")
-        yield UInt32(self, "lcbDggInfo")
-        yield Int32(self, "fcSttbfRMark")
-        yield UInt32(self, "lcbSttbfRMark")
-        yield Int32(self, "fcSttbCaption")
-        yield UInt32(self, "lcbSttbCaption")
-        yield Int32(self, "fcSttbAutoCaption")
-        yield UInt32(self, "lcbSttbAutoCaption")
-        yield Int32(self, "fcPlcfwkb")
-        yield UInt32(self, "lcbPlcfwkb")
-        yield Int32(self, "fcPlcfspl")
-        yield UInt32(self, "lcbPlcfspl")
-        yield Int32(self, "fcPlcftxbxTxt")
-        yield UInt32(self, "lcbPlcftxbxTxt")
-        yield Int32(self, "fcPlcffldTxbx")
-        yield UInt32(self, "lcbPlcffldTxbx")
-        yield Int32(self, "fcPlcfhdrtxbxTxt")
-        yield UInt32(self, "lcbPlcfhdrtxbxTxt")
-        yield Int32(self, "fcPlcffldHdrTxbx")
-        yield UInt32(self, "lcbPlcffldHdrTxbx")
-        yield Int32(self, "fcStwUser")
-        yield UInt32(self, "lcbStwUser")
-        yield Int32(self, "fcSttbttmbd")
-        yield UInt32(self, "cbSttbttmbd")
-        yield Int32(self, "fcUnused")
-        yield UInt32(self, "lcbUnused")
-        yield Int32(self, "fcPgdMother")
-        yield UInt32(self, "lcbPgdMother")
-        yield Int32(self, "fcBkdMother")
-        yield UInt32(self, "lcbBkdMother")
-        yield Int32(self, "fcPgdFtn")
-        yield UInt32(self, "lcbPgdFtn")
-        yield Int32(self, "fcBkdFtn")
-        yield UInt32(self, "lcbBkdFtn")
-        yield Int32(self, "fcPgdEdn")
-        yield UInt32(self, "lcbPgdEdn")
-        yield Int32(self, "fcBkdEdn")
-        yield UInt32(self, "lcbBkdEdn")
-        yield Int32(self, "fcSttbfIntlFld")
-        yield UInt32(self, "lcbSttbfIntlFld")
-        yield Int32(self, "fcRouteSlip")
-        yield UInt32(self, "lcbRouteSlip")
-        yield Int32(self, "fcSttbSavedBy")
-        yield UInt32(self, "lcbSttbSavedBy")
-        yield Int32(self, "fcSttbFnm")
-        yield UInt32(self, "lcbSttbFnm")
-        yield Int32(self, "fcPlcfLst")
-        yield UInt32(self, "lcbPlcfLst")
-        yield Int32(self, "fcPlfLfo")
-        yield UInt32(self, "lcbPlfLfo")
-        yield Int32(self, "fcPlcftxbxBkd")
-        yield UInt32(self, "lcbPlcftxbxBkd")
-        yield Int32(self, "fcPlcftxbxHdrBkd")
-        yield UInt32(self, "lcbPlcftxbxHdrBkd")
-        yield Int32(self, "fcDocUndo")
-        yield UInt32(self, "lcbDocUndo")
-        yield Int32(self, "fcRgbuse")
-        yield UInt32(self, "lcbRgbuse")
-        yield Int32(self, "fcUsp")
-        yield UInt32(self, "lcbUsp")
-        yield Int32(self, "fcUskf")
-        yield UInt32(self, "lcbUskf")
-        yield Int32(self, "fcPlcupcRgbuse")
-        yield UInt32(self, "lcbPlcupcRgbuse")
-        yield Int32(self, "fcPlcupcUsp")
-        yield UInt32(self, "lcbPlcupcUsp")
-        yield Int32(self, "fcSttbGlsyStyle")
-        yield UInt32(self, "lcbSttbGlsyStyle")
-        yield Int32(self, "fcPlgosl")
-        yield UInt32(self, "lcbPlgosl")
-        yield Int32(self, "fcPlcocx")
-        yield UInt32(self, "lcbPlcocx")
-        yield Int32(self, "fcPlcfbteLvc")
-        yield UInt32(self, "lcbPlcfbteLvc")
-        yield TIMESTAMP(self, "ftModified")
-        yield Int32(self, "fcPlcflvc")
-        yield UInt32(self, "lcbPlcflvc")
-        yield Int32(self, "fcPlcasumy")
-        yield UInt32(self, "lcbPlcasumy")
-        yield Int32(self, "fcPlcfgram")
-        yield UInt32(self, "lcbPlcfgram")
-        yield Int32(self, "fcSttbListNames")
-        yield UInt32(self, "lcbSttbListNames")
-        yield Int32(self, "fcSttbfUssr")
-        yield UInt32(self, "lcbSttbfUssr")
+def getOLE2Parser(ole2, path):
+    name = path+"[0]"
+    if name in ole2:
+        fragment = ole2[name]
+    else:
+        fragment = getRootParser(ole2)[name]
+    return guessParser(fragment.getSubIStream())
 
-        tail = (self.size - self.current_size) // 8
-        if tail:
-            yield RawBytes(self, "tail", tail)
-
-class WordDocumentFieldSet(BaseWordDocument, FieldSet):
-    pass
-
-class WordDocumentParser(BaseWordDocument, Parser):
+class WordDocumentParser(OLE2FragmentParser):
+    MAGIC='\xec\xa5' # 42476
     PARSER_TAGS = {
         "id": "word_document",
         "min_size": 8,
+        "magic": ((MAGIC, 0),),
         "description": "Microsoft Office Word document",
     }
     endian = LITTLE_ENDIAN
 
-    def __init__(self, stream, **kw):
-        Parser.__init__(self, stream, **kw)
+    def __init__(self, stream, **args):
+        OLE2FragmentParser.__init__(self, stream, **args)
 
     def validate(self):
+        if self.stream.readBytes(0,2) != self.MAGIC:
+            return "Invalid magic."
+        if self['FIB/nFib'].value not in (192,193):
+            return "Unknown FIB version."
         return True
 
+    def createFields(self):
+        yield FIB(self, "FIB", "File Information Block")
+        table = getOLE2Parser(self.ole2, "table"+str(self["FIB/fWhichTblStm"].value))
+
+        padding = (self['FIB/fcMin'].value - self.current_size//8)
+        if padding:
+            yield NullBytes(self, "padding[]", padding)
+        
+        # Guess whether the file uses UTF16 encoding.
+        is_unicode = False
+        if self['FIB/array2/ccpText'].value*2 == self['FIB/fcMax'].value - self['FIB/fcMin'].value:
+            is_unicode = True
+        for fieldname, textname in [('Text','text'),('Ftn','text_footnote'),
+        ('Hdr','text_header'),('Mcr','text_macro'),('Atn','text_annotation'),
+        ('Edn','text_endnote'),('Txbx','text_textbox'),('HdrTxbx','text_header_textbox')]:
+            size = self['FIB/array2/ccp'+fieldname].value
+            if size:
+                if is_unicode:
+                    yield String(self, textname, size*2, charset="UTF-16-LE")
+                else:
+                    yield Bytes(self, textname, size)
+
+        padding = (self['FIB/fcMax'].value - self.current_size//8)
+        if padding:
+            yield RawBytes(self, "padding[]", padding)
+
+class WidePascalString16(String):
+    def __init__(self, parent, name, description=None,
+    strip=None, nbytes=None, truncate=None):
+        Bytes.__init__(self, parent, name, 1, description)
+        
+        self._format = "WidePascalString16"
+        self._strip = strip
+        self._truncate = truncate
+        self._character_size = 2
+        self._charset = "UTF-16-LE"
+        self._content_offset = 2
+        self._content_size = self._character_size * self._parent.stream.readBits(
+            self.absolute_address, self._content_offset*8, self._parent.endian)
+        self._size = (self._content_size + self.content_offset) * 8
+
+class TableParsers(object):
+    class Bte(FieldSet):
+        'Bin Table Entry'
+        static_size = 32
+        def createFields(self):
+            yield Bits(self, "pn", 22, "Referenced page number")
+            yield Bits(self, "unused", 10)
+
+        def createValue(self):
+            return self["pn"].value
+
+    class Ffn(FieldSet):
+        'Font Family Name'
+        def createFields(self):
+            yield UInt8(self, "size", "Total length of this FFN in bytes, minus 1")
+            self._size = self["size"].value * 8 + 8
+            yield Bits(self, "prq", 2, "Pitch request")
+            yield Bit(self, "fTrueType", "Is font a TrueType font?")
+            yield Bits(self, "reserved[]", 1)
+            yield Bits(self, "ff", 3, "Font Family ID")
+            yield Bits(self, "reserved[]", 1)
+            yield UInt16(self, "wWeight", "Base weight of font")
+            yield UInt8(self, "chs", "Character set identifier")
+            yield UInt8(self, "ixchSzAlt", "Index into name to the name of the alternate font")
+            yield RawBytes(self, "panose", 10)
+            yield RawBytes(self, "fs", 24, "Font Signature")
+            yield CString(self, "name", charset="UTF-16-LE")
+            if self["ixchSzAlt"].value != 0:
+                yield CString(self, "nameAlt", charset="UTF-16-LE")
+
+        def createValue(self):
+            return self["name"].value
+
+    class Sttbf(FieldSet):
+        'String Table stored in File'
+        SttbfAssocDESC = {
+            0: "FileNext: unused",
+            1: "Dot: filename of associated template",
+            2: "Title: title of document",
+            3: "Subject: subject of document",
+            4: "KeyWords: keywords of document",
+            5: "Comments: comments of document",
+            6: "Author: author of document",
+            7: "LastRevBy: name of person who last revised the document",
+            8: "DataDoc: filename of data document",
+            9: "HeaderDoc: filename of header document",
+            10: "Criteria1: packed string used by print merge record selection",
+            11: "Criteria2: packed string used by print merge record selection",
+            12: "Criteria3: packed string used by print merge record selection",
+            13: "Criteria4: packed string used by print merge record selection",
+            14: "Criteria5: packed string used by print merge record selection",
+            15: "Criteria6: packed string used by print merge record selection",
+            16: "Criteria7: packed string used by print merge record selection",
+            17: "Max: maximum number of strings in string table",
+        }
+
+        def createFields(self):
+            if self.stream.readBytes(self.absolute_address, 2) == "\xff\xff":
+                yield Int16(self, "utf16_marker", "If this field is present, the Sttbf contains UTF-16 data.")
+                self.is_utf16 = True
+            else:
+                self.is_utf16 = False
+            yield UInt16(self, "count", "Number of strings in this Sttbf")
+            extra_data_field = UInt16(self, "extra_data_len", "Size of optional extra data after each string")
+            yield extra_data_field
+            extra_data_len = extra_data_field.value
+            for i in xrange(self["count"].value):
+                if self.name == "SttbfAssoc":
+                    desc = self.SttbfAssocDESC.get(i, None)
+                else:
+                    desc = None
+                if self.name == "Sttbfffn":
+                    yield TableParsers.Ffn(self, "string[]", desc)
+                elif self.is_utf16:
+                    yield WidePascalString16(self, "string[]", desc)
+                else:
+                    yield PascalString8(self, "string[]", desc)
+                if extra_data_len:
+                    yield RawBytes(self, "extra[]", extra_data_len)
+
+    class Plcf(FieldSet):
+        'Plex of CPs/FCs stored in file'
+        def createFields(self):
+            if self.size is None:
+                return
+            chunk_parser = None
+            size = None
+            if self.name.startswith("Plcfbte"):
+                chunk_parser = TableParsers.Bte
+            if not chunk_parser:
+                return
+            if size is None:
+                size = chunk_parser.static_size // 8
+            n = (self.size / 8 - 4) / (4 + size)
+            for i in xrange(n+1):
+                yield UInt32(self, "cp_fc[]", "CP or FC value")
+            for i in xrange(n):
+                yield chunk_parser(self, "obj[]")
+
+class WordTableParser(OLE2FragmentParser):
+    def createFields(self):
+        word_doc = getOLE2Parser(self.ole2, "word_doc")
+        if word_doc["FIB/fWhichTblStm"].value != int(self.ole2name[0]):
+            yield RawBytes(self, "inactive_table", self.datasize)
+            return
+        for fclcb in word_doc["FIB/array3"]:
+            if not isinstance(fclcb, FCLCB):
+                continue
+            if fclcb["fc"].value < 0 or fclcb["lcb"].value <= 0:
+                continue
+            self.seekByte(fclcb["fc"].value, relative=False)
+            if fclcb.name.startswith("Sttb"):
+                yield TableParsers.Sttbf(self, fclcb.name, size=fclcb["lcb"].value * 8)
+            elif fclcb.name.startswith("Plc"):
+                yield TableParsers.Plcf(self, fclcb.name, size=fclcb["lcb"].value * 8)
+            else:
+                yield RawBytes(self, fclcb.name, fclcb["lcb"].value, fclcb.description)
diff --git a/lib/hachoir_parser/parser_list.py b/lib/hachoir_parser/parser_list.py
index 0347840fdc99586cf0a320548a88c905bb60f9f9..38071550b2566893516bc389312b18a1a18685ea 100644
--- a/lib/hachoir_parser/parser_list.py
+++ b/lib/hachoir_parser/parser_list.py
@@ -198,8 +198,7 @@ class HachoirParserList(ParserList):
             return self.parser_list
 
         todo = []
-        import hachoir_parser
-        module = hachoir_parser
+        module = __import__("hachoir_parser")
         for attrname in dir(module):
             attr = getattr(module, attrname)
             if isinstance(attr, types.ModuleType):
diff --git a/lib/hachoir_parser/program/__init__.py b/lib/hachoir_parser/program/__init__.py
index 2e719f02b32505168181c2764a8572938755d339..261eaf15e91fdcbb5c77aa07228f79231b36cc4d 100644
--- a/lib/hachoir_parser/program/__init__.py
+++ b/lib/hachoir_parser/program/__init__.py
@@ -3,4 +3,5 @@ from hachoir_parser.program.exe import ExeFile
 from hachoir_parser.program.python import PythonCompiledFile
 from hachoir_parser.program.java import JavaCompiledClassFile
 from hachoir_parser.program.prc import PRCFile
+from hachoir_parser.program.nds import NdsFile
 
diff --git a/lib/hachoir_parser/program/elf.py b/lib/hachoir_parser/program/elf.py
index 3d5731e2244d0f245931454c38a6c420c1125ba7..4ddd6511739aa552a547c197762c5b6dfe8bd12e 100644
--- a/lib/hachoir_parser/program/elf.py
+++ b/lib/hachoir_parser/program/elf.py
@@ -1,44 +1,98 @@
 """
 ELF (Unix/BSD executable file format) parser.
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
 Creation date: 08 may 2006
 """
 
-from hachoir_parser import Parser
-from hachoir_core.field import (FieldSet, ParserError,
-    UInt8, UInt16, UInt32, Enum,
-    String, Bytes)
+from hachoir_parser import HachoirParser
+from hachoir_core.field import (RootSeekableFieldSet, FieldSet, ParserError, Bit, NullBits, RawBits,
+    UInt8, UInt16, UInt32, UInt64, Enum,
+    String, RawBytes, Bytes)
 from hachoir_core.text_handler import textHandler, hexadecimal
 from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 
 class ElfHeader(FieldSet):
-    static_size = 52*8
     LITTLE_ENDIAN_ID = 1
     BIG_ENDIAN_ID = 2
     MACHINE_NAME = {
+        # e_machine, EM_ defines
+        0: u"No machine",
         1: u"AT&T WE 32100",
         2: u"SPARC",
         3: u"Intel 80386",
         4: u"Motorola 68000",
         5: u"Motorola 88000",
+        6: u"Intel 80486",
         7: u"Intel 80860",
-        8: u"MIPS RS3000"
+        8: u"MIPS I Architecture",
+        9: u"Amdahl UTS on System/370",
+        10: u"MIPS RS3000 Little-endian",
+        11: u"IBM RS/6000 XXX reserved",
+        15: u"Hewlett-Packard PA-RISC",
+        16: u"NCube XXX reserved",
+        17: u"Fujitsu VPP500",
+        18: u"Enhanced instruction set SPARC",
+        19: u"Intel 80960",
+        20: u"PowerPC 32-bit",
+        21: u"PowerPC 64-bit",
+        36: u"NEC V800",
+        37: u"Fujitsu FR20",
+        38: u"TRW RH-32",
+        39: u"Motorola RCE",
+        40: u"Advanced RISC Machines (ARM)",
+        41: u"DIGITAL Alpha",
+        42: u"Hitachi Super-H",
+        43: u"SPARC Version 9",
+        44: u"Siemens Tricore",
+        45: u"Argonaut RISC Core",
+        46: u"Hitachi H8/300",
+        47: u"Hitachi H8/300H",
+        48: u"Hitachi H8S",
+        49: u"Hitachi H8/500",
+        50: u"Intel Merced (IA-64) Processor",
+        51: u"Stanford MIPS-X",
+        52: u"Motorola Coldfire",
+        53: u"Motorola MC68HC12",
+        62: u"Advanced Micro Devices x86-64",
+        75: u"DIGITAL VAX",
+        36902: u"used by NetBSD/alpha; obsolete",
     }
     CLASS_NAME = {
+        # e_ident[EI_CLASS], ELFCLASS defines
         1: u"32 bits",
         2: u"64 bits"
     }
     TYPE_NAME = {
+        # e_type, ET_ defines
              0: u"No file type",
              1: u"Relocatable file",
              2: u"Executable file",
              3: u"Shared object file",
              4: u"Core file",
         0xFF00: u"Processor-specific (0xFF00)",
-        0xFFFF: u"Processor-specific (0xFFFF)"
+        0xFFFF: u"Processor-specific (0xFFFF)",
+    }
+    OSABI_NAME = {
+        # e_ident[EI_OSABI], ELFOSABI_ defines
+        0: u"UNIX System V ABI",
+        1: u"HP-UX operating system",
+        2: u"NetBSD",
+        3: u"GNU/Linux",
+        4: u"GNU/Hurd",
+        5: u"86Open common IA32 ABI",
+        6: u"Solaris",
+        7: u"Monterey",
+        8: u"IRIX",
+        9: u"FreeBSD",
+        10: u"TRU64 UNIX",
+        11: u"Novell Modesto",
+        12: u"OpenBSD",
+        97: u"ARM",
+        255: u"Standalone (embedded) application",
     }
     ENDIAN_NAME = {
+        # e_ident[EI_DATA], ELFDATA defines
         LITTLE_ENDIAN_ID: "Little endian",
         BIG_ENDIAN_ID: "Big endian",
     }
@@ -46,23 +100,29 @@ class ElfHeader(FieldSet):
     def createFields(self):
         yield Bytes(self, "signature", 4, r'ELF signature ("\x7fELF")')
         yield Enum(UInt8(self, "class", "Class"), self.CLASS_NAME)
+        if self["class"].value == 1:
+            ElfLongWord = UInt32
+        else:
+            ElfLongWord = UInt64
         yield Enum(UInt8(self, "endian", "Endian"), self.ENDIAN_NAME)
         yield UInt8(self, "file_version", "File version")
-        yield String(self, "pad", 8, "Pad")
-        yield UInt8(self, "nb_ident", "Size of ident[]")
+        yield Enum(UInt8(self, "osabi_ident", "OS/syscall ABI identification"), self.OSABI_NAME)
+        yield UInt8(self, "abi_version", "syscall ABI version")
+        yield String(self, "pad", 7, "Pad")
+        
         yield Enum(UInt16(self, "type", "File type"), self.TYPE_NAME)
         yield Enum(UInt16(self, "machine", "Machine type"), self.MACHINE_NAME)
         yield UInt32(self, "version", "ELF format version")
-        yield UInt32(self, "entry", "Number of entries")
-        yield UInt32(self, "phoff", "Program header offset")
-        yield UInt32(self, "shoff", "Section header offset")
-        yield UInt32(self, "flags", "Flags")
+        yield textHandler(ElfLongWord(self, "entry", "Entry point"), hexadecimal)
+        yield ElfLongWord(self, "phoff", "Program header file offset")
+        yield ElfLongWord(self, "shoff", "Section header file offset")
+        yield UInt32(self, "flags", "Architecture-specific flags")
         yield UInt16(self, "ehsize", "Elf header size (this header)")
         yield UInt16(self, "phentsize", "Program header entry size")
         yield UInt16(self, "phnum", "Program header entry count")
         yield UInt16(self, "shentsize", "Section header entry size")
-        yield UInt16(self, "shnum", "Section header entre count")
-        yield UInt16(self, "shstrndx", "Section header strtab index")
+        yield UInt16(self, "shnum", "Section header entry count")
+        yield UInt16(self, "shstrndx", "Section header string table index")
 
     def isValid(self):
         if self["signature"].value != "\x7FELF":
@@ -73,70 +133,154 @@ class ElfHeader(FieldSet):
             return "Unknown endian (%s)" % self["endian"].value
         return ""
 
+class SectionFlags(FieldSet):
+    def createFields(self):
+        if self.root.endian == BIG_ENDIAN:
+            if self.root.is64bit:
+                yield RawBits(self, "reserved[]", 32)
+            yield RawBits(self, "processor_specific", 4, "Processor specific flags")
+            yield NullBits(self, "reserved[]", 17)
+            yield Bit(self, "is_tls", "Section contains TLS data?")
+            yield NullBits(self, "reserved[]", 7)
+            yield Bit(self, "is_exec", "Section contains executable instructions?")
+            yield Bit(self, "is_alloc", "Section occupies memory?")
+            yield Bit(self, "is_writable", "Section contains writable data?")
+        else:
+            yield Bit(self, "is_writable", "Section contains writable data?")
+            yield Bit(self, "is_alloc", "Section occupies memory?")
+            yield Bit(self, "is_exec", "Section contains executable instructions?")
+            yield NullBits(self, "reserved[]", 7)
+            yield Bit(self, "is_tls", "Section contains TLS data?")
+            yield RawBits(self, "processor_specific", 4, "Processor specific flags")
+            yield NullBits(self, "reserved[]", 17)
+            if self.root.is64bit:
+                yield RawBits(self, "reserved[]", 32)
+
+class SymbolStringTableOffset(UInt32):
+    def createDisplay(self):
+        section_index = self['/header/shstrndx'].value
+        section = self['/section['+str(section_index)+']']
+        text = section.value[self.value:]
+        return text.split('\0',1)[0]
+
 class SectionHeader32(FieldSet):
     static_size = 40*8
     TYPE_NAME = {
-        8: "BSS"
+        # sh_type, SHT_ defines
+        0: "Inactive",
+        1: "Program defined information",
+        2: "Symbol table section",
+        3: "String table section",
+        4: "Relocation section with addends",
+        5: "Symbol hash table section",
+        6: "Dynamic section",
+        7: "Note section",
+        8: "Block started by symbol (BSS) or No space section",
+        9: "Relocation section without addends",
+        10:"Reserved - purpose unknown",
+        11:"Dynamic symbol table section",
     }
 
     def createFields(self):
-        yield UInt32(self, "name", "Name")
-        yield Enum(UInt32(self, "type", "Type"), self.TYPE_NAME)
-        yield UInt32(self, "flags", "Flags")
+        yield SymbolStringTableOffset(self, "name", "Section name (index into section header string table)")
+        yield Enum(textHandler(UInt32(self, "type", "Section type"), hexadecimal), self.TYPE_NAME)
+        yield SectionFlags(self, "flags", "Section flags")
         yield textHandler(UInt32(self, "VMA", "Virtual memory address"), hexadecimal)
-        yield textHandler(UInt32(self, "LMA", "Logical memory address (in file)"), hexadecimal)
-        yield textHandler(UInt32(self, "size", "Size"), hexadecimal)
-        yield UInt32(self, "link", "Link")
-        yield UInt32(self, "info", "Information")
-        yield UInt32(self, "addr_align", "Address alignment")
-        yield UInt32(self, "entry_size", "Entry size")
+        yield textHandler(UInt32(self, "LMA", "Logical memory address (offset in file)"), hexadecimal)
+        yield textHandler(UInt32(self, "size", "Section size (bytes)"), hexadecimal)
+        yield UInt32(self, "link", "Index of a related section")
+        yield UInt32(self, "info", "Type-dependent information")
+        yield UInt32(self, "addr_align", "Address alignment (bytes)")
+        yield UInt32(self, "entry_size", "Size of each entry in section")
 
     def createDescription(self):
         return "Section header (name: %s, type: %s)" % \
-            (self["name"].value, self["type"].display)
+            (self["name"].display, self["type"].display)
+
+class SectionHeader64(SectionHeader32):
+    static_size = 64*8
+
+    def createFields(self):
+        yield SymbolStringTableOffset(self, "name", "Section name (index into section header string table)")
+        yield Enum(textHandler(UInt32(self, "type", "Section type"), hexadecimal), self.TYPE_NAME)
+        yield SectionFlags(self, "flags", "Section flags")
+        yield textHandler(UInt64(self, "VMA", "Virtual memory address"), hexadecimal)
+        yield textHandler(UInt64(self, "LMA", "Logical memory address (offset in file)"), hexadecimal)
+        yield textHandler(UInt64(self, "size", "Section size (bytes)"), hexadecimal)
+        yield UInt32(self, "link", "Index of a related section")
+        yield UInt32(self, "info", "Type-dependent information")
+        yield UInt64(self, "addr_align", "Address alignment (bytes)")
+        yield UInt64(self, "entry_size", "Size of each entry in section")
+
+class ProgramFlags(FieldSet):
+    static_size = 32
+    FLAGS = (('pf_r','readable'),('pf_w','writable'),('pf_x','executable'))
+
+    def createFields(self):
+        if self.root.endian == BIG_ENDIAN:
+            yield NullBits(self, "padding[]", 29)
+            for fld, desc in self.FLAGS:
+                yield Bit(self, fld, "Segment is " + desc)
+        else:
+            for fld, desc in reversed(self.FLAGS):
+                yield Bit(self, fld, "Segment is " + desc)
+            yield NullBits(self, "padding[]", 29)
+
+    def createDescription(self):
+        attribs=[]
+        for fld, desc in self.FLAGS:
+            if self[fld].value:
+                attribs.append(desc)
+        return 'Segment is '+', '.join(attribs)
 
 class ProgramHeader32(FieldSet):
     TYPE_NAME = {
-        3: "Dynamic library"
+        # p_type, PT_ defines
+        0: u"Unused program header table entry",
+        1: u"Loadable program segment",
+        2: u"Dynamic linking information",
+        3: u"Program interpreter",
+        4: u"Auxiliary information",
+        5: u"Reserved, unspecified semantics",
+        6: u"Entry for header table itself",
+        7: u"Thread Local Storage segment",
+        0x70000000: u"MIPS_REGINFO",
     }
     static_size = 32*8
 
     def createFields(self):
-        yield Enum(UInt16(self, "type", "Type"), ProgramHeader32.TYPE_NAME)
-        yield UInt16(self, "flags", "Flags")
+        yield Enum(UInt32(self, "type", "Segment type"), ProgramHeader32.TYPE_NAME)
         yield UInt32(self, "offset", "Offset")
         yield textHandler(UInt32(self, "vaddr", "V. address"), hexadecimal)
         yield textHandler(UInt32(self, "paddr", "P. address"), hexadecimal)
         yield UInt32(self, "file_size", "File size")
         yield UInt32(self, "mem_size", "Memory size")
-        yield UInt32(self, "align", "Alignment")
-        yield UInt32(self, "xxx", "???")
+        yield ProgramFlags(self, "flags")
+        yield UInt32(self, "align", "Alignment padding")
 
     def createDescription(self):
         return "Program Header (%s)" % self["type"].display
 
-def sortSection(a, b):
-    return int(a["offset"] - b["offset"])
-
-#class Sections(FieldSet):
-#    def createFields?(self, stream, parent, sections):
-#        for section in sections:
-#            ofs = section["offset"]
-#            size = section["file_size"]
-#            if size != 0:
-#                sub = stream.createSub(ofs, size)
-#                #yield DeflateFilter(self, "section[]", sub, size, Section,  "Section"))
-#                chunk = self.doRead("section[]", "Section", (Section,), {"stream": sub})
-#            else:
-#                chunk = self.doRead("section[]", "Section", (FormatChunk, "string[0]"))
-#            chunk.description = "ELF section (in file: %s..%s)" % (ofs, ofs+size)
-
-class ElfFile(Parser):
+class ProgramHeader64(ProgramHeader32):
+    static_size = 56*8
+
+    def createFields(self):
+        yield Enum(UInt32(self, "type", "Segment type"), ProgramHeader32.TYPE_NAME)
+        yield ProgramFlags(self, "flags")
+        yield UInt64(self, "offset", "Offset")
+        yield textHandler(UInt64(self, "vaddr", "V. address"), hexadecimal)
+        yield textHandler(UInt64(self, "paddr", "P. address"), hexadecimal)
+        yield UInt64(self, "file_size", "File size")
+        yield UInt64(self, "mem_size", "Memory size")
+        yield UInt64(self, "align", "Alignment padding")
+
+class ElfFile(HachoirParser, RootSeekableFieldSet):
+    MAGIC = "\x7FELF"
     PARSER_TAGS = {
         "id": "elf",
         "category": "program",
         "file_ext": ("so", ""),
-        "min_size": ElfHeader.static_size,  # At least one program header
+        "min_size": 52*8,  # At least one program header
         "mime": (
             u"application/x-executable",
             u"application/x-object",
@@ -148,7 +292,13 @@ class ElfFile(Parser):
     }
     endian = LITTLE_ENDIAN
 
+    def __init__(self, stream, **args):
+        RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
+        HachoirParser.__init__(self, stream, **args)
+
     def validate(self):
+        if self.stream.readBytes(0, len(self.MAGIC)) != self.MAGIC:
+            return "Invalid magic"
         err = self["header"].isValid()
         if err:
             return err
@@ -163,23 +313,27 @@ class ElfFile(Parser):
 
         # Parse header and program headers
         yield ElfHeader(self, "header", "Header")
+        self.is64bit = (self["header/class"].value == 2)
+
         for index in xrange(self["header/phnum"].value):
-            yield ProgramHeader32(self, "prg_header[]")
-
-        if False:
-            raise ParserError("TODO: Parse sections...")
-            #sections = self.array("prg_header")
-            #size = self["header/shoff"].value - self.current_size//8
-            #chunk = self.doRead("data", "Data", (DeflateFilter, stream, size, Sections, sections))
-            #chunk.description = "Sections (use an evil hack to manage share same data on differents parts)"
-            #assert self.current_size//8 == self["header/shoff"].value
-        else:
-            raw = self.seekByte(self["header/shoff"].value, "raw[]", relative=False)
-            if raw:
-                yield raw
+            if self.is64bit:
+                yield ProgramHeader64(self, "prg_header[]")
+            else:
+                yield ProgramHeader32(self, "prg_header[]")
 
+        self.seekByte(self["header/shoff"].value, relative=False)
+
+        for index in xrange(self["header/shnum"].value):
+            if self.is64bit:
+                yield SectionHeader64(self, "section_header[]")
+            else:
+                yield SectionHeader32(self, "section_header[]")
+        
         for index in xrange(self["header/shnum"].value):
-            yield SectionHeader32(self, "section_header[]")
+            field = self["section_header["+str(index)+"]"]
+            if field['size'].value != 0:
+                self.seekByte(field['LMA'].value, relative=False)
+                yield RawBytes(self, "section["+str(index)+"]", field['size'].value)
 
     def createDescription(self):
         return "ELF Unix/BSD program/library: %s" % (
diff --git a/lib/hachoir_parser/program/exe_pe.py b/lib/hachoir_parser/program/exe_pe.py
index b3b241a58f84a2ce1ed8a1a408ae84895b4423c7..d769e91d0aed9eb90c5afbff8b639380a6bd1229 100644
--- a/lib/hachoir_parser/program/exe_pe.py
+++ b/lib/hachoir_parser/program/exe_pe.py
@@ -72,7 +72,7 @@ class SectionHeader(FieldSet):
                 return "section_%s" % name
         except HACHOIR_ERRORS, err:
             self.warning(unicode(err))
-            return "section[]"
+        return "section[]"
 
 class DataDirectory(FieldSet):
     def createFields(self):
diff --git a/lib/hachoir_parser/program/nds.py b/lib/hachoir_parser/program/nds.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc6e5c44406769aee07d9211acec4f319ec1a25b
--- /dev/null
+++ b/lib/hachoir_parser/program/nds.py
@@ -0,0 +1,359 @@
+"""
+Nintendo DS .nds game file parser
+
+File format references:
+- http://www.bottledlight.com/ds/index.php/FileFormats/NDSFormat
+- http://imrannazar.com/The-Smallest-NDS-File
+- http://darkfader.net/ds/files/ndstool.cpp
+- http://crackerscrap.com/docs/dsromstructure.html
+- http://nocash.emubase.de/gbatek.htm
+"""
+
+from hachoir_parser import Parser
+from hachoir_core.field import (ParserError,
+    UInt8, UInt16, UInt32, UInt64, String, RawBytes, SubFile, FieldSet, NullBits, Bit, Bits, Bytes,
+    SeekableFieldSet, RootSeekableFieldSet)
+from hachoir_core.text_handler import textHandler, hexadecimal
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
+
+
+"""
+CRC16 Calculation
+
+Modified from:
+http://www.mail-archive.com/python-list@python.org/msg47844.html
+
+Original License:
+crc16.py by Bryan G. Olson, 2005
+This module is free software and may be used and
+distributed under the same terms as Python itself.
+"""
+class CRC16:
+    _table = None
+
+    def _initTable (self):
+        from array import array
+
+        # CRC-16 poly: p(x) = x**16 + x**15 + x**2 + 1
+        # top bit implicit, reflected
+        poly = 0xa001
+        CRC16._table = array('H')
+        for byte in range(256):
+             crc = 0
+             for bit in range(8):
+                 if (byte ^ crc) & 1:
+                     crc = (crc >> 1) ^ poly
+                 else:
+                     crc >>= 1
+                 byte >>= 1
+             CRC16._table.append(crc)
+
+    def checksum (self, string, value):
+        if CRC16._table is None:
+            self._initTable()
+
+        for ch in string:
+            value = self._table[ord(ch) ^ (value & 0xff)] ^ (value >> 8)
+        return value
+
+
+class Crc16(UInt16):
+    "16 bit field for calculating and comparing CRC-16 of specified string"
+    def __init__(self, parent, name, targetBytes):
+        UInt16.__init__(self, parent, name)
+        self.targetBytes = targetBytes
+
+    def createDescription(self):
+        crc = CRC16().checksum(self.targetBytes, 0xffff)
+        if crc == self.value:
+            return "matches CRC of %d bytes" % len(self.targetBytes)
+        else:
+            return "mismatch (calculated CRC %d for %d bytes)" % (crc, len(self.targetBytes))
+
+
+class FileNameDirTable(FieldSet):
+    static_size = (4+2+2)*8
+    def createFields(self):
+        yield UInt32(self, "entry_start")
+        yield UInt16(self, "entry_file_id")
+        yield UInt16(self, "parent_id")
+
+    def createDescription(self):
+        return "first file id: %d; parent directory id: %d (%d)" % (self["entry_file_id"].value, self["parent_id"].value, self["parent_id"].value & 0xFFF)
+
+class FileNameEntry(FieldSet):
+    def createFields(self):
+        yield Bits(self, "name_len", 7)
+        yield Bit(self, "is_directory")
+        yield String(self, "name", self["name_len"].value)
+        if self["is_directory"].value:
+            yield UInt16(self, "dir_id")
+
+    def createDescription(self):
+        s = ""
+        if self["is_directory"].value:
+            s = "[D] "
+        return s + self["name"].value
+
+class Directory(FieldSet):
+    def createFields(self):
+        while True:
+            fne = FileNameEntry(self, "entry[]")
+            if fne["name_len"].value == 0:
+                yield UInt8(self, "end_marker")
+                break
+            yield fne
+
+
+class FileNameTable(SeekableFieldSet):
+    def createFields(self):
+        self.startOffset = self.absolute_address / 8
+
+        # parent_id of first FileNameDirTable contains number of directories:
+        dt = FileNameDirTable(self, "dir_table[]")
+        numDirs = dt["parent_id"].value
+        yield dt
+
+        for i in range(1, numDirs):
+            yield FileNameDirTable(self, "dir_table[]")
+
+        for i in range(0, numDirs):
+            dt = self["dir_table[%d]" % i]
+            offset = self.startOffset + dt["entry_start"].value
+            self.seekByte(offset, relative=False)
+            yield Directory(self, "directory[]")
+
+
+class FATFileEntry(FieldSet):
+    static_size = 2*4*8
+    def createFields(self):
+        yield UInt32(self, "start")
+        yield UInt32(self, "end")
+
+    def createDescription(self):
+        return "start: %d; size: %d" % (self["start"].value, self["end"].value - self["start"].value)
+
+class FATContent(FieldSet):
+    def createFields(self):
+        num_entries = self.parent["header"]["fat_size"].value / 8
+        for i in range(0, num_entries):
+            yield FATFileEntry(self, "entry[]")
+
+
+
+class BannerTile(FieldSet):
+    static_size = 32*8
+    def createFields(self):
+        for y in range(8):
+            for x in range(8):
+                yield Bits(self, "pixel[%d,%d]" % (x,y), 4)
+
+class BannerIcon(FieldSet):
+    static_size = 16*32*8
+    def createFields(self):
+        for y in range(4):
+            for x in range(4):
+                yield BannerTile(self, "tile[%d,%d]" % (x,y))
+
+class NdsColor(FieldSet):
+    static_size = 16
+    def createFields(self):
+        yield Bits(self, "red", 5)
+        yield Bits(self, "green", 5)
+        yield Bits(self, "blue", 5)
+        yield NullBits(self, "pad", 1)
+
+    def createDescription(self):
+        return "#%02x%02x%02x" % (self["red"].value << 3, self["green"].value << 3, self["blue"].value << 3)
+
+class Banner(FieldSet):
+    static_size = 2112*8
+    def createFields(self):
+        yield UInt16(self, "version")
+        # CRC of this structure, excluding first 32 bytes:
+        yield Crc16(self, "crc", self.stream.readBytes(self.absolute_address+(32*8), (2112-32)))
+        yield RawBytes(self, "reserved", 28)
+        yield BannerIcon(self, "icon_data")
+        for i in range(0, 16):
+            yield NdsColor(self, "palette_color[]")
+        yield String(self, "title_jp", 256, charset="UTF-16-LE", truncate="\0")
+        yield String(self, "title_en", 256, charset="UTF-16-LE", truncate="\0")
+        yield String(self, "title_fr", 256, charset="UTF-16-LE", truncate="\0")
+        yield String(self, "title_de", 256, charset="UTF-16-LE", truncate="\0")
+        yield String(self, "title_it", 256, charset="UTF-16-LE", truncate="\0")
+        yield String(self, "title_es", 256, charset="UTF-16-LE", truncate="\0")
+
+
+class Overlay(FieldSet):
+    static_size = 8*4*8
+    def createFields(self):
+        yield UInt32(self, "id")
+        yield textHandler(UInt32(self, "ram_address"), hexadecimal)
+        yield UInt32(self, "ram_size")
+        yield UInt32(self, "bss_size")
+        yield textHandler(UInt32(self, "init_start_address"), hexadecimal)
+        yield textHandler(UInt32(self, "init_end_address"), hexadecimal)
+        yield UInt32(self, "file_id")
+        yield RawBytes(self, "reserved[]", 4)
+
+    def createDescription(self):
+        return "file #%d, %d (+%d) bytes to 0x%08x" % (
+            self["file_id"].value, self["ram_size"].value, self["bss_size"].value, self["ram_address"].value)
+
+
+class SecureArea(FieldSet):
+    static_size=2048*8
+    def createFields(self):
+        yield textHandler(UInt64(self, "id"), hexadecimal)
+        if self["id"].value == 0xe7ffdeffe7ffdeff: # indicates that secure area is decrypted
+            yield Bytes(self, "fixed[]", 6) # always \xff\xde\xff\xe7\xff\xde
+            yield Crc16(self, "header_crc16", self.stream.readBytes(self.absolute_address+(16*8), 2048-16))
+            yield RawBytes(self, "unknown[]", 2048-16-2)
+            yield Bytes(self, "fixed[]", 2) # always \0\0
+        else:
+            yield RawBytes(self, "encrypted[]", 2048-8)
+
+
+class DeviceSize(UInt8):
+    def createDescription(self):
+        return "%d Mbit" % ((2**(20+self.value)) / (1024*1024))
+
+class Header(FieldSet):
+    def createFields(self):
+        yield String(self, "game_title", 12, truncate="\0")
+        yield String(self, "game_code", 4)
+        yield String(self, "maker_code", 2)
+        yield UInt8(self, "unit_code")
+        yield UInt8(self, "device_code")
+
+        yield DeviceSize(self, "card_size")
+        yield String(self, "card_info", 9)
+        yield UInt8(self, "rom_version")
+        yield Bits(self, "unknown_flags[]", 2)
+        yield Bit(self, "autostart_flag")
+        yield Bits(self, "unknown_flags[]", 5)
+
+        yield UInt32(self, "arm9_source", "ARM9 ROM offset")
+        yield textHandler(UInt32(self, "arm9_execute_addr", "ARM9 entry address"), hexadecimal)
+        yield textHandler(UInt32(self, "arm9_copy_to_addr", "ARM9 RAM address"), hexadecimal)
+        yield UInt32(self, "arm9_bin_size", "ARM9 code size")
+
+        yield UInt32(self, "arm7_source", "ARM7 ROM offset")
+        yield textHandler(UInt32(self, "arm7_execute_addr", "ARM7 entry address"), hexadecimal)
+        yield textHandler(UInt32(self, "arm7_copy_to_addr", "ARM7 RAM address"), hexadecimal)
+        yield UInt32(self, "arm7_bin_size", "ARM7 code size")
+
+        yield UInt32(self, "filename_table_offset")
+        yield UInt32(self, "filename_table_size")
+        yield UInt32(self, "fat_offset")
+        yield UInt32(self, "fat_size")
+
+        yield UInt32(self, "arm9_overlay_src")
+        yield UInt32(self, "arm9_overlay_size")
+        yield UInt32(self, "arm7_overlay_src")
+        yield UInt32(self, "arm7_overlay_size")
+
+        yield textHandler(UInt32(self, "ctl_read_flags"), hexadecimal)
+        yield textHandler(UInt32(self, "ctl_init_flags"), hexadecimal)
+        yield UInt32(self, "banner_offset")
+        yield Crc16(self, "secure_crc16", self.stream.readBytes(0x4000*8, 0x4000))
+        yield UInt16(self, "rom_timeout")
+
+        yield UInt32(self, "arm9_unk_addr")
+        yield UInt32(self, "arm7_unk_addr")
+        yield UInt64(self, "unenc_mode_magic")
+
+        yield UInt32(self, "rom_size")
+        yield UInt32(self, "header_size")
+
+        yield RawBytes(self, "unknown[]", 36)
+        yield String(self, "passme_autoboot_detect", 4)
+        yield RawBytes(self, "unknown[]", 16)
+
+        yield RawBytes(self, "gba_logo", 156)
+        yield Crc16(self, "logo_crc16", self.stream.readBytes(0xc0*8, 156))
+        yield Crc16(self, "header_crc16", self.stream.readBytes(0, 350))
+
+        yield UInt32(self, "debug_rom_offset")
+        yield UInt32(self, "debug_size")
+        yield textHandler(UInt32(self, "debug_ram_address"), hexadecimal)
+
+
+class NdsFile(Parser, RootSeekableFieldSet):
+    PARSER_TAGS = {
+        "id": "nds_file",
+        "category": "program",
+        "file_ext": ("nds",),
+        "mime": (u"application/octet-stream",),
+        "min_size": 352 * 8, # just a minimal header
+        "description": "Nintendo DS game file",
+    }
+
+    endian = LITTLE_ENDIAN
+
+    def validate(self):
+        try:
+            header = self["header"]
+        except Exception, e:
+            return False
+
+        return (self.stream.readBytes(0, 1) != "\0"
+            and (header["device_code"].value & 7) == 0
+            and header["header_size"].value >= 352
+            and header["card_size"].value < 15 # arbitrary limit at 32Gbit
+            and header["arm9_bin_size"].value > 0 and header["arm9_bin_size"].value <= 0x3bfe00
+            and header["arm7_bin_size"].value > 0 and header["arm7_bin_size"].value <= 0x3bfe00
+            and header["arm9_source"].value + header["arm9_bin_size"].value < self._size
+            and header["arm7_source"].value + header["arm7_bin_size"].value < self._size
+            and header["arm9_execute_addr"].value >= 0x02000000 and header["arm9_execute_addr"].value <= 0x023bfe00
+            and header["arm9_copy_to_addr"].value >= 0x02000000 and header["arm9_copy_to_addr"].value <= 0x023bfe00
+            and header["arm7_execute_addr"].value >= 0x02000000 and header["arm7_execute_addr"].value <= 0x03807e00
+            and header["arm7_copy_to_addr"].value >= 0x02000000 and header["arm7_copy_to_addr"].value <= 0x03807e00
+            )
+
+    def createFields(self):
+        # Header
+        yield Header(self, "header")
+
+        # Secure Area
+        if self["header"]["arm9_source"].value >= 0x4000 and self["header"]["arm9_source"].value < 0x8000:
+            secStart = self["header"]["arm9_source"].value & 0xfffff000
+            self.seekByte(secStart, relative=False)
+            yield SecureArea(self, "secure_area", size=0x8000-secStart)
+
+        # ARM9 binary
+        self.seekByte(self["header"]["arm9_source"].value, relative=False)
+        yield RawBytes(self, "arm9_bin", self["header"]["arm9_bin_size"].value)
+
+        # ARM7 binary
+        self.seekByte(self["header"]["arm7_source"].value, relative=False)
+        yield RawBytes(self, "arm7_bin", self["header"]["arm7_bin_size"].value)
+
+        # File Name Table
+        if self["header"]["filename_table_size"].value > 0:
+            self.seekByte(self["header"]["filename_table_offset"].value, relative=False)
+            yield FileNameTable(self, "filename_table", size=self["header"]["filename_table_size"].value*8)
+
+        # FAT
+        if self["header"]["fat_size"].value > 0:
+            self.seekByte(self["header"]["fat_offset"].value, relative=False)
+            yield FATContent(self, "fat_content", size=self["header"]["fat_size"].value*8)
+
+        # banner
+        if self["header"]["banner_offset"].value > 0:
+            self.seekByte(self["header"]["banner_offset"].value, relative=False)
+            yield Banner(self, "banner")
+
+        # ARM9 overlays
+        if self["header"]["arm9_overlay_src"].value > 0:
+            self.seekByte(self["header"]["arm9_overlay_src"].value, relative=False)
+            numOvls = self["header"]["arm9_overlay_size"].value / (8*4)
+            for i in range(numOvls):
+                yield Overlay(self, "arm9_overlay[]")
+
+        # files
+        if self["header"]["fat_size"].value > 0:
+            for field in self["fat_content"]:
+                if field["end"].value > field["start"].value:
+                    self.seekByte(field["start"].value, relative=False)
+                    yield SubFile(self, "file[]", field["end"].value - field["start"].value)
diff --git a/lib/hachoir_parser/program/python.py b/lib/hachoir_parser/program/python.py
index 6eea32bc7db1c611dde7b5f8a71cf633c657c703..f408fb2d2a7acedc07c7f1a717e6a7a08bc6702d 100644
--- a/lib/hachoir_parser/program/python.py
+++ b/lib/hachoir_parser/program/python.py
@@ -268,6 +268,7 @@ class PythonCompiledFile(Parser):
     MAGIC = {
         # Python 1.x
         20121: ("1.5", 0x1050000),
+        50428: ("1.6", 0x1060000),
 
         # Python 2.x
         50823: ("2.0", 0x2000000),
@@ -286,6 +287,13 @@ class PythonCompiledFile(Parser):
         62111: ("2.5b3", 0x2050000),
         62121: ("2.5c1", 0x2050000),
         62131: ("2.5c2", 0x2050000),
+        62151: ("2.6a0", 0x2070000),
+        62161: ("2.6a1", 0x2070000),
+        62171: ("2.7a0", 0x2070000),
+        62181: ("2.7a0", 0x2070000),
+        62191: ("2.7a0", 0x2070000),
+        62201: ("2.7a0", 0x2070000),
+        62211: ("2.7a0", 0x2070000),
 
         # Python 3.x
         3000:  ("3.0 (3000)",  0x3000000),
@@ -295,14 +303,20 @@ class PythonCompiledFile(Parser):
         3040:  ("3.0 (3040)",  0x3000000),
         3050:  ("3.0 (3050)",  0x3000000),
         3060:  ("3.0 (3060)",  0x3000000),
-        3070:  ("3.0 (3070)",  0x3000000),
-        3080:  ("3.0 (3080)",  0x3000000),
-        3090:  ("3.0 (3090)",  0x3000000),
-        3100:  ("3.0 (3100)",  0x3000000),
-        3102:  ("3.0 (3102)",  0x3000000),
-        3110:  ("3.0a4",       0x3000000),
-        3130:  ("3.0a5",       0x3000000),
-        3131:  ("3.0a5 unicode",       0x3000000),
+        3061:  ("3.0 (3061)",  0x3000000),
+        3071:  ("3.0 (3071)",  0x3000000),
+        3081:  ("3.0 (3081)",  0x3000000),
+        3091:  ("3.0 (3091)",  0x3000000),
+        3101:  ("3.0 (3101)",  0x3000000),
+        3103:  ("3.0 (3103)",  0x3000000),
+        3111:  ("3.0a4",       0x3000000),
+        3131:  ("3.0a5",       0x3000000),
+        3141:  ("3.1a0",       0x3010000),
+        3151:  ("3.1a0",       0x3010000),
+        3160:  ("3.2a0",       0x3020000),
+        3170:  ("3.2a1",       0x3020000),
+        3180:  ("3.2a2",       0x3020000),
+        3190:  ("Python 3.3a0",       0x3030000),
     }
 
     # Dictionnary which associate the pyc signature (4-byte long string)
diff --git a/lib/hachoir_parser/template.py b/lib/hachoir_parser/template.py
index 836215c1217b625c697f637a24580e889d39b6cb..2b75eb6ef54ef5e29b2b1822d4a20d0cab203ce2 100644
--- a/lib/hachoir_parser/template.py
+++ b/lib/hachoir_parser/template.py
@@ -23,7 +23,7 @@ class TODOFile(Parser):
         "id": "TODO",
         "category": "TODO",    # "archive", "audio", "container", ...
         "file_ext": ("TODO",), # TODO: Example ("bmp",) to parse the file "image.bmp"
-        "mime": (u"TODO"),      # TODO: Example: "image/png"
+        "mime": (u"TODO",),    # TODO: Example: "image/png"
         "min_size": 0,         # TODO: Minimum file size (x bits, or x*8 in bytes)
         "description": "TODO", # TODO: Example: "A bitmap picture"
     }
diff --git a/lib/hachoir_parser/version.py b/lib/hachoir_parser/version.py
index 28d1e616bd4338d40500d8611f035ace7833bed0..6571743e9f715a46c6610ea29244d1c5eb051605 100644
--- a/lib/hachoir_parser/version.py
+++ b/lib/hachoir_parser/version.py
@@ -1,4 +1,4 @@
-__version__ = "1.3.4"
+__version__ = "1.3.5"
 PACKAGE = "hachoir-parser"
 WEBSITE = "http://bitbucket.org/haypo/hachoir/wiki/hachoir-parser"
 LICENSE = 'GNU GPL v2'
diff --git a/lib/hachoir_parser/video/mov.py b/lib/hachoir_parser/video/mov.py
index f6b0a8ab6ff1af30bc9582efdd290cb5ee283999..1ab6ac511bd494d8af9daf1a011a01d7500d4dff 100644
--- a/lib/hachoir_parser/video/mov.py
+++ b/lib/hachoir_parser/video/mov.py
@@ -10,28 +10,48 @@ Documents:
   http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
 - File type (ftyp):
   http://www.ftyps.com/
+- MPEG4 standard
+  http://neuron2.net/library/avc/c041828_ISO_IEC_14496-12_2005%28E%29.pdf
 
-Author: Victor Stinner
+Author: Victor Stinner, Robert Xiao
 Creation: 2 august 2006
 """
 
 from hachoir_parser import Parser
+from hachoir_parser.common.win32 import GUID
 from hachoir_core.field import (ParserError, FieldSet, MissingField,
-    UInt8, Int16, UInt16, UInt32, TimestampMac32,
-    String, PascalString8, CString,
-    RawBytes, PaddingBytes)
+    Enum,
+    Bit, NullBits, Bits, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, TimestampMac32,
+    String, PascalString8, PascalString16, CString,
+    RawBytes, NullBytes, PaddingBytes)
 from hachoir_core.endian import BIG_ENDIAN
 from hachoir_core.text_handler import textHandler, hexadecimal
 
-class QTFloat32(FieldSet):
-    static_size = 32
-    def createFields(self):
-        yield Int16(self, "int_part")
-        yield UInt16(self, "float_part")
-    def createValue(self):
-        return self["int_part"].value + float(self["float_part"].value) / 65535
-    def createDescription(self):
-        return str(self.value)
+from hachoir_core.tools import MAC_TIMESTAMP_T0, timedelta
+def timestampMac64(value):
+    if not isinstance(value, (float, int, long)):
+        raise TypeError("an integer or float is required")
+    return MAC_TIMESTAMP_T0 + timedelta(seconds=value)
+from hachoir_core.field.timestamp import timestampFactory
+TimestampMac64 = timestampFactory("TimestampMac64", timestampMac64, 64)
+
+def fixedFloatFactory(name, int_bits, float_bits, doc):
+    size = int_bits + float_bits
+    class Float(FieldSet):
+        static_size = size
+        __doc__ = doc
+        def createFields(self):
+            yield Bits(self, "int_part", int_bits)
+            yield Bits(self, "float_part", float_bits)
+        def createValue(self):
+            return self["int_part"].value + float(self["float_part"].value) / (1<<float_bits)
+    klass = Float
+    klass.__name__ = name
+    return klass
+
+QTFloat16 = fixedFloatFactory("QTFloat32", 8, 8, "8.8 fixed point number")
+QTFloat32 = fixedFloatFactory("QTFloat32", 16, 16, "16.16 fixed point number")
+QTFloat2_30 = fixedFloatFactory("QTFloat2_30", 2, 30, "2.30 fixed point number")
 
 class AtomList(FieldSet):
     def createFields(self):
@@ -40,67 +60,304 @@ class AtomList(FieldSet):
 
 class TrackHeader(FieldSet):
     def createFields(self):
-        yield textHandler(UInt8(self, "version"), hexadecimal)
-
-        # TODO: sum of :
-        # TrackEnabled = 1;
-        # TrackInMovie = 2;
-        # TrackInPreview = 4;
-        # TrackInPoster = 8
-        yield RawBytes(self, "flags", 3)
+        yield UInt8(self, "version", "Version (0 or 1)")
+        yield NullBits(self, "flags", 20)
+        yield Bit(self, "is_in_poster")
+        yield Bit(self, "is_in_preview", "Is this track used when previewing the presentation?")
+        yield Bit(self, "is_in_movie", "Is this track used in the presentation?")
+        yield Bit(self, "is_enabled", "Is this track enabled?")
 
-        yield TimestampMac32(self, "creation_date")
-        yield TimestampMac32(self, "lastmod_date")
-        yield UInt32(self, "track_id")
-        yield PaddingBytes(self, "reserved[]", 8)
-        yield UInt32(self, "duration")
-        yield PaddingBytes(self, "reserved[]", 8)
-        yield Int16(self, "video_layer", "Middle is 0, negative in front")
-        yield PaddingBytes(self, "other", 2)
+        if self['version'].value == 0:
+            # 32-bit version
+            yield TimestampMac32(self, "creation_date", "Creation time of this track")
+            yield TimestampMac32(self, "lastmod_date", "Last modification time of this track")
+            yield UInt32(self, "track_id", "Unique nonzero identifier of this track within the presentation")
+            yield NullBytes(self, "reserved[]", 4)
+            yield UInt32(self, "duration", "Length of track, in movie time-units")
+        elif self['version'].value == 1:
+            # 64-bit version
+            yield TimestampMac64(self, "creation_date", "Creation time of this track")
+            yield TimestampMac64(self, "lastmod_date", "Last modification time of this track")
+            yield UInt32(self, "track_id", "Unique nonzero identifier of this track within the presentation")
+            yield NullBytes(self, "reserved[]", 4)
+            yield UInt64(self, "duration", "Length of track, in movie time-units")
+        yield NullBytes(self, "reserved[]", 8)
+        yield Int16(self, "video_layer", "Middle layer is 0; lower numbers are closer to the viewer")
+        yield Int16(self, "alternate_group", "Group ID that this track belongs to (0=no group)")
+        yield QTFloat16(self, "volume", "Track relative audio volume (1.0 = full)")
+        yield NullBytes(self, "reserved[]", 2)
         yield QTFloat32(self, "geom_a", "Width scale")
         yield QTFloat32(self, "geom_b", "Width rotate")
-        yield QTFloat32(self, "geom_u", "Width angle")
+        yield QTFloat2_30(self, "geom_u", "Width angle")
         yield QTFloat32(self, "geom_c", "Height rotate")
         yield QTFloat32(self, "geom_d", "Height scale")
-        yield QTFloat32(self, "geom_v", "Height angle")
+        yield QTFloat2_30(self, "geom_v", "Height angle")
         yield QTFloat32(self, "geom_x", "Position X")
         yield QTFloat32(self, "geom_y", "Position Y")
-        yield QTFloat32(self, "geom_w", "Divider scale")
+        yield QTFloat2_30(self, "geom_w", "Divider scale")
         yield QTFloat32(self, "frame_size_width")
         yield QTFloat32(self, "frame_size_height")
 
-class HDLR(FieldSet):
+class TrackReferenceType(FieldSet):
+    def createFields(self):
+        while not self.eof:
+            yield UInt32(self, "track_id[]", "Referenced track ID")
+
+class Handler(FieldSet):
     def createFields(self):
-        yield textHandler(UInt8(self, "version"), hexadecimal)
-        yield RawBytes(self, "flags", 3)
-        yield String(self, "subtype", 8)
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield String(self, "creator", 4)
+        yield String(self, "subtype", 4)
         yield String(self, "manufacturer", 4)
         yield UInt32(self, "res_flags")
         yield UInt32(self, "res_flags_mask")
         if self.root.is_mpeg4:
-            yield CString(self, "name")
+            yield CString(self, "name", charset="UTF-8")
         else:
             yield PascalString8(self, "name")
 
+class LanguageCode(FieldSet):
+    static_size = 16
+    MAC_LANG = {
+        0: 'English',
+        1: 'French',
+        2: 'German',
+        3: 'Italian',
+        4: 'Dutch',
+        5: 'Swedish',
+        6: 'Spanish',
+        7: 'Danish',
+        8: 'Portuguese',
+        9: 'Norwegian',
+        10: 'Hebrew',
+        11: 'Japanese',
+        12: 'Arabic',
+        13: 'Finnish',
+        14: 'Greek',
+        15: 'Icelandic',
+        16: 'Maltese',
+        17: 'Turkish',
+        18: 'Croatian',
+        19: 'Traditional Chinese',
+        20: 'Urdu',
+        21: 'Hindi',
+        22: 'Thai',
+        23: 'Korean',
+        24: 'Lithuanian',
+        25: 'Polish',
+        26: 'Hungarian',
+        27: 'Estonian',
+        28: 'Latvian',
+        28: 'Lettish',
+        29: 'Lappish',
+        29: 'Saamisk',
+        30: 'Faeroese',
+        31: 'Farsi',
+        32: 'Russian',
+        33: 'Simplified Chinese',
+        34: 'Flemish',
+        35: 'Irish',
+        36: 'Albanian',
+        37: 'Romanian',
+        38: 'Czech',
+        39: 'Slovak',
+        40: 'Slovenian',
+        41: 'Yiddish',
+        42: 'Serbian',
+        43: 'Macedonian',
+        44: 'Bulgarian',
+        45: 'Ukrainian',
+        46: 'Byelorussian',
+        47: 'Uzbek',
+        48: 'Kazakh',
+        49: 'Azerbaijani',
+        50: 'AzerbaijanAr',
+        51: 'Armenian',
+        52: 'Georgian',
+        53: 'Moldavian',
+        54: 'Kirghiz',
+        55: 'Tajiki',
+        56: 'Turkmen',
+        57: 'Mongolian',
+        58: 'MongolianCyr',
+        59: 'Pashto',
+        60: 'Kurdish',
+        61: 'Kashmiri',
+        62: 'Sindhi',
+        63: 'Tibetan',
+        64: 'Nepali',
+        65: 'Sanskrit',
+        66: 'Marathi',
+        67: 'Bengali',
+        68: 'Assamese',
+        69: 'Gujarati',
+        70: 'Punjabi',
+        71: 'Oriya',
+        72: 'Malayalam',
+        73: 'Kannada',
+        74: 'Tamil',
+        75: 'Telugu',
+        76: 'Sinhalese',
+        77: 'Burmese',
+        78: 'Khmer',
+        79: 'Lao',
+        80: 'Vietnamese',
+        81: 'Indonesian',
+        82: 'Tagalog',
+        83: 'MalayRoman',
+        84: 'MalayArabic',
+        85: 'Amharic',
+        86: 'Tigrinya',
+        88: 'Somali',
+        89: 'Swahili',
+        90: 'Ruanda',
+        91: 'Rundi',
+        92: 'Chewa',
+        93: 'Malagasy',
+        94: 'Esperanto',
+        128: 'Welsh',
+        129: 'Basque',
+        130: 'Catalan',
+        131: 'Latin',
+        132: 'Quechua',
+        133: 'Guarani',
+        134: 'Aymara',
+        135: 'Tatar',
+        136: 'Uighur',
+        137: 'Dzongkha',
+        138: 'JavaneseRom',
+    }
+
+    def fieldHandler(self, field):
+        if field.value == 0:
+            return ' '
+        return chr(field.value + 0x60)
+    def createFields(self):
+        value = self.stream.readBits(self.absolute_address, 16, self.endian)
+        if value < 1024:
+            yield Enum(UInt16(self, "lang"), self.MAC_LANG)
+        else:
+            yield NullBits(self, "padding[]", 1)
+            yield textHandler(Bits(self, "lang[0]", 5), self.fieldHandler)
+            yield textHandler(Bits(self, "lang[1]", 5), self.fieldHandler)
+            yield textHandler(Bits(self, "lang[2]", 5), self.fieldHandler)
+    def createValue(self):
+        if 'lang' in self:
+            return self['lang'].display
+        return self['lang[0]'].display + self['lang[1]'].display + self['lang[2]'].display
+
 class MediaHeader(FieldSet):
     def createFields(self):
-        yield textHandler(UInt8(self, "version"), hexadecimal)
-        yield RawBytes(self, "flags", 3)
-        yield TimestampMac32(self, "creation_date")
-        yield TimestampMac32(self, "lastmod_date")
-        yield UInt32(self, "time_scale")
-        yield UInt32(self, "duration")
-        yield UInt16(self, "mac_lang")
+        yield UInt8(self, "version", "Version (0 or 1)")
+        yield NullBits(self, "flags", 24)
+        if self['version'].value == 0:
+            # 32-bit version
+            yield TimestampMac32(self, "creation_date", "Creation time of this media")
+            yield TimestampMac32(self, "lastmod_date", "Last modification time of this media")
+            yield UInt32(self, "time_scale", "Number of time-units per second")
+            yield UInt32(self, "duration", "Length of media, in time-units")
+        elif self['version'].value == 1:
+            # 64-bit version
+            yield TimestampMac64(self, "creation_date", "Creation time of this media")
+            yield TimestampMac64(self, "lastmod_date", "Last modification time of this media")
+            yield UInt32(self, "time_scale", "Number of time-units per second")
+            yield UInt64(self, "duration", "Length of media, in time-units")
+        yield LanguageCode(self, "language")
         yield Int16(self, "quality")
 
-class ELST(FieldSet):
+class VideoMediaHeader(FieldSet):
+    GRAPHICSMODE = {
+        0: ('Copy', "Copy the source image over the destination"),
+        0x20: ('Blend', "Blend of source and destination; blending factor is controlled by op color"),
+        0x24: ('Transparent', "Replace destination pixel with source pixel if the source pixel is not the op color"),
+        0x40: ('Dither copy', "Dither image if necessary, else copy"),
+        0x100: ('Straight alpha', "Blend of source and destination; blending factor is controlled by alpha channel"),
+        0x101: ('Premul white alpha', "Remove white from each pixel and blend"),
+        0x102: ('Premul black alpha', "Remove black from each pixel and blend"),
+        0x103: ('Composition', "Track drawn offscreen and dither copied onto screen"),
+        0x104: ('Straight alpha blend', "Blend of source and destination; blending factor is controlled by combining alpha channel and op color")
+    }
+
+    def graphicsDisplay(self, field):
+        if field.value in self.GRAPHICSMODE:
+            return self.GRAPHICSMODE[field.value][0]
+        return hex(field.value)
+
+    def graphicsDescription(self, field):
+        if field.value in self.GRAPHICSMODE:
+            return self.GRAPHICSMODE[field.value][1]
+        return ""
+
     def createFields(self):
-        yield textHandler(UInt8(self, "version"), hexadecimal)
-        yield RawBytes(self, "flags", 3)
-        yield UInt32(self, "nb_edits")
-        yield UInt32(self, "length")
-        yield UInt32(self, "start")
-        yield QTFloat32(self, "playback_speed")
+        yield UInt8(self, "version", "Version")
+        yield Bits(self, "flags", 24, "Flags (=1)")
+        graphics = UInt16(self, "graphicsmode")
+        graphics.createDisplay = lambda:self.graphicsDisplay(graphics)
+        graphics.createDescription = lambda:self.graphicsDescription(graphics)
+        yield graphics
+        yield UInt16(self, "op_red", "Red value for graphics mode")
+        yield UInt16(self, "op_green", "Green value for graphics mode")
+        yield UInt16(self, "op_blue", "Blue value for graphics mode")
+
+class SoundMediaHeader(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield QTFloat16(self, "balance")
+        yield UInt16(self, "reserved[]")
+
+class HintMediaHeader(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield UInt16(self, "max_pdu_size")
+        yield UInt16(self, "avg_pdu_size")
+        yield UInt32(self, "max_bit_rate")
+        yield UInt32(self, "avg_bit_rate")
+        yield UInt32(self, "reserved[]")
+
+class DataEntryUrl(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 23)
+        yield Bit(self, "is_same_file", "Is the reference to this file?")
+        if not self['is_same_file'].value:
+            yield CString(self, "location")
+
+class DataEntryUrn(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 23)
+        yield Bit(self, "is_same_file", "Is the reference to this file?")
+        if not self['is_same_file'].value:
+            yield CString(self, "name")
+            yield CString(self, "location")
+
+class DataReference(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count")
+        for i in xrange(self['count'].value):
+            yield Atom(self, "atom[]")
+
+class EditList(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version (0 or 1)")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count")
+        version = self['version'].value
+        if version == 0:
+            UInt, Int = UInt32, Int32
+        elif version == 1:
+            UInt, Int = UInt64, Int64
+        else:
+            raise ParserError("elst version %d not supported"%version)
+        for i in xrange(self['count'].value):
+            yield UInt(self, "duration[]", "Duration of this edit segment")
+            yield Int(self, "time[]", "Starting time of this edit segment within the media (-1 = empty edit)")
+            yield QTFloat32(self, "play_speed[]", "Playback rate (0 = dwell edit, 1 = normal playback)")
 
 class Load(FieldSet):
     def createFields(self):
@@ -111,31 +368,39 @@ class Load(FieldSet):
 
 class MovieHeader(FieldSet):
     def createFields(self):
-        yield textHandler(UInt8(self, "version"), hexadecimal)
-        yield RawBytes(self, "flags", 3)
-        yield TimestampMac32(self, "creation_date")
-        yield TimestampMac32(self, "lastmod_date")
-        yield UInt32(self, "time_scale")
-        yield UInt32(self, "duration")
-        yield QTFloat32(self, "play_speed")
-        yield UInt16(self, "volume")
-        yield PaddingBytes(self, "reserved[]", 10)
+        yield UInt8(self, "version", "Version (0 or 1)")
+        yield NullBits(self, "flags", 24)
+        if self['version'].value == 0:
+            # 32-bit version
+            yield TimestampMac32(self, "creation_date", "Creation time of this presentation")
+            yield TimestampMac32(self, "lastmod_date", "Last modification time of this presentation")
+            yield UInt32(self, "time_scale", "Number of time-units per second")
+            yield UInt32(self, "duration", "Length of presentation, in time-units")
+        elif self['version'].value == 1:
+            # 64-bit version
+            yield TimestampMac64(self, "creation_date", "Creation time of this presentation")
+            yield TimestampMac64(self, "lastmod_date", "Last modification time of this presentation")
+            yield UInt32(self, "time_scale", "Number of time-units per second")
+            yield UInt64(self, "duration", "Length of presentation, in time-units")
+        yield QTFloat32(self, "play_speed", "Preferred playback speed (1.0 = normal)")
+        yield QTFloat16(self, "volume", "Preferred playback volume (1.0 = full)")
+        yield NullBytes(self, "reserved[]", 10)
         yield QTFloat32(self, "geom_a", "Width scale")
         yield QTFloat32(self, "geom_b", "Width rotate")
-        yield QTFloat32(self, "geom_u", "Width angle")
+        yield QTFloat2_30(self, "geom_u", "Width angle")
         yield QTFloat32(self, "geom_c", "Height rotate")
         yield QTFloat32(self, "geom_d", "Height scale")
-        yield QTFloat32(self, "geom_v", "Height angle")
+        yield QTFloat2_30(self, "geom_v", "Height angle")
         yield QTFloat32(self, "geom_x", "Position X")
         yield QTFloat32(self, "geom_y", "Position Y")
-        yield QTFloat32(self, "geom_w", "Divider scale")
+        yield QTFloat2_30(self, "geom_w", "Divider scale")
         yield UInt32(self, "preview_start")
         yield UInt32(self, "preview_length")
         yield UInt32(self, "still_poster")
         yield UInt32(self, "sel_start")
         yield UInt32(self, "sel_length")
         yield UInt32(self, "current_time")
-        yield UInt32(self, "next_track")
+        yield UInt32(self, "next_track_ID", "Value to use as the track ID for the next track added")
 
 class FileType(FieldSet):
     def createFields(self):
@@ -144,46 +409,410 @@ class FileType(FieldSet):
         while not self.eof:
             yield String(self, "compat_brand[]", 4, "Compatible brand")
 
+class MovieFragmentHeader(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "sequence_number")
+
+class TrackFragmentRandomAccess(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "track_id")
+        yield NullBits(self, "reserved", 26)
+        yield Bits(self, "length_size_of_traf_num", 2)
+        yield Bits(self, "length_size_of_trun_num", 2)
+        yield Bits(self, "length_size_of_sample_num", 2)
+        yield UInt32(self, "number_of_entry")
+        for i in xrange(self['number_of_entry'].value):
+            if self['version'].value == 1:
+                yield UInt64(self, "time[%i]" % i)
+                yield UInt64(self, "moof_offset[%i]" %i)
+            else:
+                yield UInt32(self, "time[%i]" %i)
+                yield UInt32(self, "moof_offset[%i]" %i)
+
+            if self['length_size_of_traf_num'].value == 3:
+                yield UInt64(self, "traf_number[%i]" %i) 
+            elif self['length_size_of_traf_num'].value == 2:
+                yield UInt32(self, "traf_number[%i]" %i) 
+            elif self['length_size_of_traf_num'].value == 1:
+                yield UInt16(self, "traf_number[%i]" %i) 
+            else:
+                yield UInt8(self, "traf_number[%i]" %i) 
+
+            if self['length_size_of_trun_num'].value == 3:
+                yield UInt64(self, "trun_number[%i]" %i) 
+            elif self['length_size_of_trun_num'].value == 2:
+                yield UInt32(self, "trun_number[%i]" %i) 
+            elif self['length_size_of_trun_num'].value == 1:
+                yield UInt16(self, "trun_number[%i]" %i) 
+            else:
+                yield UInt8(self, "trun_number[%i]" %i) 
+
+            if self['length_size_of_sample_num'].value == 3:
+                yield UInt64(self, "sample_number[%i]" %i) 
+            elif self['length_size_of_sample_num'].value == 2:
+                yield UInt32(self, "sample_number[%i]" %i) 
+            elif self['length_size_of_sample_num'].value == 1:
+                yield UInt16(self, "sample_number[%i]" %i) 
+            else:
+                yield UInt8(self, "sample_number[%i]" %i) 
+
+class MovieFragmentRandomAccessOffset(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version", "Version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "size")
+
+def findHandler(self):
+    ''' find the handler corresponding to this fieldset '''
+    while self:
+        if self.name in ('media', 'tags'):
+            break
+        self = self.parent
+    else:
+        return None
+    for atom in self:
+        if atom['tag'].value == 'hdlr':
+            return atom['hdlr']
+    return None
+
+class METATAG(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "unk[]", "0x80 or 0x00")
+        yield PascalString16(self, "tag_name", charset='UTF-8')
+        yield UInt16(self, "unk[]", "0x0001")
+        yield UInt16(self, "unk[]", "0x0000")
+        yield PascalString16(self, "tag_value", charset='UTF-8')
+
+class META(FieldSet):
+    def createFields(self):
+        # This tag has too many variant forms.
+        if '/tags/' in self.path:
+            yield UInt32(self, "count")
+            for i in xrange(self['count'].value):
+                yield METATAG(self, "tag[]")
+        elif self.stream.readBits(self.absolute_address, 32, self.endian) == 0:
+            yield UInt8(self, "version")
+            yield Bits(self, "flags", 24)
+            yield AtomList(self, "tags")
+        else:
+            yield AtomList(self, "tags")
+
+class Item(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "size")
+        yield UInt32(self, "index")
+        yield Atom(self, "value")
+
+class KeyList(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count")
+        for i in xrange(self['count'].value):
+            yield Atom(self, "key[]")
+
+class ItemList(FieldSet):
+    def createFields(self):
+        handler = findHandler(self)
+        if handler is None:
+            raise ParserError("ilst couldn't find metadata handler")
+        if handler['subtype'].value == 'mdir':
+            while not self.eof:
+                yield Atom(self, "atom[]")
+        elif handler['subtype'].value == 'mdta':
+            while not self.eof:
+                yield Item(self, "item[]")
+
+class NeroChapters(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "unknown")
+        yield UInt8(self, "count", description="Number of chapters")
+        for i in xrange(self['count'].value):
+            yield UInt64(self, "chapter_start[]")
+            yield PascalString8(self, "chapter_name[]", charset='UTF-8')
+
+class SampleDecodeTimeTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Total entries in sample time table")
+        for i in xrange(self['count'].value):
+            yield UInt32(self, "sample_count[]", "Number of consecutive samples with this delta")
+            yield UInt32(self, "sample_delta[]", "Decode time delta since last sample, in time-units")
+
+class SampleCompositionTimeTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Total entries in sample time table")
+        for i in xrange(self['count'].value):
+            yield UInt32(self, "sample_count[]", "Number of consecutive samples with this offset")
+            yield UInt32(self, "sample_offset[]", "Difference between decode time and composition time of this sample, in time-units")
+
+class ChunkOffsetTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Total entries in offset table")
+        for i in xrange(self['count'].value):
+            yield UInt32(self, "chunk_offset[]")
+
+class ChunkOffsetTable64(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Total entries in offset table")
+        for i in xrange(self['count'].value):
+            yield UInt64(self, "chunk_offset[]")
+
+class SampleEntry(FieldSet):
+    def createFields(self):
+        yield UInt32(self, "size")
+        yield RawBytes(self, "format", 4, "Data Format (codec)")
+        yield NullBytes(self, "reserved[]", 6, "Reserved")
+        yield UInt16(self, "data_reference_index")
+        handler = findHandler(self)
+        if not handler:
+            raise ParserError("stsd couldn't find track handler")
+        if handler['subtype'].value == 'soun':
+            # Audio sample entry
+            yield NullBytes(self, "reserved[]", 8)
+            yield UInt16(self, "channels", "Number of audio channels")
+            yield UInt16(self, "samplesize", "Sample size in bits")
+            yield UInt16(self, "unknown")
+            yield NullBytes(self, "reserved[]", 2)
+            yield QTFloat32(self, "samplerate", "Sample rate in Hz")
+        elif handler['subtype'].value == 'vide':
+            # Video sample entry
+            yield UInt16(self, "version")
+            yield UInt16(self, "revision_level")
+            yield RawBytes(self, "vendor_id", 4)
+            yield UInt32(self, "temporal_quality")
+            yield UInt32(self, "spatial_quality")
+            yield UInt16(self, "width", "Width (pixels)")
+            yield UInt16(self, "height", "Height (pixels)")
+            yield QTFloat32(self, "horizontal_resolution", "Horizontal resolution in DPI")
+            yield QTFloat32(self, "vertical resolution", "Vertical resolution in DPI")
+            yield UInt32(self, "data_size")
+            yield UInt16(self, "frame_count")
+            yield UInt8(self, "compressor_name_length")
+            yield String(self, "compressor_name", 31, strip='\0')
+            yield UInt16(self, "depth", "Bit depth of image")
+            yield Int16(self, "unknown")
+        elif handler['subtype'].value == 'hint':
+            # Hint sample entry
+            pass
+        
+        size = self['size'].value - self.current_size//8
+        if size > 0:
+            yield RawBytes(self, "extra_data", size)
+
+class SampleDescription(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Total entries in table")
+        for i in xrange(self['count'].value):
+            yield SampleEntry(self, "sample_entry[]")
+
+class SyncSampleTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Number of sync samples")
+        for i in xrange(self['count'].value):
+            yield UInt32(self, "sample_number[]")
+
+class SampleSizeTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "uniform_size", description="Uniform size of each sample (0 if non-uniform)")
+        yield UInt32(self, "count", description="Number of samples")
+        if self['uniform_size'].value == 0:
+            for i in xrange(self['count'].value):
+                yield UInt32(self, "sample_size[]")
+
+class CompactSampleSizeTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield NullBits(self, "reserved[]", 24)
+        yield UInt8(self, "field_size", "Size of each entry in this table, in bits")
+        yield UInt32(self, "count", description="Number of samples")
+        bitsize = self['field_size'].value
+        for i in xrange(self['count'].value):
+            yield Bits(self, "sample_size[]", bitsize)
+        if self.current_size % 8 != 0:
+            yield NullBits(self, "padding[]", 8 - (self.current_size % 8))
+
+class SampleToChunkTable(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "version")
+        yield NullBits(self, "flags", 24)
+        yield UInt32(self, "count", description="Number of samples")
+        for i in xrange(self['count'].value):
+            yield UInt32(self, "first_chunk[]")
+            yield UInt32(self, "samples_per_chunk[]")
+            yield UInt32(self, "sample_description_index[]")
+
 class Atom(FieldSet):
     tag_info = {
-        # TODO: Use dictionnary of dictionnary, like Matroska parser does
-        # "elst" is a child of "edts", but not of "moov" for example
-        "moov": (AtomList, "movie", "Movie"),
-        "trak": (AtomList, "track", "Track"),
-        "mdia": (AtomList, "media", "Media"),
-        "edts": (AtomList, "edts", ""),
-        "minf": (AtomList, "minf", ""),
-        "stbl": (AtomList, "stbl", ""),
-        "dinf": (AtomList, "dinf", ""),
-        "elst": (ELST, "edts", ""),
-        "tkhd": (TrackHeader, "track_hdr", "Track header"),
-        "hdlr": (HDLR, "hdlr", ""),
-        "mdhd": (MediaHeader, "media_hdr", "Media header"),
-        "load": (Load, "load", ""),
-        "mvhd": (MovieHeader, "movie_hdr", "Movie header"),
-        "ftyp": (FileType, "file_type", "File type"),
+        "ftyp": (FileType, "file_type", "File type and compatibility"),
+        # pdin: progressive download information
+        # pnot: movie preview (old QT spec)
+        "moov": (AtomList, "movie", "Container for all metadata"),
+            "mvhd": (MovieHeader, "movie_hdr", "Movie header, overall declarations"),
+            # clip: movie clipping (old QT spec)
+                # crgn: movie clipping region (old QT spec)
+            "trak": (AtomList, "track", "Container for an individual track or stream"),
+                "tkhd": (TrackHeader, "track_hdr", "Track header, overall information about the track"),
+                # matt: track matte (old QT spec)
+                    # kmat: compressed matte (old QT spec)
+                "tref": (AtomList, "tref", "Track reference container"),
+                    "hint": (TrackReferenceType, "hint", "Original media track(s) for this hint track"),
+                    "cdsc": (TrackReferenceType, "cdsc", "Reference to track described by this track"),
+                "edts": (AtomList, "edts", "Edit list container"),
+                    "elst": (EditList, "elst", "Edit list"),
+                "load": (Load, "load", "Track loading settings (old QT spec)"),
+                # imap: Track input map (old QT spec)
+                "mdia": (AtomList, "media", "Container for the media information in a track"),
+                    "mdhd": (MediaHeader, "media_hdr", "Media header, overall information about the media"),
+                    "hdlr": (Handler, "hdlr", "Handler, declares the media or metadata (handler) type"),
+                    "minf": (AtomList, "minf", "Media information container"),
+                        "vmhd": (VideoMediaHeader, "vmhd", "Video media header, overall information (video track only)"),
+                        "smhd": (SoundMediaHeader, "smhd", "Sound media header, overall information (sound track only)"),
+                        "hmhd": (HintMediaHeader, "hmhd", "Hint media header, overall information (hint track only)"),
+                        # nmhd: Null media header, overall information (some tracks only) (unparsed)
+                        "dinf": (AtomList, "dinf", "Data information, container"),
+                            "dref": (DataReference, "dref", "Data reference, declares source(s) of media data in track"),
+                                "url ": (DataEntryUrl, "url", "URL data reference"),
+                                "urn ": (DataEntryUrn, "urn", "URN data reference"),
+                        "stbl": (AtomList, "stbl", "Sample table, container for the time/space map"),
+                            "stsd": (SampleDescription, "stsd", "Sample descriptions (codec types, initialization etc.)"),
+                            "stts": (SampleDecodeTimeTable, "stts", "decoding time-to-sample delta table"),
+                            "ctts": (SampleCompositionTimeTable, "ctts", "composition time-to-sample offset table"),
+                            "stsc": (SampleToChunkTable, "stsc", "sample-to-chunk, partial data-offset information"),
+                            "stsz": (SampleSizeTable, "stsz", "Sample size table (framing)"),
+                            "stz2": (CompactSampleSizeTable, "stz2", "Compact sample size table (framing)"),
+                            "stco": (ChunkOffsetTable, "stco", "Chunk offset, partial data-offset information"),
+                            "co64": (ChunkOffsetTable64, "co64", "64-bit chunk offset"),
+                            "stss": (SyncSampleTable, "stss", "Sync sample table (random access points)"),
+                            # stsh: shadow sync sample table
+                            # padb: sample padding bits
+                            # stdp: sample degradation priority
+                            # sdtp: independent and disposable samples
+                            # sbgp: sample-to-group
+                            # sgpd: sample group description
+                            # subs: sub-sample information
+            # ctab color table (old QT spec)
+            # mvex: movie extends
+                # mehd: movie extends header
+                # trex: track extends defaults
+            # ipmc: IPMP control
+        "moof": (AtomList, "moof", "movie fragment"),
+            "mfhd": (MovieFragmentHeader, "mfhd", "movie fragment header"),
+            # traf: track fragment
+                # tfhd: track fragment header
+                # trun: track fragment run
+                # sdtp: independent and disposable samples
+                # sbgp: sample-to-group
+                # subs: sub-sample information
+        "mfra": (AtomList, "mfra", "movie fragment random access"),
+            "tfra": (TrackFragmentRandomAccess, "tfra", "track fragment random access"),
+            "mfro": (MovieFragmentRandomAccessOffset, "mfro", "movie fragment random access offset"),
+        # mdat: media data container
+        # free: free space (unparsed)
+        # skip: free space (unparsed)
+        "udta": (AtomList, "udta", "User data"),
+        "meta": (META, "meta", "File metadata"),
+            "keys": (KeyList, "keys", "Metadata keys"),
+            ## hdlr
+            ## dinf
+                ## dref: data reference, declares source(s) of metadata items
+            ## ipmc: IPMP control
+            # iloc: item location
+            # ipro: item protection
+                # sinf: protection scheme information
+                    # frma: original format
+                    # imif: IPMP information
+                    # schm: scheme type
+                    # schi: scheme information
+            # iinf: item information
+            # xml : XML container
+            # bxml: binary XML container
+            # pitm: primary item reference
+        ## other tags
+        "ilst": (ItemList, "ilst", "Item list"),
+            "trkn": (AtomList, "trkn", "Metadata: Track number"),
+            "disk": (AtomList, "disk", "Metadata: Disk number"),
+            "tmpo": (AtomList, "tempo", "Metadata: Tempo"),
+            "cpil": (AtomList, "cpil", "Metadata: Compilation"),
+            "gnre": (AtomList, "gnre", "Metadata: Genre"),
+            "\xa9cpy": (AtomList, "copyright", "Metadata: Copyright statement"),
+            "\xa9day": (AtomList, "date", "Metadata: Date of content creation"),
+            "\xa9dir": (AtomList, "director", "Metadata: Movie director"),
+            "\xa9ed1": (AtomList, "edit1", "Metadata: Edit date and description (1)"),
+            "\xa9ed2": (AtomList, "edit2", "Metadata: Edit date and description (2)"),
+            "\xa9ed3": (AtomList, "edit3", "Metadata: Edit date and description (3)"),
+            "\xa9ed4": (AtomList, "edit4", "Metadata: Edit date and description (4)"),
+            "\xa9ed5": (AtomList, "edit5", "Metadata: Edit date and description (5)"),
+            "\xa9ed6": (AtomList, "edit6", "Metadata: Edit date and description (6)"),
+            "\xa9ed7": (AtomList, "edit7", "Metadata: Edit date and description (7)"),
+            "\xa9ed8": (AtomList, "edit8", "Metadata: Edit date and description (8)"),
+            "\xa9ed9": (AtomList, "edit9", "Metadata: Edit date and description (9)"),
+            "\xa9fmt": (AtomList, "format", "Metadata: Movie format (CGI, digitized, etc.)"),
+            "\xa9inf": (AtomList, "info", "Metadata: Information about the movie"),
+            "\xa9prd": (AtomList, "producer", "Metadata: Movie producer"),
+            "\xa9prf": (AtomList, "performers", "Metadata: Performer names"),
+            "\xa9req": (AtomList, "requirements", "Metadata: Special hardware and software requirements"),
+            "\xa9src": (AtomList, "source", "Metadata: Credits for those who provided movie source content"),
+            "\xa9nam": (AtomList, "name", "Metadata: Name of song or video"),
+            "\xa9des": (AtomList, "description", "Metadata: File description"),
+            "\xa9cmt": (AtomList, "comment", "Metadata: General comment"),
+            "\xa9alb": (AtomList, "album", "Metadata: Album name"),
+            "\xa9gen": (AtomList, "genre", "Metadata: Custom genre"),
+            "\xa9ART": (AtomList, "artist", "Metadata: Artist name"),
+            "\xa9too": (AtomList, "encoder", "Metadata: Encoder"),
+            "\xa9wrt": (AtomList, "writer", "Metadata: Writer"),
+            "covr": (AtomList, "cover", "Metadata: Cover art"),
+            "----": (AtomList, "misc", "Metadata: Miscellaneous"),
+        "tags": (AtomList, "tags", "File tags"),
+        "tseg": (AtomList, "tseg", "tseg"),
+        "chpl": (NeroChapters, "chpl", "Nero chapter data"),
     }
     tag_handler = [ item[0] for item in tag_info ]
     tag_desc = [ item[1] for item in tag_info ]
 
     def createFields(self):
         yield UInt32(self, "size")
-        yield String(self, "tag", 4)
+        yield RawBytes(self, "tag", 4)
         size = self["size"].value
         if size == 1:
-            raise ParserError("Extended size is not supported!")
-            #yield UInt64(self, "size64")
-            size = self["size64"].value
+            # 64-bit size
+            yield UInt64(self, "size64")
+            size = self["size64"].value - 16
         elif size == 0:
-            #size = (self.root.size - self.root.current_size - self.current_size) / 8
+            # Unbounded atom
             if self._size is None:
-                size = (self.parent.size - self.current_size) / 8 - 8
+                size = (self.parent.size - self.parent.current_size) / 8 - 8
             else:
                 size = (self.size - self.current_size) / 8
         else:
             size = size - 8
-        if 0 < size:
+        if self['tag'].value == 'uuid':
+            yield GUID(self, "usertag")
+            tag = self["usertag"].value
+            size -= 16
+        else:
             tag = self["tag"].value
+        if size > 0:
             if tag in self.tag_info:
                 handler, name, desc = self.tag_info[tag]
                 yield handler(self, name, desc, size=size*8)
@@ -191,6 +820,8 @@ class Atom(FieldSet):
                 yield RawBytes(self, "data", size)
 
     def createDescription(self):
+        if self["tag"].value == "uuid":
+            return "Atom: uuid: "+self["usertag"].value
         return "Atom: %s" % self["tag"].value
 
 class MovFile(Parser):
@@ -207,12 +838,16 @@ class MovFile(Parser):
         # File type brand => MIME type
         'mp41': u'video/mp4',
         'mp42': u'video/mp4',
+        'avc1': u'video/mp4',
+        'isom': u'video/mp4',
+        'iso2': u'video/mp4',
     }
     endian = BIG_ENDIAN
 
     def __init__(self, *args, **kw):
         Parser.__init__(self, *args, **kw)
-        self.is_mpeg4 = False
+
+    is_mpeg4 = property(lambda self:self.mime_type==u'video/mp4')
 
     def validate(self):
         # TODO: Write better code, erk!
@@ -242,5 +877,5 @@ class MovFile(Parser):
                     return self.BRANDS[brand]
         except MissingField:
             pass
-        return None
+        return u'video/quicktime'
 
diff --git a/lib/hachoir_parser/video/mpeg_ts.py b/lib/hachoir_parser/video/mpeg_ts.py
index 56b9bc85e02a2abf790af0835b0f575797c400d6..ed8724a376fbbde01e2b9e5d885c3a4431dc8aea 100644
--- a/lib/hachoir_parser/video/mpeg_ts.py
+++ b/lib/hachoir_parser/video/mpeg_ts.py
@@ -92,11 +92,11 @@ class MPEG_TS(Parser):
         return True
 
     def createFields(self):
-        sync = self.stream.searchBytes("\x47", 0, 204*8)
-        if sync is None:
-            raise ParserError("Unable to find synchronization byte")
-        elif sync:
-            yield RawBytes(self, "incomplete_packet", sync//8)
         while not self.eof:
+            sync = self.stream.searchBytes("\x47", self.current_size, self.current_size+204*8)
+            if sync is None:
+                raise ParserError("Unable to find synchronization byte")
+            elif sync:
+                yield RawBytes(self, "incomplete_packet[]", (sync-self.current_size)//8)
             yield Packet(self, "packet[]")
 
diff --git a/lib/mako/__init__.py b/lib/mako/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d963848188928afc7088af2538de6d119dc9ebb4
--- /dev/null
+++ b/lib/mako/__init__.py
@@ -0,0 +1,8 @@
+# mako/__init__.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+__version__ = '1.0.1'
diff --git a/lib/mako/_ast_util.py b/lib/mako/_ast_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..efbc4fc245d2d4d08a3cea15e7d216f2a7e09a36
--- /dev/null
+++ b/lib/mako/_ast_util.py
@@ -0,0 +1,845 @@
+# mako/_ast_util.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""
+    ast
+    ~~~
+
+    The `ast` module helps Python applications to process trees of the Python
+    abstract syntax grammar.  The abstract syntax itself might change with
+    each Python release; this module helps to find out programmatically what
+    the current grammar looks like and allows modifications of it.
+
+    An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
+    a flag to the `compile()` builtin function or by using the `parse()`
+    function from this module.  The result will be a tree of objects whose
+    classes all inherit from `ast.AST`.
+
+    A modified abstract syntax tree can be compiled into a Python code object
+    using the built-in `compile()` function.
+
+    Additionally various helper functions are provided that make working with
+    the trees simpler.  The main intention of the helper functions and this
+    module in general is to provide an easy to use interface for libraries
+    that work tightly with the python syntax (template engines for example).
+
+
+    :copyright: Copyright 2008 by Armin Ronacher.
+    :license: Python License.
+"""
+from _ast import *
+from mako.compat import arg_stringname
+
+BOOLOP_SYMBOLS = {
+    And: 'and',
+    Or: 'or'
+}
+
+BINOP_SYMBOLS = {
+    Add: '+',
+    Sub: '-',
+    Mult: '*',
+    Div: '/',
+    FloorDiv: '//',
+    Mod: '%',
+    LShift: '<<',
+    RShift: '>>',
+    BitOr: '|',
+    BitAnd: '&',
+    BitXor: '^'
+}
+
+CMPOP_SYMBOLS = {
+    Eq: '==',
+    Gt: '>',
+    GtE: '>=',
+    In: 'in',
+    Is: 'is',
+    IsNot: 'is not',
+    Lt: '<',
+    LtE: '<=',
+    NotEq: '!=',
+    NotIn: 'not in'
+}
+
+UNARYOP_SYMBOLS = {
+    Invert: '~',
+    Not: 'not',
+    UAdd: '+',
+    USub: '-'
+}
+
+ALL_SYMBOLS = {}
+ALL_SYMBOLS.update(BOOLOP_SYMBOLS)
+ALL_SYMBOLS.update(BINOP_SYMBOLS)
+ALL_SYMBOLS.update(CMPOP_SYMBOLS)
+ALL_SYMBOLS.update(UNARYOP_SYMBOLS)
+
+
+def parse(expr, filename='<unknown>', mode='exec'):
+    """Parse an expression into an AST node."""
+    return compile(expr, filename, mode, PyCF_ONLY_AST)
+
+
+def to_source(node, indent_with=' ' * 4):
+    """
+    This function can convert a node tree back into python sourcecode.  This
+    is useful for debugging purposes, especially if you're dealing with custom
+    asts not generated by python itself.
+
+    It could be that the sourcecode is evaluable when the AST itself is not
+    compilable / evaluable.  The reason for this is that the AST contains some
+    more data than regular sourcecode does, which is dropped during
+    conversion.
+
+    Each level of indentation is replaced with `indent_with`.  Per default this
+    parameter is equal to four spaces as suggested by PEP 8, but it might be
+    adjusted to match the application's styleguide.
+    """
+    generator = SourceGenerator(indent_with)
+    generator.visit(node)
+    return ''.join(generator.result)
+
+
+def dump(node):
+    """
+    A very verbose representation of the node passed.  This is useful for
+    debugging purposes.
+    """
+    def _format(node):
+        if isinstance(node, AST):
+            return '%s(%s)' % (node.__class__.__name__,
+                               ', '.join('%s=%s' % (a, _format(b))
+                                         for a, b in iter_fields(node)))
+        elif isinstance(node, list):
+            return '[%s]' % ', '.join(_format(x) for x in node)
+        return repr(node)
+    if not isinstance(node, AST):
+        raise TypeError('expected AST, got %r' % node.__class__.__name__)
+    return _format(node)
+
+
+def copy_location(new_node, old_node):
+    """
+    Copy the source location hint (`lineno` and `col_offset`) from the
+    old to the new node if possible and return the new one.
+    """
+    for attr in 'lineno', 'col_offset':
+        if attr in old_node._attributes and attr in new_node._attributes \
+           and hasattr(old_node, attr):
+            setattr(new_node, attr, getattr(old_node, attr))
+    return new_node
+
+
+def fix_missing_locations(node):
+    """
+    Some nodes require a line number and the column offset.  Without that
+    information the compiler will abort the compilation.  Because it can be
+    a dull task to add appropriate line numbers and column offsets when
+    adding new nodes this function can help.  It copies the line number and
+    column offset of the parent node to the child nodes without this
+    information.
+
+    Unlike `copy_location` this works recursive and won't touch nodes that
+    already have a location information.
+    """
+    def _fix(node, lineno, col_offset):
+        if 'lineno' in node._attributes:
+            if not hasattr(node, 'lineno'):
+                node.lineno = lineno
+            else:
+                lineno = node.lineno
+        if 'col_offset' in node._attributes:
+            if not hasattr(node, 'col_offset'):
+                node.col_offset = col_offset
+            else:
+                col_offset = node.col_offset
+        for child in iter_child_nodes(node):
+            _fix(child, lineno, col_offset)
+    _fix(node, 1, 0)
+    return node
+
+
+def increment_lineno(node, n=1):
+    """
+    Increment the line numbers of all nodes by `n` if they have line number
+    attributes.  This is useful to "move code" to a different location in a
+    file.
+    """
+    for node in zip((node,), walk(node)):
+        if 'lineno' in node._attributes:
+            node.lineno = getattr(node, 'lineno', 0) + n
+
+
+def iter_fields(node):
+    """Iterate over all fields of a node, only yielding existing fields."""
+    # CPython 2.5 compat
+    if not hasattr(node, '_fields') or not node._fields:
+        return
+    for field in node._fields:
+        try:
+            yield field, getattr(node, field)
+        except AttributeError:
+            pass
+
+
+def get_fields(node):
+    """Like `iter_fiels` but returns a dict."""
+    return dict(iter_fields(node))
+
+
+def iter_child_nodes(node):
+    """Iterate over all child nodes or a node."""
+    for name, field in iter_fields(node):
+        if isinstance(field, AST):
+            yield field
+        elif isinstance(field, list):
+            for item in field:
+                if isinstance(item, AST):
+                    yield item
+
+
+def get_child_nodes(node):
+    """Like `iter_child_nodes` but returns a list."""
+    return list(iter_child_nodes(node))
+
+
+def get_compile_mode(node):
+    """
+    Get the mode for `compile` of a given node.  If the node is not a `mod`
+    node (`Expression`, `Module` etc.) a `TypeError` is thrown.
+    """
+    if not isinstance(node, mod):
+        raise TypeError('expected mod node, got %r' % node.__class__.__name__)
+    return {
+        Expression: 'eval',
+        Interactive: 'single'
+    }.get(node.__class__, 'expr')
+
+
+def get_docstring(node):
+    """
+    Return the docstring for the given node or `None` if no docstring can be
+    found.  If the node provided does not accept docstrings a `TypeError`
+    will be raised.
+    """
+    if not isinstance(node, (FunctionDef, ClassDef, Module)):
+        raise TypeError("%r can't have docstrings" % node.__class__.__name__)
+    if node.body and isinstance(node.body[0], Str):
+        return node.body[0].s
+
+
+def walk(node):
+    """
+    Iterate over all nodes.  This is useful if you only want to modify nodes in
+    place and don't care about the context or the order the nodes are returned.
+    """
+    from collections import deque
+    todo = deque([node])
+    while todo:
+        node = todo.popleft()
+        todo.extend(iter_child_nodes(node))
+        yield node
+
+
+class NodeVisitor(object):
+    """
+    Walks the abstract syntax tree and call visitor functions for every node
+    found.  The visitor functions may return values which will be forwarded
+    by the `visit` method.
+
+    Per default the visitor functions for the nodes are ``'visit_'`` +
+    class name of the node.  So a `TryFinally` node visit function would
+    be `visit_TryFinally`.  This behavior can be changed by overriding
+    the `get_visitor` function.  If no visitor function exists for a node
+    (return value `None`) the `generic_visit` visitor is used instead.
+
+    Don't use the `NodeVisitor` if you want to apply changes to nodes during
+    traversing.  For this a special visitor exists (`NodeTransformer`) that
+    allows modifications.
+    """
+
+    def get_visitor(self, node):
+        """
+        Return the visitor function for this node or `None` if no visitor
+        exists for this node.  In that case the generic visit function is
+        used instead.
+        """
+        method = 'visit_' + node.__class__.__name__
+        return getattr(self, method, None)
+
+    def visit(self, node):
+        """Visit a node."""
+        f = self.get_visitor(node)
+        if f is not None:
+            return f(node)
+        return self.generic_visit(node)
+
+    def generic_visit(self, node):
+        """Called if no explicit visitor function exists for a node."""
+        for field, value in iter_fields(node):
+            if isinstance(value, list):
+                for item in value:
+                    if isinstance(item, AST):
+                        self.visit(item)
+            elif isinstance(value, AST):
+                self.visit(value)
+
+
+class NodeTransformer(NodeVisitor):
+    """
+    Walks the abstract syntax tree and allows modifications of nodes.
+
+    The `NodeTransformer` will walk the AST and use the return value of the
+    visitor functions to replace or remove the old node.  If the return
+    value of the visitor function is `None` the node will be removed
+    from the previous location otherwise it's replaced with the return
+    value.  The return value may be the original node in which case no
+    replacement takes place.
+
+    Here an example transformer that rewrites all `foo` to `data['foo']`::
+
+        class RewriteName(NodeTransformer):
+
+            def visit_Name(self, node):
+                return copy_location(Subscript(
+                    value=Name(id='data', ctx=Load()),
+                    slice=Index(value=Str(s=node.id)),
+                    ctx=node.ctx
+                ), node)
+
+    Keep in mind that if the node you're operating on has child nodes
+    you must either transform the child nodes yourself or call the generic
+    visit function for the node first.
+
+    Nodes that were part of a collection of statements (that applies to
+    all statement nodes) may also return a list of nodes rather than just
+    a single node.
+
+    Usually you use the transformer like this::
+
+        node = YourTransformer().visit(node)
+    """
+
+    def generic_visit(self, node):
+        for field, old_value in iter_fields(node):
+            old_value = getattr(node, field, None)
+            if isinstance(old_value, list):
+                new_values = []
+                for value in old_value:
+                    if isinstance(value, AST):
+                        value = self.visit(value)
+                        if value is None:
+                            continue
+                        elif not isinstance(value, AST):
+                            new_values.extend(value)
+                            continue
+                    new_values.append(value)
+                old_value[:] = new_values
+            elif isinstance(old_value, AST):
+                new_node = self.visit(old_value)
+                if new_node is None:
+                    delattr(node, field)
+                else:
+                    setattr(node, field, new_node)
+        return node
+
+
+class SourceGenerator(NodeVisitor):
+    """
+    This visitor is able to transform a well formed syntax tree into python
+    sourcecode.  For more details have a look at the docstring of the
+    `node_to_source` function.
+    """
+
+    def __init__(self, indent_with):
+        self.result = []
+        self.indent_with = indent_with
+        self.indentation = 0
+        self.new_lines = 0
+
+    def write(self, x):
+        if self.new_lines:
+            if self.result:
+                self.result.append('\n' * self.new_lines)
+            self.result.append(self.indent_with * self.indentation)
+            self.new_lines = 0
+        self.result.append(x)
+
+    def newline(self, n=1):
+        self.new_lines = max(self.new_lines, n)
+
+    def body(self, statements):
+        self.new_line = True
+        self.indentation += 1
+        for stmt in statements:
+            self.visit(stmt)
+        self.indentation -= 1
+
+    def body_or_else(self, node):
+        self.body(node.body)
+        if node.orelse:
+            self.newline()
+            self.write('else:')
+            self.body(node.orelse)
+
+    def signature(self, node):
+        want_comma = []
+        def write_comma():
+            if want_comma:
+                self.write(', ')
+            else:
+                want_comma.append(True)
+
+        padding = [None] * (len(node.args) - len(node.defaults))
+        for arg, default in zip(node.args, padding + node.defaults):
+            write_comma()
+            self.visit(arg)
+            if default is not None:
+                self.write('=')
+                self.visit(default)
+        if node.vararg is not None:
+            write_comma()
+            self.write('*' + arg_stringname(node.vararg))
+        if node.kwarg is not None:
+            write_comma()
+            self.write('**' + arg_stringname(node.kwarg))
+
+    def decorators(self, node):
+        for decorator in node.decorator_list:
+            self.newline()
+            self.write('@')
+            self.visit(decorator)
+
+    # Statements
+
+    def visit_Assign(self, node):
+        self.newline()
+        for idx, target in enumerate(node.targets):
+            if idx:
+                self.write(', ')
+            self.visit(target)
+        self.write(' = ')
+        self.visit(node.value)
+
+    def visit_AugAssign(self, node):
+        self.newline()
+        self.visit(node.target)
+        self.write(BINOP_SYMBOLS[type(node.op)] + '=')
+        self.visit(node.value)
+
+    def visit_ImportFrom(self, node):
+        self.newline()
+        self.write('from %s%s import ' % ('.' * node.level, node.module))
+        for idx, item in enumerate(node.names):
+            if idx:
+                self.write(', ')
+            self.write(item)
+
+    def visit_Import(self, node):
+        self.newline()
+        for item in node.names:
+            self.write('import ')
+            self.visit(item)
+
+    def visit_Expr(self, node):
+        self.newline()
+        self.generic_visit(node)
+
+    def visit_FunctionDef(self, node):
+        self.newline(n=2)
+        self.decorators(node)
+        self.newline()
+        self.write('def %s(' % node.name)
+        self.signature(node.args)
+        self.write('):')
+        self.body(node.body)
+
+    def visit_ClassDef(self, node):
+        have_args = []
+        def paren_or_comma():
+            if have_args:
+                self.write(', ')
+            else:
+                have_args.append(True)
+                self.write('(')
+
+        self.newline(n=3)
+        self.decorators(node)
+        self.newline()
+        self.write('class %s' % node.name)
+        for base in node.bases:
+            paren_or_comma()
+            self.visit(base)
+        # XXX: the if here is used to keep this module compatible
+        #      with python 2.6.
+        if hasattr(node, 'keywords'):
+            for keyword in node.keywords:
+                paren_or_comma()
+                self.write(keyword.arg + '=')
+                self.visit(keyword.value)
+            if node.starargs is not None:
+                paren_or_comma()
+                self.write('*')
+                self.visit(node.starargs)
+            if node.kwargs is not None:
+                paren_or_comma()
+                self.write('**')
+                self.visit(node.kwargs)
+        self.write(have_args and '):' or ':')
+        self.body(node.body)
+
+    def visit_If(self, node):
+        self.newline()
+        self.write('if ')
+        self.visit(node.test)
+        self.write(':')
+        self.body(node.body)
+        while True:
+            else_ = node.orelse
+            if len(else_) == 1 and isinstance(else_[0], If):
+                node = else_[0]
+                self.newline()
+                self.write('elif ')
+                self.visit(node.test)
+                self.write(':')
+                self.body(node.body)
+            else:
+                self.newline()
+                self.write('else:')
+                self.body(else_)
+                break
+
+    def visit_For(self, node):
+        self.newline()
+        self.write('for ')
+        self.visit(node.target)
+        self.write(' in ')
+        self.visit(node.iter)
+        self.write(':')
+        self.body_or_else(node)
+
+    def visit_While(self, node):
+        self.newline()
+        self.write('while ')
+        self.visit(node.test)
+        self.write(':')
+        self.body_or_else(node)
+
+    def visit_With(self, node):
+        self.newline()
+        self.write('with ')
+        self.visit(node.context_expr)
+        if node.optional_vars is not None:
+            self.write(' as ')
+            self.visit(node.optional_vars)
+        self.write(':')
+        self.body(node.body)
+
+    def visit_Pass(self, node):
+        self.newline()
+        self.write('pass')
+
+    def visit_Print(self, node):
+        # XXX: python 2.6 only
+        self.newline()
+        self.write('print ')
+        want_comma = False
+        if node.dest is not None:
+            self.write(' >> ')
+            self.visit(node.dest)
+            want_comma = True
+        for value in node.values:
+            if want_comma:
+                self.write(', ')
+            self.visit(value)
+            want_comma = True
+        if not node.nl:
+            self.write(',')
+
+    def visit_Delete(self, node):
+        self.newline()
+        self.write('del ')
+        for idx, target in enumerate(node):
+            if idx:
+                self.write(', ')
+            self.visit(target)
+
+    def visit_TryExcept(self, node):
+        self.newline()
+        self.write('try:')
+        self.body(node.body)
+        for handler in node.handlers:
+            self.visit(handler)
+
+    def visit_TryFinally(self, node):
+        self.newline()
+        self.write('try:')
+        self.body(node.body)
+        self.newline()
+        self.write('finally:')
+        self.body(node.finalbody)
+
+    def visit_Global(self, node):
+        self.newline()
+        self.write('global ' + ', '.join(node.names))
+
+    def visit_Nonlocal(self, node):
+        self.newline()
+        self.write('nonlocal ' + ', '.join(node.names))
+
+    def visit_Return(self, node):
+        self.newline()
+        self.write('return ')
+        self.visit(node.value)
+
+    def visit_Break(self, node):
+        self.newline()
+        self.write('break')
+
+    def visit_Continue(self, node):
+        self.newline()
+        self.write('continue')
+
+    def visit_Raise(self, node):
+        # XXX: Python 2.6 / 3.0 compatibility
+        self.newline()
+        self.write('raise')
+        if hasattr(node, 'exc') and node.exc is not None:
+            self.write(' ')
+            self.visit(node.exc)
+            if node.cause is not None:
+                self.write(' from ')
+                self.visit(node.cause)
+        elif hasattr(node, 'type') and node.type is not None:
+            self.visit(node.type)
+            if node.inst is not None:
+                self.write(', ')
+                self.visit(node.inst)
+            if node.tback is not None:
+                self.write(', ')
+                self.visit(node.tback)
+
+    # Expressions
+
+    def visit_Attribute(self, node):
+        self.visit(node.value)
+        self.write('.' + node.attr)
+
+    def visit_Call(self, node):
+        want_comma = []
+        def write_comma():
+            if want_comma:
+                self.write(', ')
+            else:
+                want_comma.append(True)
+
+        self.visit(node.func)
+        self.write('(')
+        for arg in node.args:
+            write_comma()
+            self.visit(arg)
+        for keyword in node.keywords:
+            write_comma()
+            self.write(keyword.arg + '=')
+            self.visit(keyword.value)
+        if node.starargs is not None:
+            write_comma()
+            self.write('*')
+            self.visit(node.starargs)
+        if node.kwargs is not None:
+            write_comma()
+            self.write('**')
+            self.visit(node.kwargs)
+        self.write(')')
+
+    def visit_Name(self, node):
+        self.write(node.id)
+
+    def visit_NameConstant(self, node):
+        self.write(str(node.value))
+
+    def visit_arg(self, node):
+        self.write(node.arg)
+
+    def visit_Str(self, node):
+        self.write(repr(node.s))
+
+    def visit_Bytes(self, node):
+        self.write(repr(node.s))
+
+    def visit_Num(self, node):
+        self.write(repr(node.n))
+
+    def visit_Tuple(self, node):
+        self.write('(')
+        idx = -1
+        for idx, item in enumerate(node.elts):
+            if idx:
+                self.write(', ')
+            self.visit(item)
+        self.write(idx and ')' or ',)')
+
+    def sequence_visit(left, right):
+        def visit(self, node):
+            self.write(left)
+            for idx, item in enumerate(node.elts):
+                if idx:
+                    self.write(', ')
+                self.visit(item)
+            self.write(right)
+        return visit
+
+    visit_List = sequence_visit('[', ']')
+    visit_Set = sequence_visit('{', '}')
+    del sequence_visit
+
+    def visit_Dict(self, node):
+        self.write('{')
+        for idx, (key, value) in enumerate(zip(node.keys, node.values)):
+            if idx:
+                self.write(', ')
+            self.visit(key)
+            self.write(': ')
+            self.visit(value)
+        self.write('}')
+
+    def visit_BinOp(self, node):
+        self.write('(')
+        self.visit(node.left)
+        self.write(' %s ' % BINOP_SYMBOLS[type(node.op)])
+        self.visit(node.right)
+        self.write(')')
+
+    def visit_BoolOp(self, node):
+        self.write('(')
+        for idx, value in enumerate(node.values):
+            if idx:
+                self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)])
+            self.visit(value)
+        self.write(')')
+
+    def visit_Compare(self, node):
+        self.write('(')
+        self.visit(node.left)
+        for op, right in zip(node.ops, node.comparators):
+            self.write(' %s ' % CMPOP_SYMBOLS[type(op)])
+            self.visit(right)
+        self.write(')')
+
+    def visit_UnaryOp(self, node):
+        self.write('(')
+        op = UNARYOP_SYMBOLS[type(node.op)]
+        self.write(op)
+        if op == 'not':
+            self.write(' ')
+        self.visit(node.operand)
+        self.write(')')
+
+    def visit_Subscript(self, node):
+        self.visit(node.value)
+        self.write('[')
+        self.visit(node.slice)
+        self.write(']')
+
+    def visit_Slice(self, node):
+        if node.lower is not None:
+            self.visit(node.lower)
+        self.write(':')
+        if node.upper is not None:
+            self.visit(node.upper)
+        if node.step is not None:
+            self.write(':')
+            if not (isinstance(node.step, Name) and node.step.id == 'None'):
+                self.visit(node.step)
+
+    def visit_ExtSlice(self, node):
+        for idx, item in node.dims:
+            if idx:
+                self.write(', ')
+            self.visit(item)
+
+    def visit_Yield(self, node):
+        self.write('yield ')
+        self.visit(node.value)
+
+    def visit_Lambda(self, node):
+        self.write('lambda ')
+        self.signature(node.args)
+        self.write(': ')
+        self.visit(node.body)
+
+    def visit_Ellipsis(self, node):
+        self.write('Ellipsis')
+
+    def generator_visit(left, right):
+        def visit(self, node):
+            self.write(left)
+            self.visit(node.elt)
+            for comprehension in node.generators:
+                self.visit(comprehension)
+            self.write(right)
+        return visit
+
+    visit_ListComp = generator_visit('[', ']')
+    visit_GeneratorExp = generator_visit('(', ')')
+    visit_SetComp = generator_visit('{', '}')
+    del generator_visit
+
+    def visit_DictComp(self, node):
+        self.write('{')
+        self.visit(node.key)
+        self.write(': ')
+        self.visit(node.value)
+        for comprehension in node.generators:
+            self.visit(comprehension)
+        self.write('}')
+
+    def visit_IfExp(self, node):
+        self.visit(node.body)
+        self.write(' if ')
+        self.visit(node.test)
+        self.write(' else ')
+        self.visit(node.orelse)
+
+    def visit_Starred(self, node):
+        self.write('*')
+        self.visit(node.value)
+
+    def visit_Repr(self, node):
+        # XXX: python 2.6 only
+        self.write('`')
+        self.visit(node.value)
+        self.write('`')
+
+    # Helper Nodes
+
+    def visit_alias(self, node):
+        self.write(node.name)
+        if node.asname is not None:
+            self.write(' as ' + node.asname)
+
+    def visit_comprehension(self, node):
+        self.write(' for ')
+        self.visit(node.target)
+        self.write(' in ')
+        self.visit(node.iter)
+        if node.ifs:
+            for if_ in node.ifs:
+                self.write(' if ')
+                self.visit(if_)
+
+    def visit_excepthandler(self, node):
+        self.newline()
+        self.write('except')
+        if node.type is not None:
+            self.write(' ')
+            self.visit(node.type)
+            if node.name is not None:
+                self.write(' as ')
+                self.visit(node.name)
+        self.write(':')
+        self.body(node.body)
diff --git a/lib/mako/ast.py b/lib/mako/ast.py
new file mode 100644
index 0000000000000000000000000000000000000000..65fd84dfe155aa77bd38176d6f44500499fd4bfd
--- /dev/null
+++ b/lib/mako/ast.py
@@ -0,0 +1,178 @@
+# mako/ast.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""utilities for analyzing expressions and blocks of Python
+code, as well as generating Python from AST nodes"""
+
+from mako import exceptions, pyparser, compat
+import re
+
+class PythonCode(object):
+    """represents information about a string containing Python code"""
+    def __init__(self, code, **exception_kwargs):
+        self.code = code
+
+        # represents all identifiers which are assigned to at some point in
+        # the code
+        self.declared_identifiers = set()
+
+        # represents all identifiers which are referenced before their
+        # assignment, if any
+        self.undeclared_identifiers = set()
+
+        # note that an identifier can be in both the undeclared and declared
+        # lists.
+
+        # using AST to parse instead of using code.co_varnames,
+        # code.co_names has several advantages:
+        # - we can locate an identifier as "undeclared" even if
+        # its declared later in the same block of code
+        # - AST is less likely to break with version changes
+        # (for example, the behavior of co_names changed a little bit
+        # in python version 2.5)
+        if isinstance(code, compat.string_types):
+            expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
+        else:
+            expr = code
+
+        f = pyparser.FindIdentifiers(self, **exception_kwargs)
+        f.visit(expr)
+
+class ArgumentList(object):
+    """parses a fragment of code as a comma-separated list of expressions"""
+    def __init__(self, code, **exception_kwargs):
+        self.codeargs = []
+        self.args = []
+        self.declared_identifiers = set()
+        self.undeclared_identifiers = set()
+        if isinstance(code, compat.string_types):
+            if re.match(r"\S", code) and not re.match(r",\s*$", code):
+                # if theres text and no trailing comma, insure its parsed
+                # as a tuple by adding a trailing comma
+                code  += ","
+            expr = pyparser.parse(code, "exec", **exception_kwargs)
+        else:
+            expr = code
+
+        f = pyparser.FindTuple(self, PythonCode, **exception_kwargs)
+        f.visit(expr)
+
+class PythonFragment(PythonCode):
+    """extends PythonCode to provide identifier lookups in partial control
+    statements
+
+    e.g.
+        for x in 5:
+        elif y==9:
+        except (MyException, e):
+    etc.
+    """
+    def __init__(self, code, **exception_kwargs):
+        m = re.match(r'^(\w+)(?:\s+(.*?))?:\s*(#|$)', code.strip(), re.S)
+        if not m:
+            raise exceptions.CompileException(
+                          "Fragment '%s' is not a partial control statement" %
+                          code, **exception_kwargs)
+        if m.group(3):
+            code = code[:m.start(3)]
+        (keyword, expr) = m.group(1,2)
+        if keyword in ['for','if', 'while']:
+            code = code + "pass"
+        elif keyword == 'try':
+            code = code + "pass\nexcept:pass"
+        elif keyword == 'elif' or keyword == 'else':
+            code = "if False:pass\n" + code + "pass"
+        elif keyword == 'except':
+            code = "try:pass\n" + code + "pass"
+        elif keyword == 'with':
+            code = code + "pass"
+        else:
+            raise exceptions.CompileException(
+                                "Unsupported control keyword: '%s'" %
+                                keyword, **exception_kwargs)
+        super(PythonFragment, self).__init__(code, **exception_kwargs)
+
+
+class FunctionDecl(object):
+    """function declaration"""
+    def __init__(self, code, allow_kwargs=True, **exception_kwargs):
+        self.code = code
+        expr = pyparser.parse(code, "exec", **exception_kwargs)
+
+        f = pyparser.ParseFunc(self, **exception_kwargs)
+        f.visit(expr)
+        if not hasattr(self, 'funcname'):
+            raise exceptions.CompileException(
+                            "Code '%s' is not a function declaration" % code,
+                            **exception_kwargs)
+        if not allow_kwargs and self.kwargs:
+            raise exceptions.CompileException(
+                                "'**%s' keyword argument not allowed here" %
+                                self.kwargnames[-1], **exception_kwargs)
+
+    def get_argument_expressions(self, as_call=False):
+        """Return the argument declarations of this FunctionDecl as a printable
+        list.
+
+        By default the return value is appropriate for writing in a ``def``;
+        set `as_call` to true to build arguments to be passed to the function
+        instead (assuming locals with the same names as the arguments exist).
+        """
+
+        namedecls = []
+
+        # Build in reverse order, since defaults and slurpy args come last
+        argnames = self.argnames[::-1]
+        kwargnames = self.kwargnames[::-1]
+        defaults = self.defaults[::-1]
+        kwdefaults = self.kwdefaults[::-1]
+
+        # Named arguments
+        if self.kwargs:
+            namedecls.append("**" + kwargnames.pop(0))
+
+        for name in kwargnames:
+            # Keyword-only arguments must always be used by name, so even if
+            # this is a call, print out `foo=foo`
+            if as_call:
+                namedecls.append("%s=%s" % (name, name))
+            elif kwdefaults:
+                default = kwdefaults.pop(0)
+                if default is None:
+                    # The AST always gives kwargs a default, since you can do
+                    # `def foo(*, a=1, b, c=3)`
+                    namedecls.append(name)
+                else:
+                    namedecls.append("%s=%s" % (
+                        name, pyparser.ExpressionGenerator(default).value()))
+            else:
+                namedecls.append(name)
+
+        # Positional arguments
+        if self.varargs:
+            namedecls.append("*" + argnames.pop(0))
+
+        for name in argnames:
+            if as_call or not defaults:
+                namedecls.append(name)
+            else:
+                default = defaults.pop(0)
+                namedecls.append("%s=%s" % (
+                    name, pyparser.ExpressionGenerator(default).value()))
+
+        namedecls.reverse()
+        return namedecls
+
+    @property
+    def allargnames(self):
+        return tuple(self.argnames) + tuple(self.kwargnames)
+
+class FunctionArgs(FunctionDecl):
+    """the argument portion of a function declaration"""
+
+    def __init__(self, code, **kwargs):
+        super(FunctionArgs, self).__init__("def ANON(%s):pass" % code,
+                **kwargs)
diff --git a/lib/mako/cache.py b/lib/mako/cache.py
new file mode 100644
index 0000000000000000000000000000000000000000..c405c5171d747503f133854da9875d1457f65c92
--- /dev/null
+++ b/lib/mako/cache.py
@@ -0,0 +1,238 @@
+# mako/cache.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from mako import compat, util
+
+_cache_plugins = util.PluginLoader("mako.cache")
+
+register_plugin = _cache_plugins.register
+register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl")
+
+
+class Cache(object):
+    """Represents a data content cache made available to the module
+    space of a specific :class:`.Template` object.
+
+    .. versionadded:: 0.6
+       :class:`.Cache` by itself is mostly a
+       container for a :class:`.CacheImpl` object, which implements
+       a fixed API to provide caching services; specific subclasses exist to
+       implement different
+       caching strategies.   Mako includes a backend that works with
+       the Beaker caching system.   Beaker itself then supports
+       a number of backends (i.e. file, memory, memcached, etc.)
+
+    The construction of a :class:`.Cache` is part of the mechanics
+    of a :class:`.Template`, and programmatic access to this
+    cache is typically via the :attr:`.Template.cache` attribute.
+
+    """
+
+    impl = None
+    """Provide the :class:`.CacheImpl` in use by this :class:`.Cache`.
+
+    This accessor allows a :class:`.CacheImpl` with additional
+    methods beyond that of :class:`.Cache` to be used programmatically.
+
+    """
+
+    id = None
+    """Return the 'id' that identifies this cache.
+
+    This is a value that should be globally unique to the
+    :class:`.Template` associated with this cache, and can
+    be used by a caching system to name a local container
+    for data specific to this template.
+
+    """
+
+    starttime = None
+    """Epochal time value for when the owning :class:`.Template` was
+    first compiled.
+
+    A cache implementation may wish to invalidate data earlier than
+    this timestamp; this has the effect of the cache for a specific
+    :class:`.Template` starting clean any time the :class:`.Template`
+    is recompiled, such as when the original template file changed on
+    the filesystem.
+
+    """
+
+    def __init__(self, template, *args):
+        # check for a stale template calling the
+        # constructor
+        if isinstance(template, compat.string_types) and args:
+            return
+        self.template = template
+        self.id = template.module.__name__
+        self.starttime = template.module._modified_time
+        self._def_regions = {}
+        self.impl = self._load_impl(self.template.cache_impl)
+
+    def _load_impl(self, name):
+        return _cache_plugins.load(name)(self)
+
+    def get_or_create(self, key, creation_function, **kw):
+        """Retrieve a value from the cache, using the given creation function
+        to generate a new value."""
+
+        return self._ctx_get_or_create(key, creation_function, None, **kw)
+
+    def _ctx_get_or_create(self, key, creation_function, context, **kw):
+        """Retrieve a value from the cache, using the given creation function
+        to generate a new value."""
+
+        if not self.template.cache_enabled:
+            return creation_function()
+
+        return self.impl.get_or_create(
+            key,
+            creation_function,
+            **self._get_cache_kw(kw, context))
+
+    def set(self, key, value, **kw):
+        """Place a value in the cache.
+
+        :param key: the value's key.
+        :param value: the value.
+        :param \**kw: cache configuration arguments.
+
+        """
+
+        self.impl.set(key, value, **self._get_cache_kw(kw, None))
+
+    put = set
+    """A synonym for :meth:`.Cache.set`.
+
+    This is here for backwards compatibility.
+
+    """
+
+    def get(self, key, **kw):
+        """Retrieve a value from the cache.
+
+        :param key: the value's key.
+        :param \**kw: cache configuration arguments.  The
+         backend is configured using these arguments upon first request.
+         Subsequent requests that use the same series of configuration
+         values will use that same backend.
+
+        """
+        return self.impl.get(key, **self._get_cache_kw(kw, None))
+
+    def invalidate(self, key, **kw):
+        """Invalidate a value in the cache.
+
+        :param key: the value's key.
+        :param \**kw: cache configuration arguments.  The
+         backend is configured using these arguments upon first request.
+         Subsequent requests that use the same series of configuration
+         values will use that same backend.
+
+        """
+        self.impl.invalidate(key, **self._get_cache_kw(kw, None))
+
+    def invalidate_body(self):
+        """Invalidate the cached content of the "body" method for this
+        template.
+
+        """
+        self.invalidate('render_body', __M_defname='render_body')
+
+    def invalidate_def(self, name):
+        """Invalidate the cached content of a particular ``<%def>`` within this
+        template.
+
+        """
+
+        self.invalidate('render_%s' % name, __M_defname='render_%s' % name)
+
+    def invalidate_closure(self, name):
+        """Invalidate a nested ``<%def>`` within this template.
+
+        Caching of nested defs is a blunt tool as there is no
+        management of scope -- nested defs that use cache tags
+        need to have names unique of all other nested defs in the
+        template, else their content will be overwritten by
+        each other.
+
+        """
+
+        self.invalidate(name, __M_defname=name)
+
+    def _get_cache_kw(self, kw, context):
+        defname = kw.pop('__M_defname', None)
+        if not defname:
+            tmpl_kw = self.template.cache_args.copy()
+            tmpl_kw.update(kw)
+        elif defname in self._def_regions:
+            tmpl_kw = self._def_regions[defname]
+        else:
+            tmpl_kw = self.template.cache_args.copy()
+            tmpl_kw.update(kw)
+            self._def_regions[defname] = tmpl_kw
+        if context and self.impl.pass_context:
+            tmpl_kw = tmpl_kw.copy()
+            tmpl_kw.setdefault('context', context)
+        return tmpl_kw
+
+
+class CacheImpl(object):
+    """Provide a cache implementation for use by :class:`.Cache`."""
+
+    def __init__(self, cache):
+        self.cache = cache
+
+    pass_context = False
+    """If ``True``, the :class:`.Context` will be passed to
+    :meth:`get_or_create <.CacheImpl.get_or_create>` as the name ``'context'``.
+    """
+
+    def get_or_create(self, key, creation_function, **kw):
+        """Retrieve a value from the cache, using the given creation function
+        to generate a new value.
+
+        This function *must* return a value, either from
+        the cache, or via the given creation function.
+        If the creation function is called, the newly
+        created value should be populated into the cache
+        under the given key before being returned.
+
+        :param key: the value's key.
+        :param creation_function: function that when called generates
+         a new value.
+        :param \**kw: cache configuration arguments.
+
+        """
+        raise NotImplementedError()
+
+    def set(self, key, value, **kw):
+        """Place a value in the cache.
+
+        :param key: the value's key.
+        :param value: the value.
+        :param \**kw: cache configuration arguments.
+
+        """
+        raise NotImplementedError()
+
+    def get(self, key, **kw):
+        """Retrieve a value from the cache.
+
+        :param key: the value's key.
+        :param \**kw: cache configuration arguments.
+
+        """
+        raise NotImplementedError()
+
+    def invalidate(self, key, **kw):
+        """Invalidate a value in the cache.
+
+        :param key: the value's key.
+        :param \**kw: cache configuration arguments.
+
+        """
+        raise NotImplementedError()
diff --git a/lib/mako/cmd.py b/lib/mako/cmd.py
new file mode 100755
index 0000000000000000000000000000000000000000..1a9ca56637cf99311716a1ee55b9e6177b884022
--- /dev/null
+++ b/lib/mako/cmd.py
@@ -0,0 +1,62 @@
+# mako/cmd.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+from argparse import ArgumentParser
+from os.path import isfile, dirname
+import sys
+from mako.template import Template
+from mako.lookup import TemplateLookup
+from mako import exceptions
+
+def varsplit(var):
+    if "=" not in var:
+        return (var, "")
+    return var.split("=", 1)
+
+def _exit():
+    sys.stderr.write(exceptions.text_error_template().render())
+    sys.exit(1)
+
+def cmdline(argv=None):
+
+    parser = ArgumentParser("usage: %prog [FILENAME]")
+    parser.add_argument("--var", default=[], action="append",
+                  help="variable (can be used multiple times, use name=value)")
+    parser.add_argument("--template-dir", default=[], action="append",
+                  help="Directory to use for template lookup (multiple "
+                    "directories may be provided). If not given then if the "
+                    "template is read from stdin, the value defaults to be "
+                    "the current directory, otherwise it defaults to be the "
+                    "parent directory of the file provided.")
+    parser.add_argument('input', nargs='?', default='-')
+
+    options = parser.parse_args(argv)
+    if options.input == '-':
+        lookup_dirs = options.template_dir or ["."]
+        lookup = TemplateLookup(lookup_dirs)
+        try:
+            template = Template(sys.stdin.read(), lookup=lookup)
+        except:
+            _exit()
+    else:
+        filename = options.input
+        if not isfile(filename):
+            raise SystemExit("error: can't find %s" % filename)
+        lookup_dirs = options.template_dir or [dirname(filename)]
+        lookup = TemplateLookup(lookup_dirs)
+        try:
+            template = Template(filename=filename, lookup=lookup)
+        except:
+            _exit()
+
+    kw = dict([varsplit(var) for var in options.var])
+    try:
+        print(template.render(**kw))
+    except:
+        _exit()
+
+
+if __name__ == "__main__":
+    cmdline()
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b0bda8673127739d9020cd054f909cad2b5964e
--- /dev/null
+++ b/lib/mako/codegen.py
@@ -0,0 +1,1237 @@
+# mako/codegen.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides functionality for rendering a parsetree constructing into module
+source code."""
+
+import time
+import re
+from mako.pygen import PythonPrinter
+from mako import util, ast, parsetree, filters, exceptions
+from mako import compat
+
+
+MAGIC_NUMBER = 10
+
+# names which are hardwired into the
+# template and are not accessed via the
+# context itself
+RESERVED_NAMES = set(['context', 'loop', 'UNDEFINED'])
+
+def compile(node,
+                uri,
+                filename=None,
+                default_filters=None,
+                buffer_filters=None,
+                imports=None,
+                future_imports=None,
+                source_encoding=None,
+                generate_magic_comment=True,
+                disable_unicode=False,
+                strict_undefined=False,
+                enable_loop=True,
+                reserved_names=frozenset()):
+
+    """Generate module source code given a parsetree node,
+      uri, and optional source filename"""
+
+    # if on Py2K, push the "source_encoding" string to be
+    # a bytestring itself, as we will be embedding it into
+    # the generated source and we don't want to coerce the
+    # result into a unicode object, in "disable_unicode" mode
+    if not compat.py3k and isinstance(source_encoding, compat.text_type):
+        source_encoding = source_encoding.encode(source_encoding)
+
+
+    buf = util.FastEncodingBuffer()
+
+    printer = PythonPrinter(buf)
+    _GenerateRenderMethod(printer,
+                            _CompileContext(uri,
+                                            filename,
+                                            default_filters,
+                                            buffer_filters,
+                                            imports,
+                                            future_imports,
+                                            source_encoding,
+                                            generate_magic_comment,
+                                            disable_unicode,
+                                            strict_undefined,
+                                            enable_loop,
+                                            reserved_names),
+                                node)
+    return buf.getvalue()
+
+class _CompileContext(object):
+    def __init__(self,
+                    uri,
+                    filename,
+                    default_filters,
+                    buffer_filters,
+                    imports,
+                    future_imports,
+                    source_encoding,
+                    generate_magic_comment,
+                    disable_unicode,
+                    strict_undefined,
+                    enable_loop,
+                    reserved_names):
+        self.uri = uri
+        self.filename = filename
+        self.default_filters = default_filters
+        self.buffer_filters = buffer_filters
+        self.imports = imports
+        self.future_imports = future_imports
+        self.source_encoding = source_encoding
+        self.generate_magic_comment = generate_magic_comment
+        self.disable_unicode = disable_unicode
+        self.strict_undefined = strict_undefined
+        self.enable_loop = enable_loop
+        self.reserved_names = reserved_names
+
+class _GenerateRenderMethod(object):
+    """A template visitor object which generates the
+       full module source for a template.
+
+    """
+    def __init__(self, printer, compiler, node):
+        self.printer = printer
+        self.compiler = compiler
+        self.node = node
+        self.identifier_stack = [None]
+        self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag))
+
+        if self.in_def:
+            name = "render_%s" % node.funcname
+            args = node.get_argument_expressions()
+            filtered = len(node.filter_args.args) > 0
+            buffered = eval(node.attributes.get('buffered', 'False'))
+            cached = eval(node.attributes.get('cached', 'False'))
+            defs = None
+            pagetag = None
+            if node.is_block and not node.is_anonymous:
+                args += ['**pageargs']
+        else:
+            defs = self.write_toplevel()
+            pagetag = self.compiler.pagetag
+            name = "render_body"
+            if pagetag is not None:
+                args = pagetag.body_decl.get_argument_expressions()
+                if not pagetag.body_decl.kwargs:
+                    args += ['**pageargs']
+                cached = eval(pagetag.attributes.get('cached', 'False'))
+                self.compiler.enable_loop = self.compiler.enable_loop or eval(
+                                        pagetag.attributes.get(
+                                                'enable_loop', 'False')
+                                    )
+            else:
+                args = ['**pageargs']
+                cached = False
+            buffered = filtered = False
+        if args is None:
+            args = ['context']
+        else:
+            args = [a for a in ['context'] + args]
+
+        self.write_render_callable(
+                            pagetag or node,
+                            name, args,
+                            buffered, filtered, cached)
+
+        if defs is not None:
+            for node in defs:
+                _GenerateRenderMethod(printer, compiler, node)
+
+        if not self.in_def:
+            self.write_metadata_struct()
+
+    def write_metadata_struct(self):
+        self.printer.source_map[self.printer.lineno] = \
+                    max(self.printer.source_map)
+        struct = {
+            "filename": self.compiler.filename,
+            "uri": self.compiler.uri,
+            "source_encoding": self.compiler.source_encoding,
+            "line_map": self.printer.source_map,
+        }
+        self.printer.writelines(
+            '"""',
+            '__M_BEGIN_METADATA',
+            compat.json.dumps(struct),
+            '__M_END_METADATA\n'
+            '"""'
+        )
+
+    @property
+    def identifiers(self):
+        return self.identifier_stack[-1]
+
+    def write_toplevel(self):
+        """Traverse a template structure for module-level directives and
+        generate the start of module-level code.
+
+        """
+        inherit = []
+        namespaces = {}
+        module_code = []
+
+        self.compiler.pagetag = None
+
+        class FindTopLevel(object):
+            def visitInheritTag(s, node):
+                inherit.append(node)
+            def visitNamespaceTag(s, node):
+                namespaces[node.name] = node
+            def visitPageTag(s, node):
+                self.compiler.pagetag = node
+            def visitCode(s, node):
+                if node.ismodule:
+                    module_code.append(node)
+
+        f = FindTopLevel()
+        for n in self.node.nodes:
+            n.accept_visitor(f)
+
+        self.compiler.namespaces = namespaces
+
+        module_ident = set()
+        for n in module_code:
+            module_ident = module_ident.union(n.declared_identifiers())
+
+        module_identifiers = _Identifiers(self.compiler)
+        module_identifiers.declared = module_ident
+
+        # module-level names, python code
+        if self.compiler.generate_magic_comment and \
+                self.compiler.source_encoding:
+            self.printer.writeline("# -*- coding:%s -*-" %
+                                    self.compiler.source_encoding)
+
+        if self.compiler.future_imports:
+            self.printer.writeline("from __future__ import %s" %
+                                   (", ".join(self.compiler.future_imports),))
+        self.printer.writeline("from mako import runtime, filters, cache")
+        self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
+        self.printer.writeline("__M_dict_builtin = dict")
+        self.printer.writeline("__M_locals_builtin = locals")
+        self.printer.writeline("_magic_number = %r" % MAGIC_NUMBER)
+        self.printer.writeline("_modified_time = %r" % time.time())
+        self.printer.writeline("_enable_loop = %r" % self.compiler.enable_loop)
+        self.printer.writeline(
+                            "_template_filename = %r" % self.compiler.filename)
+        self.printer.writeline("_template_uri = %r" % self.compiler.uri)
+        self.printer.writeline(
+                    "_source_encoding = %r" % self.compiler.source_encoding)
+        if self.compiler.imports:
+            buf = ''
+            for imp in self.compiler.imports:
+                buf += imp + "\n"
+                self.printer.writeline(imp)
+            impcode = ast.PythonCode(
+                            buf,
+                            source='', lineno=0,
+                            pos=0,
+                            filename='template defined imports')
+        else:
+            impcode = None
+
+        main_identifiers = module_identifiers.branch(self.node)
+        module_identifiers.topleveldefs = \
+            module_identifiers.topleveldefs.\
+                union(main_identifiers.topleveldefs)
+        module_identifiers.declared.add("UNDEFINED")
+        if impcode:
+            module_identifiers.declared.update(impcode.declared_identifiers)
+
+        self.compiler.identifiers = module_identifiers
+        self.printer.writeline("_exports = %r" %
+                            [n.name for n in
+                            main_identifiers.topleveldefs.values()]
+                        )
+        self.printer.write_blanks(2)
+
+        if len(module_code):
+            self.write_module_code(module_code)
+
+        if len(inherit):
+            self.write_namespaces(namespaces)
+            self.write_inherit(inherit[-1])
+        elif len(namespaces):
+            self.write_namespaces(namespaces)
+
+        return list(main_identifiers.topleveldefs.values())
+
+    def write_render_callable(self, node, name, args, buffered, filtered,
+            cached):
+        """write a top-level render callable.
+
+        this could be the main render() method or that of a top-level def."""
+
+        if self.in_def:
+            decorator = node.decorator
+            if decorator:
+                self.printer.writeline(
+                                "@runtime._decorate_toplevel(%s)" % decorator)
+
+        self.printer.start_source(node.lineno)
+        self.printer.writelines(
+            "def %s(%s):" % (name, ','.join(args)),
+                # push new frame, assign current frame to __M_caller
+                "__M_caller = context.caller_stack._push_frame()",
+                "try:"
+        )
+        if buffered or filtered or cached:
+            self.printer.writeline("context._push_buffer()")
+
+        self.identifier_stack.append(
+                                self.compiler.identifiers.branch(self.node))
+        if (not self.in_def or self.node.is_block) and '**pageargs' in args:
+            self.identifier_stack[-1].argument_declared.add('pageargs')
+
+        if not self.in_def and (
+                                len(self.identifiers.locally_assigned) > 0 or
+                                len(self.identifiers.argument_declared) > 0
+                                ):
+            self.printer.writeline("__M_locals = __M_dict_builtin(%s)" %
+                                    ','.join([
+                                            "%s=%s" % (x, x) for x in
+                                            self.identifiers.argument_declared
+                                            ]))
+
+        self.write_variable_declares(self.identifiers, toplevel=True)
+
+        for n in self.node.nodes:
+            n.accept_visitor(self)
+
+        self.write_def_finish(self.node, buffered, filtered, cached)
+        self.printer.writeline(None)
+        self.printer.write_blanks(2)
+        if cached:
+            self.write_cache_decorator(
+                                node, name,
+                                args, buffered,
+                                self.identifiers, toplevel=True)
+
+    def write_module_code(self, module_code):
+        """write module-level template code, i.e. that which
+        is enclosed in <%! %> tags in the template."""
+        for n in module_code:
+            self.printer.start_source(n.lineno)
+            self.printer.write_indented_block(n.text)
+
+    def write_inherit(self, node):
+        """write the module-level inheritance-determination callable."""
+
+        self.printer.writelines(
+            "def _mako_inherit(template, context):",
+                "_mako_generate_namespaces(context)",
+                "return runtime._inherit_from(context, %s, _template_uri)" %
+                (node.parsed_attributes['file']),
+                None
+        )
+
+    def write_namespaces(self, namespaces):
+        """write the module-level namespace-generating callable."""
+        self.printer.writelines(
+            "def _mako_get_namespace(context, name):",
+                "try:",
+                    "return context.namespaces[(__name__, name)]",
+                "except KeyError:",
+                    "_mako_generate_namespaces(context)",
+                "return context.namespaces[(__name__, name)]",
+            None, None
+        )
+        self.printer.writeline("def _mako_generate_namespaces(context):")
+
+
+        for node in namespaces.values():
+            if 'import' in node.attributes:
+                self.compiler.has_ns_imports = True
+            self.printer.start_source(node.lineno)
+            if len(node.nodes):
+                self.printer.writeline("def make_namespace():")
+                export = []
+                identifiers = self.compiler.identifiers.branch(node)
+                self.in_def = True
+                class NSDefVisitor(object):
+                    def visitDefTag(s, node):
+                        s.visitDefOrBase(node)
+
+                    def visitBlockTag(s, node):
+                        s.visitDefOrBase(node)
+
+                    def visitDefOrBase(s, node):
+                        if node.is_anonymous:
+                            raise exceptions.CompileException(
+                                "Can't put anonymous blocks inside "
+                                "<%namespace>",
+                                **node.exception_kwargs
+                            )
+                        self.write_inline_def(node, identifiers, nested=False)
+                        export.append(node.funcname)
+                vis = NSDefVisitor()
+                for n in node.nodes:
+                    n.accept_visitor(vis)
+                self.printer.writeline("return [%s]" % (','.join(export)))
+                self.printer.writeline(None)
+                self.in_def = False
+                callable_name = "make_namespace()"
+            else:
+                callable_name = "None"
+
+            if 'file' in node.parsed_attributes:
+                self.printer.writeline(
+                                "ns = runtime.TemplateNamespace(%r,"
+                                " context._clean_inheritance_tokens(),"
+                                " templateuri=%s, callables=%s, "
+                                " calling_uri=_template_uri)" %
+                                (
+                                    node.name,
+                                    node.parsed_attributes.get('file', 'None'),
+                                    callable_name,
+                                )
+                            )
+            elif 'module' in node.parsed_attributes:
+                self.printer.writeline(
+                                "ns = runtime.ModuleNamespace(%r,"
+                                " context._clean_inheritance_tokens(),"
+                                " callables=%s, calling_uri=_template_uri,"
+                                " module=%s)" %
+                                (
+                                    node.name,
+                                    callable_name,
+                                    node.parsed_attributes.get(
+                                                'module', 'None')
+                                )
+                            )
+            else:
+                self.printer.writeline(
+                                "ns = runtime.Namespace(%r,"
+                                " context._clean_inheritance_tokens(),"
+                                " callables=%s, calling_uri=_template_uri)" %
+                                (
+                                    node.name,
+                                    callable_name,
+                                )
+                            )
+            if eval(node.attributes.get('inheritable', "False")):
+                self.printer.writeline("context['self'].%s = ns" % (node.name))
+
+            self.printer.writeline(
+                "context.namespaces[(__name__, %s)] = ns" % repr(node.name))
+            self.printer.write_blanks(1)
+        if not len(namespaces):
+            self.printer.writeline("pass")
+        self.printer.writeline(None)
+
+    def write_variable_declares(self, identifiers, toplevel=False, limit=None):
+        """write variable declarations at the top of a function.
+
+        the variable declarations are in the form of callable
+        definitions for defs and/or name lookup within the
+        function's context argument. the names declared are based
+        on the names that are referenced in the function body,
+        which don't otherwise have any explicit assignment
+        operation. names that are assigned within the body are
+        assumed to be locally-scoped variables and are not
+        separately declared.
+
+        for def callable definitions, if the def is a top-level
+        callable then a 'stub' callable is generated which wraps
+        the current Context into a closure. if the def is not
+        top-level, it is fully rendered as a local closure.
+
+        """
+
+        # collection of all defs available to us in this scope
+        comp_idents = dict([(c.funcname, c) for c in identifiers.defs])
+        to_write = set()
+
+        # write "context.get()" for all variables we are going to
+        # need that arent in the namespace yet
+        to_write = to_write.union(identifiers.undeclared)
+
+        # write closure functions for closures that we define
+        # right here
+        to_write = to_write.union(
+                        [c.funcname for c in identifiers.closuredefs.values()])
+
+        # remove identifiers that are declared in the argument
+        # signature of the callable
+        to_write = to_write.difference(identifiers.argument_declared)
+
+        # remove identifiers that we are going to assign to.
+        # in this way we mimic Python's behavior,
+        # i.e. assignment to a variable within a block
+        # means that variable is now a "locally declared" var,
+        # which cannot be referenced beforehand.
+        to_write = to_write.difference(identifiers.locally_declared)
+
+        if self.compiler.enable_loop:
+            has_loop = "loop" in to_write
+            to_write.discard("loop")
+        else:
+            has_loop = False
+
+        # if a limiting set was sent, constraint to those items in that list
+        # (this is used for the caching decorator)
+        if limit is not None:
+            to_write = to_write.intersection(limit)
+
+        if toplevel and getattr(self.compiler, 'has_ns_imports', False):
+            self.printer.writeline("_import_ns = {}")
+            self.compiler.has_imports = True
+            for ident, ns in self.compiler.namespaces.items():
+                if 'import' in ns.attributes:
+                    self.printer.writeline(
+                            "_mako_get_namespace(context, %r)."
+                                    "_populate(_import_ns, %r)" %
+                            (
+                                ident,
+                                re.split(r'\s*,\s*', ns.attributes['import'])
+                            ))
+
+        if has_loop:
+            self.printer.writeline(
+                'loop = __M_loop = runtime.LoopStack()'
+            )
+
+        for ident in to_write:
+            if ident in comp_idents:
+                comp = comp_idents[ident]
+                if comp.is_block:
+                    if not comp.is_anonymous:
+                        self.write_def_decl(comp, identifiers)
+                    else:
+                        self.write_inline_def(comp, identifiers, nested=True)
+                else:
+                    if comp.is_root():
+                        self.write_def_decl(comp, identifiers)
+                    else:
+                        self.write_inline_def(comp, identifiers, nested=True)
+
+            elif ident in self.compiler.namespaces:
+                self.printer.writeline(
+                            "%s = _mako_get_namespace(context, %r)" %
+                                (ident, ident)
+                            )
+            else:
+                if getattr(self.compiler, 'has_ns_imports', False):
+                    if self.compiler.strict_undefined:
+                        self.printer.writelines(
+                        "%s = _import_ns.get(%r, UNDEFINED)" %
+                        (ident, ident),
+                        "if %s is UNDEFINED:" % ident,
+                            "try:",
+                                "%s = context[%r]" % (ident, ident),
+                            "except KeyError:",
+                                "raise NameError(\"'%s' is not defined\")" %
+                                    ident,
+                            None, None
+                        )
+                    else:
+                        self.printer.writeline(
+                        "%s = _import_ns.get(%r, context.get(%r, UNDEFINED))" %
+                        (ident, ident, ident))
+                else:
+                    if self.compiler.strict_undefined:
+                        self.printer.writelines(
+                            "try:",
+                                "%s = context[%r]" % (ident, ident),
+                            "except KeyError:",
+                                "raise NameError(\"'%s' is not defined\")" %
+                                    ident,
+                            None
+                        )
+                    else:
+                        self.printer.writeline(
+                            "%s = context.get(%r, UNDEFINED)" % (ident, ident)
+                        )
+
+        self.printer.writeline("__M_writer = context.writer()")
+
+    def write_def_decl(self, node, identifiers):
+        """write a locally-available callable referencing a top-level def"""
+        funcname = node.funcname
+        namedecls = node.get_argument_expressions()
+        nameargs = node.get_argument_expressions(as_call=True)
+
+        if not self.in_def and (
+                                len(self.identifiers.locally_assigned) > 0 or
+                                len(self.identifiers.argument_declared) > 0):
+            nameargs.insert(0, 'context._locals(__M_locals)')
+        else:
+            nameargs.insert(0, 'context')
+        self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
+        self.printer.writeline(
+                    "return render_%s(%s)" % (funcname, ",".join(nameargs)))
+        self.printer.writeline(None)
+
+    def write_inline_def(self, node, identifiers, nested):
+        """write a locally-available def callable inside an enclosing def."""
+
+        namedecls = node.get_argument_expressions()
+
+        decorator = node.decorator
+        if decorator:
+            self.printer.writeline(
+                        "@runtime._decorate_inline(context, %s)" % decorator)
+        self.printer.writeline(
+                        "def %s(%s):" % (node.funcname, ",".join(namedecls)))
+        filtered = len(node.filter_args.args) > 0
+        buffered = eval(node.attributes.get('buffered', 'False'))
+        cached = eval(node.attributes.get('cached', 'False'))
+        self.printer.writelines(
+            # push new frame, assign current frame to __M_caller
+            "__M_caller = context.caller_stack._push_frame()",
+            "try:"
+        )
+        if buffered or filtered or cached:
+            self.printer.writelines(
+                "context._push_buffer()",
+            )
+
+        identifiers = identifiers.branch(node, nested=nested)
+
+        self.write_variable_declares(identifiers)
+
+        self.identifier_stack.append(identifiers)
+        for n in node.nodes:
+            n.accept_visitor(self)
+        self.identifier_stack.pop()
+
+        self.write_def_finish(node, buffered, filtered, cached)
+        self.printer.writeline(None)
+        if cached:
+            self.write_cache_decorator(node, node.funcname,
+                                        namedecls, False, identifiers,
+                                        inline=True, toplevel=False)
+
+    def write_def_finish(self, node, buffered, filtered, cached,
+            callstack=True):
+        """write the end section of a rendering function, either outermost or
+        inline.
+
+        this takes into account if the rendering function was filtered,
+        buffered, etc.  and closes the corresponding try: block if any, and
+        writes code to retrieve captured content, apply filters, send proper
+        return value."""
+
+        if not buffered and not cached and not filtered:
+            self.printer.writeline("return ''")
+            if callstack:
+                self.printer.writelines(
+                    "finally:",
+                        "context.caller_stack._pop_frame()",
+                    None
+                )
+
+        if buffered or filtered or cached:
+            if buffered or cached:
+                # in a caching scenario, don't try to get a writer
+                # from the context after popping; assume the caching
+                # implemenation might be using a context with no
+                # extra buffers
+                self.printer.writelines(
+                    "finally:",
+                        "__M_buf = context._pop_buffer()"
+                )
+            else:
+                self.printer.writelines(
+                    "finally:",
+                    "__M_buf, __M_writer = context._pop_buffer_and_writer()"
+                )
+
+            if callstack:
+                self.printer.writeline("context.caller_stack._pop_frame()")
+
+            s = "__M_buf.getvalue()"
+            if filtered:
+                s = self.create_filter_callable(node.filter_args.args, s,
+                                                False)
+            self.printer.writeline(None)
+            if buffered and not cached:
+                s = self.create_filter_callable(self.compiler.buffer_filters,
+                                                s, False)
+            if buffered or cached:
+                self.printer.writeline("return %s" % s)
+            else:
+                self.printer.writelines(
+                    "__M_writer(%s)" % s,
+                    "return ''"
+                )
+
+    def write_cache_decorator(self, node_or_pagetag, name,
+                                    args, buffered, identifiers,
+                                    inline=False, toplevel=False):
+        """write a post-function decorator to replace a rendering
+            callable with a cached version of itself."""
+
+        self.printer.writeline("__M_%s = %s" % (name, name))
+        cachekey = node_or_pagetag.parsed_attributes.get('cache_key',
+                                                         repr(name))
+
+        cache_args = {}
+        if self.compiler.pagetag is not None:
+            cache_args.update(
+                (
+                    pa[6:],
+                    self.compiler.pagetag.parsed_attributes[pa]
+                )
+                for pa in self.compiler.pagetag.parsed_attributes
+                if pa.startswith('cache_') and pa != 'cache_key'
+            )
+        cache_args.update(
+            (
+                pa[6:],
+                node_or_pagetag.parsed_attributes[pa]
+            ) for pa in node_or_pagetag.parsed_attributes
+            if pa.startswith('cache_') and pa != 'cache_key'
+        )
+        if 'timeout' in cache_args:
+            cache_args['timeout'] = int(eval(cache_args['timeout']))
+
+        self.printer.writeline("def %s(%s):" % (name, ','.join(args)))
+
+        # form "arg1, arg2, arg3=arg3, arg4=arg4", etc.
+        pass_args = [
+                        "%s=%s" % ((a.split('=')[0],) * 2) if '=' in a else a
+                        for a in args
+                    ]
+
+        self.write_variable_declares(
+                            identifiers,
+                            toplevel=toplevel,
+                            limit=node_or_pagetag.undeclared_identifiers()
+                        )
+        if buffered:
+            s = "context.get('local')."\
+                "cache._ctx_get_or_create("\
+                "%s, lambda:__M_%s(%s),  context, %s__M_defname=%r)" % (
+                                cachekey, name, ','.join(pass_args),
+                                ''.join(["%s=%s, " % (k, v)
+                                for k, v in cache_args.items()]),
+                                name
+                            )
+            # apply buffer_filters
+            s = self.create_filter_callable(self.compiler.buffer_filters, s,
+                                            False)
+            self.printer.writelines("return " + s, None)
+        else:
+            self.printer.writelines(
+                    "__M_writer(context.get('local')."
+                    "cache._ctx_get_or_create("
+                    "%s, lambda:__M_%s(%s), context, %s__M_defname=%r))" %
+                    (
+                        cachekey, name, ','.join(pass_args),
+                        ''.join(["%s=%s, " % (k, v)
+                        for k, v in cache_args.items()]),
+                        name,
+                    ),
+                    "return ''",
+                None
+            )
+
+    def create_filter_callable(self, args, target, is_expression):
+        """write a filter-applying expression based on the filters
+        present in the given filter names, adjusting for the global
+        'default' filter aliases as needed."""
+
+        def locate_encode(name):
+            if re.match(r'decode\..+', name):
+                return "filters." + name
+            elif self.compiler.disable_unicode:
+                return filters.NON_UNICODE_ESCAPES.get(name, name)
+            else:
+                return filters.DEFAULT_ESCAPES.get(name, name)
+
+        if 'n' not in args:
+            if is_expression:
+                if self.compiler.pagetag:
+                    args = self.compiler.pagetag.filter_args.args + args
+                if self.compiler.default_filters:
+                    args = self.compiler.default_filters + args
+        for e in args:
+            # if filter given as a function, get just the identifier portion
+            if e == 'n':
+                continue
+            m = re.match(r'(.+?)(\(.*\))', e)
+            if m:
+                ident, fargs = m.group(1, 2)
+                f = locate_encode(ident)
+                e = f + fargs
+            else:
+                e = locate_encode(e)
+                assert e is not None
+            target = "%s(%s)" % (e, target)
+        return target
+
+    def visitExpression(self, node):
+        self.printer.start_source(node.lineno)
+        if len(node.escapes) or \
+                (
+                    self.compiler.pagetag is not None and
+                    len(self.compiler.pagetag.filter_args.args)
+                ) or \
+                len(self.compiler.default_filters):
+
+            s = self.create_filter_callable(node.escapes_code.args,
+                                            "%s" % node.text, True)
+            self.printer.writeline("__M_writer(%s)" % s)
+        else:
+            self.printer.writeline("__M_writer(%s)" % node.text)
+
+    def visitControlLine(self, node):
+        if node.isend:
+            self.printer.writeline(None)
+            if node.has_loop_context:
+                self.printer.writeline('finally:')
+                self.printer.writeline("loop = __M_loop._exit()")
+                self.printer.writeline(None)
+        else:
+            self.printer.start_source(node.lineno)
+            if self.compiler.enable_loop and node.keyword == 'for':
+                text = mangle_mako_loop(node, self.printer)
+            else:
+                text = node.text
+            self.printer.writeline(text)
+            children = node.get_children()
+            # this covers the three situations where we want to insert a pass:
+            #    1) a ternary control line with no children,
+            #    2) a primary control line with nothing but its own ternary
+            #          and end control lines, and
+            #    3) any control line with no content other than comments
+            if not children or (
+                    compat.all(isinstance(c, (parsetree.Comment,
+                                            parsetree.ControlLine))
+                             for c in children) and
+                    compat.all((node.is_ternary(c.keyword) or c.isend)
+                             for c in children
+                             if isinstance(c, parsetree.ControlLine))):
+                self.printer.writeline("pass")
+
+    def visitText(self, node):
+        self.printer.start_source(node.lineno)
+        self.printer.writeline("__M_writer(%s)" % repr(node.content))
+
+    def visitTextTag(self, node):
+        filtered = len(node.filter_args.args) > 0
+        if filtered:
+            self.printer.writelines(
+                "__M_writer = context._push_writer()",
+                "try:",
+            )
+        for n in node.nodes:
+            n.accept_visitor(self)
+        if filtered:
+            self.printer.writelines(
+                "finally:",
+                "__M_buf, __M_writer = context._pop_buffer_and_writer()",
+                "__M_writer(%s)" %
+                self.create_filter_callable(
+                                node.filter_args.args,
+                                "__M_buf.getvalue()",
+                                False),
+                None
+            )
+
+    def visitCode(self, node):
+        if not node.ismodule:
+            self.printer.start_source(node.lineno)
+            self.printer.write_indented_block(node.text)
+
+            if not self.in_def and len(self.identifiers.locally_assigned) > 0:
+                # if we are the "template" def, fudge locally
+                # declared/modified variables into the "__M_locals" dictionary,
+                # which is used for def calls within the same template,
+                # to simulate "enclosing scope"
+                self.printer.writeline(
+                    '__M_locals_builtin_stored = __M_locals_builtin()')
+                self.printer.writeline(
+                    '__M_locals.update(__M_dict_builtin([(__M_key,'
+                    ' __M_locals_builtin_stored[__M_key]) for __M_key in'
+                    ' [%s] if __M_key in __M_locals_builtin_stored]))' %
+                    ','.join([repr(x) for x in node.declared_identifiers()]))
+
+    def visitIncludeTag(self, node):
+        self.printer.start_source(node.lineno)
+        args = node.attributes.get('args')
+        if args:
+            self.printer.writeline(
+                    "runtime._include_file(context, %s, _template_uri, %s)" %
+                    (node.parsed_attributes['file'], args))
+        else:
+            self.printer.writeline(
+                        "runtime._include_file(context, %s, _template_uri)" %
+                        (node.parsed_attributes['file']))
+
+    def visitNamespaceTag(self, node):
+        pass
+
+    def visitDefTag(self, node):
+        pass
+
+    def visitBlockTag(self, node):
+        if node.is_anonymous:
+            self.printer.writeline("%s()" % node.funcname)
+        else:
+            nameargs = node.get_argument_expressions(as_call=True)
+            nameargs += ['**pageargs']
+            self.printer.writeline("if 'parent' not in context._data or "
+                                  "not hasattr(context._data['parent'], '%s'):"
+                                  % node.funcname)
+            self.printer.writeline(
+                "context['self'].%s(%s)" % (node.funcname, ",".join(nameargs)))
+            self.printer.writeline("\n")
+
+    def visitCallNamespaceTag(self, node):
+        # TODO: we can put namespace-specific checks here, such
+        # as ensure the given namespace will be imported,
+        # pre-import the namespace, etc.
+        self.visitCallTag(node)
+
+    def visitCallTag(self, node):
+        self.printer.writeline("def ccall(caller):")
+        export = ['body']
+        callable_identifiers = self.identifiers.branch(node, nested=True)
+        body_identifiers = callable_identifiers.branch(node, nested=False)
+        # we want the 'caller' passed to ccall to be used
+        # for the body() function, but for other non-body()
+        # <%def>s within <%call> we want the current caller
+        # off the call stack (if any)
+        body_identifiers.add_declared('caller')
+
+        self.identifier_stack.append(body_identifiers)
+        class DefVisitor(object):
+            def visitDefTag(s, node):
+                s.visitDefOrBase(node)
+
+            def visitBlockTag(s, node):
+                s.visitDefOrBase(node)
+
+            def visitDefOrBase(s, node):
+                self.write_inline_def(node, callable_identifiers, nested=False)
+                if not node.is_anonymous:
+                    export.append(node.funcname)
+                # remove defs that are within the <%call> from the
+                # "closuredefs" defined in the body, so they dont render twice
+                if node.funcname in body_identifiers.closuredefs:
+                    del body_identifiers.closuredefs[node.funcname]
+
+        vis = DefVisitor()
+        for n in node.nodes:
+            n.accept_visitor(vis)
+        self.identifier_stack.pop()
+
+        bodyargs = node.body_decl.get_argument_expressions()
+        self.printer.writeline("def body(%s):" % ','.join(bodyargs))
+
+        # TODO: figure out best way to specify
+        # buffering/nonbuffering (at call time would be better)
+        buffered = False
+        if buffered:
+            self.printer.writelines(
+                "context._push_buffer()",
+                "try:"
+            )
+        self.write_variable_declares(body_identifiers)
+        self.identifier_stack.append(body_identifiers)
+
+        for n in node.nodes:
+            n.accept_visitor(self)
+        self.identifier_stack.pop()
+
+        self.write_def_finish(node, buffered, False, False, callstack=False)
+        self.printer.writelines(
+            None,
+            "return [%s]" % (','.join(export)),
+            None
+        )
+
+        self.printer.writelines(
+            # push on caller for nested call
+            "context.caller_stack.nextcaller = "
+                "runtime.Namespace('caller', context, "
+                                "callables=ccall(__M_caller))",
+            "try:")
+        self.printer.start_source(node.lineno)
+        self.printer.writelines(
+                "__M_writer(%s)" % self.create_filter_callable(
+                                                    [], node.expression, True),
+            "finally:",
+                "context.caller_stack.nextcaller = None",
+            None
+        )
+
+class _Identifiers(object):
+    """tracks the status of identifier names as template code is rendered."""
+
+    def __init__(self, compiler, node=None, parent=None, nested=False):
+        if parent is not None:
+            # if we are the branch created in write_namespaces(),
+            # we don't share any context from the main body().
+            if isinstance(node, parsetree.NamespaceTag):
+                self.declared = set()
+                self.topleveldefs = util.SetLikeDict()
+            else:
+                # things that have already been declared
+                # in an enclosing namespace (i.e. names we can just use)
+                self.declared = set(parent.declared).\
+                        union([c.name for c in parent.closuredefs.values()]).\
+                        union(parent.locally_declared).\
+                        union(parent.argument_declared)
+
+                # if these identifiers correspond to a "nested"
+                # scope, it means whatever the parent identifiers
+                # had as undeclared will have been declared by that parent,
+                # and therefore we have them in our scope.
+                if nested:
+                    self.declared = self.declared.union(parent.undeclared)
+
+                # top level defs that are available
+                self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
+        else:
+            self.declared = set()
+            self.topleveldefs = util.SetLikeDict()
+
+        self.compiler = compiler
+
+        # things within this level that are referenced before they
+        # are declared (e.g. assigned to)
+        self.undeclared = set()
+
+        # things that are declared locally.  some of these things
+        # could be in the "undeclared" list as well if they are
+        # referenced before declared
+        self.locally_declared = set()
+
+        # assignments made in explicit python blocks.
+        # these will be propagated to
+        # the context of local def calls.
+        self.locally_assigned = set()
+
+        # things that are declared in the argument
+        # signature of the def callable
+        self.argument_declared = set()
+
+        # closure defs that are defined in this level
+        self.closuredefs = util.SetLikeDict()
+
+        self.node = node
+
+        if node is not None:
+            node.accept_visitor(self)
+
+        illegal_names = self.compiler.reserved_names.intersection(
+                                                        self.locally_declared)
+        if illegal_names:
+            raise exceptions.NameConflictError(
+                "Reserved words declared in template: %s" %
+                ", ".join(illegal_names))
+
+
+    def branch(self, node, **kwargs):
+        """create a new Identifiers for a new Node, with
+          this Identifiers as the parent."""
+
+        return _Identifiers(self.compiler, node, self, **kwargs)
+
+    @property
+    def defs(self):
+        return set(self.topleveldefs.union(self.closuredefs).values())
+
+    def __repr__(self):
+        return "Identifiers(declared=%r, locally_declared=%r, "\
+                "undeclared=%r, topleveldefs=%r, closuredefs=%r, "\
+                "argumentdeclared=%r)" %\
+                (
+                    list(self.declared),
+                    list(self.locally_declared),
+                    list(self.undeclared),
+                    [c.name for c in self.topleveldefs.values()],
+                    [c.name for c in self.closuredefs.values()],
+                    self.argument_declared)
+
+    def check_declared(self, node):
+        """update the state of this Identifiers with the undeclared
+            and declared identifiers of the given node."""
+
+        for ident in node.undeclared_identifiers():
+            if ident != 'context' and\
+                    ident not in self.declared.union(self.locally_declared):
+                self.undeclared.add(ident)
+        for ident in node.declared_identifiers():
+            self.locally_declared.add(ident)
+
+    def add_declared(self, ident):
+        self.declared.add(ident)
+        if ident in self.undeclared:
+            self.undeclared.remove(ident)
+
+    def visitExpression(self, node):
+        self.check_declared(node)
+
+    def visitControlLine(self, node):
+        self.check_declared(node)
+
+    def visitCode(self, node):
+        if not node.ismodule:
+            self.check_declared(node)
+            self.locally_assigned = self.locally_assigned.union(
+                                                node.declared_identifiers())
+
+    def visitNamespaceTag(self, node):
+        # only traverse into the sub-elements of a
+        # <%namespace> tag if we are the branch created in
+        # write_namespaces()
+        if self.node is node:
+            for n in node.nodes:
+                n.accept_visitor(self)
+
+    def _check_name_exists(self, collection, node):
+        existing = collection.get(node.funcname)
+        collection[node.funcname] = node
+        if existing is not None and \
+            existing is not node and \
+            (node.is_block or existing.is_block):
+            raise exceptions.CompileException(
+                    "%%def or %%block named '%s' already "
+                    "exists in this template." %
+                    node.funcname, **node.exception_kwargs)
+
+    def visitDefTag(self, node):
+        if node.is_root() and not node.is_anonymous:
+            self._check_name_exists(self.topleveldefs, node)
+        elif node is not self.node:
+            self._check_name_exists(self.closuredefs, node)
+
+        for ident in node.undeclared_identifiers():
+            if ident != 'context' and \
+                    ident not in self.declared.union(self.locally_declared):
+                self.undeclared.add(ident)
+
+        # visit defs only one level deep
+        if node is self.node:
+            for ident in node.declared_identifiers():
+                self.argument_declared.add(ident)
+
+            for n in node.nodes:
+                n.accept_visitor(self)
+
+    def visitBlockTag(self, node):
+        if node is not self.node and not node.is_anonymous:
+
+            if isinstance(self.node, parsetree.DefTag):
+                raise exceptions.CompileException(
+                        "Named block '%s' not allowed inside of def '%s'"
+                        % (node.name, self.node.name), **node.exception_kwargs)
+            elif isinstance(self.node,
+                            (parsetree.CallTag, parsetree.CallNamespaceTag)):
+                raise exceptions.CompileException(
+                        "Named block '%s' not allowed inside of <%%call> tag"
+                        % (node.name, ), **node.exception_kwargs)
+
+        for ident in node.undeclared_identifiers():
+            if ident != 'context' and \
+                    ident not in self.declared.union(self.locally_declared):
+                self.undeclared.add(ident)
+
+        if not node.is_anonymous:
+            self._check_name_exists(self.topleveldefs, node)
+            self.undeclared.add(node.funcname)
+        elif node is not self.node:
+            self._check_name_exists(self.closuredefs, node)
+        for ident in node.declared_identifiers():
+            self.argument_declared.add(ident)
+        for n in node.nodes:
+            n.accept_visitor(self)
+
+    def visitTextTag(self, node):
+        for ident in node.undeclared_identifiers():
+            if ident != 'context' and \
+                    ident not in self.declared.union(self.locally_declared):
+                self.undeclared.add(ident)
+
+    def visitIncludeTag(self, node):
+        self.check_declared(node)
+
+    def visitPageTag(self, node):
+        for ident in node.declared_identifiers():
+            self.argument_declared.add(ident)
+        self.check_declared(node)
+
+    def visitCallNamespaceTag(self, node):
+        self.visitCallTag(node)
+
+    def visitCallTag(self, node):
+        if node is self.node:
+            for ident in node.undeclared_identifiers():
+                if ident != 'context' and \
+                        ident not in self.declared.union(
+                                                self.locally_declared):
+                    self.undeclared.add(ident)
+            for ident in node.declared_identifiers():
+                self.argument_declared.add(ident)
+            for n in node.nodes:
+                n.accept_visitor(self)
+        else:
+            for ident in node.undeclared_identifiers():
+                if ident != 'context' and \
+                        ident not in self.declared.union(
+                                                self.locally_declared):
+                    self.undeclared.add(ident)
+
+
+_FOR_LOOP = re.compile(
+        r'^for\s+((?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*'
+        r'(?:\s*,\s*(?:[A-Za-z_][A-Za-z0-9_]*),??)*\s*(?:\)?))\s+in\s+(.*):'
+)
+
+def mangle_mako_loop(node, printer):
+    """converts a for loop into a context manager wrapped around a for loop
+    when access to the `loop` variable has been detected in the for loop body
+    """
+    loop_variable = LoopVariable()
+    node.accept_visitor(loop_variable)
+    if loop_variable.detected:
+        node.nodes[-1].has_loop_context = True
+        match = _FOR_LOOP.match(node.text)
+        if match:
+            printer.writelines(
+                    'loop = __M_loop._enter(%s)' % match.group(2),
+                    'try:'
+                    #'with __M_loop(%s) as loop:' % match.group(2)
+            )
+            text = 'for %s in loop:' % match.group(1)
+        else:
+            raise SyntaxError("Couldn't apply loop context: %s" % node.text)
+    else:
+        text = node.text
+    return text
+
+
+class LoopVariable(object):
+    """A node visitor which looks for the name 'loop' within undeclared
+    identifiers."""
+
+    def __init__(self):
+        self.detected = False
+
+    def _loop_reference_detected(self, node):
+        if 'loop' in node.undeclared_identifiers():
+            self.detected = True
+        else:
+            for n in node.get_children():
+                n.accept_visitor(self)
+
+    def visitControlLine(self, node):
+        self._loop_reference_detected(node)
+
+    def visitCode(self, node):
+        self._loop_reference_detected(node)
+
+    def visitExpression(self, node):
+        self._loop_reference_detected(node)
diff --git a/lib/mako/compat.py b/lib/mako/compat.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe277bbf05a45149e2cea2f9269b806ec968bb30
--- /dev/null
+++ b/lib/mako/compat.py
@@ -0,0 +1,174 @@
+import sys
+import time
+
+py3k = sys.version_info >= (3, 0)
+py33 = sys.version_info >= (3, 3)
+py2k = sys.version_info < (3,)
+py26 = sys.version_info >= (2, 6)
+jython = sys.platform.startswith('java')
+win32 = sys.platform.startswith('win')
+pypy = hasattr(sys, 'pypy_version_info')
+
+if py3k:
+    from io import StringIO
+    import builtins as compat_builtins
+    from urllib.parse import quote_plus, unquote_plus
+    from html.entities import codepoint2name, name2codepoint
+    string_types = str,
+    binary_type = bytes
+    text_type = str
+
+    from io import BytesIO as byte_buffer
+
+    def u(s):
+        return s
+
+    def b(s):
+        return s.encode("latin-1")
+
+    def octal(lit):
+        return eval("0o" + lit)
+
+else:
+    import __builtin__ as compat_builtins
+    try:
+        from cStringIO import StringIO
+    except:
+        from StringIO import StringIO
+
+    byte_buffer = StringIO
+
+    from urllib import quote_plus, unquote_plus
+    from htmlentitydefs import codepoint2name, name2codepoint
+    string_types = basestring,
+    binary_type = str
+    text_type = unicode
+
+    def u(s):
+        return unicode(s, "utf-8")
+
+    def b(s):
+        return s
+
+    def octal(lit):
+        return eval("0" + lit)
+
+
+if py33:
+    from importlib import machinery
+    def load_module(module_id, path):
+        return machinery.SourceFileLoader(module_id, path).load_module()
+else:
+    import imp
+    def load_module(module_id, path):
+        fp = open(path, 'rb')
+        try:
+            return imp.load_source(module_id, path, fp)
+        finally:
+            fp.close()
+
+
+if py3k:
+    def reraise(tp, value, tb=None, cause=None):
+        if cause is not None:
+            value.__cause__ = cause
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+else:
+    exec("def reraise(tp, value, tb=None, cause=None):\n"
+            "    raise tp, value, tb\n")
+
+
+def exception_as():
+    return sys.exc_info()[1]
+
+try:
+    import threading
+    if py3k:
+        import _thread as thread
+    else:
+        import thread
+except ImportError:
+    import dummy_threading as threading
+    if py3k:
+        import _dummy_thread as thread
+    else:
+        import dummy_thread as thread
+
+if win32 or jython:
+    time_func = time.clock
+else:
+    time_func = time.time
+
+try:
+    from functools import partial
+except:
+    def partial(func, *args, **keywords):
+        def newfunc(*fargs, **fkeywords):
+            newkeywords = keywords.copy()
+            newkeywords.update(fkeywords)
+            return func(*(args + fargs), **newkeywords)
+        return newfunc
+
+
+all = all
+import json
+
+def exception_name(exc):
+    return exc.__class__.__name__
+
+try:
+    from inspect import CO_VARKEYWORDS, CO_VARARGS
+    def inspect_func_args(fn):
+        if py3k:
+            co = fn.__code__
+        else:
+            co = fn.func_code
+
+        nargs = co.co_argcount
+        names = co.co_varnames
+        args = list(names[:nargs])
+
+        varargs = None
+        if co.co_flags & CO_VARARGS:
+            varargs = co.co_varnames[nargs]
+            nargs = nargs + 1
+        varkw = None
+        if co.co_flags & CO_VARKEYWORDS:
+            varkw = co.co_varnames[nargs]
+
+        if py3k:
+            return args, varargs, varkw, fn.__defaults__
+        else:
+            return args, varargs, varkw, fn.func_defaults
+except ImportError:
+    import inspect
+    def inspect_func_args(fn):
+        return inspect.getargspec(fn)
+
+if py3k:
+    def callable(fn):
+        return hasattr(fn, '__call__')
+else:
+    callable = callable
+
+
+################################################
+# cross-compatible metaclass implementation
+# Copyright (c) 2010-2012 Benjamin Peterson
+def with_metaclass(meta, base=object):
+    """Create a base class with a metaclass."""
+    return meta("%sBase" % meta.__name__, (base,), {})
+################################################
+
+
+def arg_stringname(func_arg):
+    """Gets the string name of a kwarg or vararg
+    In Python3.4 a function's args are
+    of _ast.arg type not _ast.name
+    """
+    if hasattr(func_arg, 'arg'):
+        return func_arg.arg
+    else:
+        return str(func_arg)
diff --git a/lib/mako/exceptions.py b/lib/mako/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..c531f2118d0469c7faabc9ae340053ea54d08a83
--- /dev/null
+++ b/lib/mako/exceptions.py
@@ -0,0 +1,373 @@
+# mako/exceptions.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""exception classes"""
+
+import traceback
+import sys
+from mako import util, compat
+
+class MakoException(Exception):
+    pass
+
+class RuntimeException(MakoException):
+    pass
+
+def _format_filepos(lineno, pos, filename):
+    if filename is None:
+        return " at line: %d char: %d" % (lineno, pos)
+    else:
+        return " in file '%s' at line: %d char: %d" % (filename, lineno, pos)
+
+
+class CompileException(MakoException):
+    def __init__(self, message, source, lineno, pos, filename):
+        MakoException.__init__(self,
+                              message + _format_filepos(lineno, pos, filename))
+        self.lineno = lineno
+        self.pos = pos
+        self.filename = filename
+        self.source = source
+
+class SyntaxException(MakoException):
+    def __init__(self, message, source, lineno, pos, filename):
+        MakoException.__init__(self,
+                              message + _format_filepos(lineno, pos, filename))
+        self.lineno = lineno
+        self.pos = pos
+        self.filename = filename
+        self.source = source
+
+class UnsupportedError(MakoException):
+    """raised when a retired feature is used."""
+
+class NameConflictError(MakoException):
+    """raised when a reserved word is used inappropriately"""
+
+class TemplateLookupException(MakoException):
+    pass
+
+class TopLevelLookupException(TemplateLookupException):
+    pass
+
+class RichTraceback(object):
+    """Pull the current exception from the ``sys`` traceback and extracts
+    Mako-specific template information.
+
+    See the usage examples in :ref:`handling_exceptions`.
+
+    """
+    def __init__(self, error=None, traceback=None):
+        self.source, self.lineno = "", 0
+
+        if error is None or traceback is None:
+            t, value, tback = sys.exc_info()
+
+        if error is None:
+            error = value or t
+
+        if traceback is None:
+            traceback = tback
+
+        self.error = error
+        self.records = self._init(traceback)
+
+        if isinstance(self.error, (CompileException, SyntaxException)):
+            self.source = self.error.source
+            self.lineno = self.error.lineno
+            self._has_source = True
+
+        self._init_message()
+
+    @property
+    def errorname(self):
+        return compat.exception_name(self.error)
+
+    def _init_message(self):
+        """Find a unicode representation of self.error"""
+        try:
+            self.message = compat.text_type(self.error)
+        except UnicodeError:
+            try:
+                self.message = str(self.error)
+            except UnicodeEncodeError:
+                # Fallback to args as neither unicode nor
+                # str(Exception(u'\xe6')) work in Python < 2.6
+                self.message = self.error.args[0]
+        if not isinstance(self.message, compat.text_type):
+            self.message = compat.text_type(self.message, 'ascii', 'replace')
+
+    def _get_reformatted_records(self, records):
+        for rec in records:
+            if rec[6] is not None:
+                yield (rec[4], rec[5], rec[2], rec[6])
+            else:
+                yield tuple(rec[0:4])
+
+    @property
+    def traceback(self):
+        """Return a list of 4-tuple traceback records (i.e. normal python
+        format) with template-corresponding lines remapped to the originating
+        template.
+
+        """
+        return list(self._get_reformatted_records(self.records))
+
+    @property
+    def reverse_records(self):
+        return reversed(self.records)
+
+    @property
+    def reverse_traceback(self):
+        """Return the same data as traceback, except in reverse order.
+        """
+
+        return list(self._get_reformatted_records(self.reverse_records))
+
+    def _init(self, trcback):
+        """format a traceback from sys.exc_info() into 7-item tuples,
+        containing the regular four traceback tuple items, plus the original
+        template filename, the line number adjusted relative to the template
+        source, and code line from that line number of the template."""
+
+        import mako.template
+        mods = {}
+        rawrecords = traceback.extract_tb(trcback)
+        new_trcback = []
+        for filename, lineno, function, line in rawrecords:
+            if not line:
+                line = ''
+            try:
+                (line_map, template_lines) = mods[filename]
+            except KeyError:
+                try:
+                    info = mako.template._get_module_info(filename)
+                    module_source = info.code
+                    template_source = info.source
+                    template_filename = info.template_filename or filename
+                except KeyError:
+                    # A normal .py file (not a Template)
+                    if not compat.py3k:
+                        try:
+                            fp = open(filename, 'rb')
+                            encoding = util.parse_encoding(fp)
+                            fp.close()
+                        except IOError:
+                            encoding = None
+                        if encoding:
+                            line = line.decode(encoding)
+                        else:
+                            line = line.decode('ascii', 'replace')
+                    new_trcback.append((filename, lineno, function, line,
+                                            None, None, None, None))
+                    continue
+
+                template_ln = 1
+
+                source_map = mako.template.ModuleInfo.\
+                                get_module_source_metadata(
+                                    module_source, full_line_map=True)
+                line_map = source_map['full_line_map']
+
+                template_lines = [line for line in
+                                    template_source.split("\n")]
+                mods[filename] = (line_map, template_lines)
+
+            template_ln = line_map[lineno - 1]
+
+            if template_ln <= len(template_lines):
+                template_line = template_lines[template_ln - 1]
+            else:
+                template_line = None
+            new_trcback.append((filename, lineno, function,
+                                line, template_filename, template_ln,
+                                template_line, template_source))
+        if not self.source:
+            for l in range(len(new_trcback) - 1, 0, -1):
+                if new_trcback[l][5]:
+                    self.source = new_trcback[l][7]
+                    self.lineno = new_trcback[l][5]
+                    break
+            else:
+                if new_trcback:
+                    try:
+                        # A normal .py file (not a Template)
+                        fp = open(new_trcback[-1][0], 'rb')
+                        encoding = util.parse_encoding(fp)
+                        fp.seek(0)
+                        self.source = fp.read()
+                        fp.close()
+                        if encoding:
+                            self.source = self.source.decode(encoding)
+                    except IOError:
+                        self.source = ''
+                    self.lineno = new_trcback[-1][1]
+        return new_trcback
+
+
+def text_error_template(lookup=None):
+    """Provides a template that renders a stack trace in a similar format to
+    the Python interpreter, substituting source template filenames, line
+    numbers and code for that of the originating source template, as
+    applicable.
+
+    """
+    import mako.template
+    return mako.template.Template(r"""
+<%page args="error=None, traceback=None"/>
+<%!
+    from mako.exceptions import RichTraceback
+%>\
+<%
+    tback = RichTraceback(error=error, traceback=traceback)
+%>\
+Traceback (most recent call last):
+% for (filename, lineno, function, line) in tback.traceback:
+  File "${filename}", line ${lineno}, in ${function or '?'}
+    ${line | trim}
+% endfor
+${tback.errorname}: ${tback.message}
+""")
+
+
+def _install_pygments():
+    global syntax_highlight, pygments_html_formatter
+    from mako.ext.pygmentplugin import syntax_highlight,\
+            pygments_html_formatter
+
+def _install_fallback():
+    global syntax_highlight, pygments_html_formatter
+    from mako.filters import html_escape
+    pygments_html_formatter = None
+    def syntax_highlight(filename='', language=None):
+        return html_escape
+
+def _install_highlighting():
+    try:
+        _install_pygments()
+    except ImportError:
+        _install_fallback()
+_install_highlighting()
+
+def html_error_template():
+    """Provides a template that renders a stack trace in an HTML format,
+    providing an excerpt of code as well as substituting source template
+    filenames, line numbers and code for that of the originating source
+    template, as applicable.
+
+    The template's default ``encoding_errors`` value is
+    ``'htmlentityreplace'``. The template has two options. With the
+    ``full`` option disabled, only a section of an HTML document is
+    returned. With the ``css`` option disabled, the default stylesheet
+    won't be included.
+
+    """
+    import mako.template
+    return mako.template.Template(r"""
+<%!
+    from mako.exceptions import RichTraceback, syntax_highlight,\
+            pygments_html_formatter
+%>
+<%page args="full=True, css=True, error=None, traceback=None"/>
+% if full:
+<html>
+<head>
+    <title>Mako Runtime Error</title>
+% endif
+% if css:
+    <style>
+        body { font-family:verdana; margin:10px 30px 10px 30px;}
+        .stacktrace { margin:5px 5px 5px 5px; }
+        .highlight { padding:0px 10px 0px 10px; background-color:#9F9FDF; }
+        .nonhighlight { padding:0px; background-color:#DFDFDF; }
+        .sample { padding:10px; margin:10px 10px 10px 10px;
+                  font-family:monospace; }
+        .sampleline { padding:0px 10px 0px 10px; }
+        .sourceline { margin:5px 5px 10px 5px; font-family:monospace;}
+        .location { font-size:80%; }
+        .highlight { white-space:pre; }
+        .sampleline { white-space:pre; }
+
+    % if pygments_html_formatter:
+        ${pygments_html_formatter.get_style_defs()}
+        .linenos { min-width: 2.5em; text-align: right; }
+        pre { margin: 0; }
+        .syntax-highlighted { padding: 0 10px; }
+        .syntax-highlightedtable { border-spacing: 1px; }
+        .nonhighlight { border-top: 1px solid #DFDFDF;
+                        border-bottom: 1px solid #DFDFDF; }
+        .stacktrace .nonhighlight { margin: 5px 15px 10px; }
+        .sourceline { margin: 0 0; font-family:monospace; }
+        .code { background-color: #F8F8F8; width: 100%; }
+        .error .code { background-color: #FFBDBD; }
+        .error .syntax-highlighted { background-color: #FFBDBD; }
+    % endif
+
+    </style>
+% endif
+% if full:
+</head>
+<body>
+% endif
+
+<h2>Error !</h2>
+<%
+    tback = RichTraceback(error=error, traceback=traceback)
+    src = tback.source
+    line = tback.lineno
+    if src:
+        lines = src.split('\n')
+    else:
+        lines = None
+%>
+<h3>${tback.errorname}: ${tback.message|h}</h3>
+
+% if lines:
+    <div class="sample">
+    <div class="nonhighlight">
+% for index in range(max(0, line-4),min(len(lines), line+5)):
+    <%
+       if pygments_html_formatter:
+           pygments_html_formatter.linenostart = index + 1
+    %>
+    % if index + 1 == line:
+    <%
+       if pygments_html_formatter:
+           old_cssclass = pygments_html_formatter.cssclass
+           pygments_html_formatter.cssclass = 'error ' + old_cssclass
+    %>
+        ${lines[index] | syntax_highlight(language='mako')}
+    <%
+       if pygments_html_formatter:
+           pygments_html_formatter.cssclass = old_cssclass
+    %>
+    % else:
+        ${lines[index] | syntax_highlight(language='mako')}
+    % endif
+% endfor
+    </div>
+    </div>
+% endif
+
+<div class="stacktrace">
+% for (filename, lineno, function, line) in tback.reverse_traceback:
+    <div class="location">${filename}, line ${lineno}:</div>
+    <div class="nonhighlight">
+    <%
+       if pygments_html_formatter:
+           pygments_html_formatter.linenostart = lineno
+    %>
+      <div class="sourceline">${line | syntax_highlight(filename)}</div>
+    </div>
+% endfor
+</div>
+
+% if full:
+</body>
+</html>
+% endif
+""", output_encoding=sys.getdefaultencoding(),
+        encoding_errors='htmlentityreplace')
diff --git a/lib/mako/ext/__init__.py b/lib/mako/ext/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/mako/ext/autohandler.py b/lib/mako/ext/autohandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..8deaae19e9153a3d7bcd5e84bedf3eae8f1f23b5
--- /dev/null
+++ b/lib/mako/ext/autohandler.py
@@ -0,0 +1,65 @@
+# ext/autohandler.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""adds autohandler functionality to Mako templates.
+
+requires that the TemplateLookup class is used with templates.
+
+usage:
+
+<%!
+    from mako.ext.autohandler import autohandler
+%>
+<%inherit file="${autohandler(template, context)}"/>
+
+
+or with custom autohandler filename:
+
+<%!
+    from mako.ext.autohandler import autohandler
+%>
+<%inherit file="${autohandler(template, context, name='somefilename')}"/>
+
+"""
+
+import posixpath, os, re
+
+def autohandler(template, context, name='autohandler'):
+    lookup = context.lookup
+    _template_uri = template.module._template_uri
+    if not lookup.filesystem_checks:
+        try:
+            return lookup._uri_cache[(autohandler, _template_uri, name)]
+        except KeyError:
+            pass
+
+    tokens = re.findall(r'([^/]+)', posixpath.dirname(_template_uri)) + [name]
+    while len(tokens):
+        path = '/' + '/'.join(tokens)
+        if path != _template_uri and _file_exists(lookup, path):
+            if not lookup.filesystem_checks:
+                return lookup._uri_cache.setdefault(
+                            (autohandler, _template_uri, name), path)
+            else:
+                return path
+        if len(tokens) == 1:
+            break
+        tokens[-2:] = [name]
+ 
+    if not lookup.filesystem_checks:
+        return lookup._uri_cache.setdefault(
+                            (autohandler, _template_uri, name), None)
+    else:
+        return None
+
+def _file_exists(lookup, path):
+    psub = re.sub(r'^/', '',path)
+    for d in lookup.directories:
+        if os.path.exists(d + '/' + psub):
+            return True
+    else:
+        return False
+ 
diff --git a/lib/mako/ext/babelplugin.py b/lib/mako/ext/babelplugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..ead7081110be6aeb7c7b793b56f3bcfcc94bb4da
--- /dev/null
+++ b/lib/mako/ext/babelplugin.py
@@ -0,0 +1,49 @@
+# ext/babelplugin.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""gettext message extraction via Babel: http://babel.edgewall.org/"""
+from babel.messages.extract import extract_python
+from mako.ext.extract import MessageExtractor
+
+
+class BabelMakoExtractor(MessageExtractor):
+    def __init__(self, keywords, comment_tags, options):
+        self.keywords = keywords
+        self.options = options
+        self.config = {
+                'comment-tags': u' '.join(comment_tags),
+                'encoding': options.get('input_encoding',
+                    options.get('encoding', None)),
+            }
+        super(BabelMakoExtractor, self).__init__()
+
+    def __call__(self, fileobj):
+        return self.process_file(fileobj)
+
+    def process_python(self, code, code_lineno, translator_strings):
+        comment_tags = self.config['comment-tags']
+        for lineno, funcname, messages, python_translator_comments \
+                in extract_python(code,
+                        self.keywords, comment_tags, self.options):
+            yield (code_lineno + (lineno - 1), funcname, messages,
+                   translator_strings + python_translator_comments)
+
+
+def extract(fileobj, keywords, comment_tags, options):
+    """Extract messages from Mako templates.
+
+    :param fileobj: the file-like object the messages should be extracted from
+    :param keywords: a list of keywords (i.e. function names) that should be
+                     recognized as translation functions
+    :param comment_tags: a list of translator tags to search for and include
+                         in the results
+    :param options: a dictionary of additional options (optional)
+    :return: an iterator over ``(lineno, funcname, message, comments)`` tuples
+    :rtype: ``iterator``
+    """
+    extractor = BabelMakoExtractor(keywords, comment_tags, options)
+    for message in extractor(fileobj):
+        yield message
diff --git a/lib/mako/ext/beaker_cache.py b/lib/mako/ext/beaker_cache.py
new file mode 100644
index 0000000000000000000000000000000000000000..40ef7745e42c2f5136bd978d2646f43fcaf41b87
--- /dev/null
+++ b/lib/mako/ext/beaker_cache.py
@@ -0,0 +1,75 @@
+"""Provide a :class:`.CacheImpl` for the Beaker caching system."""
+
+from mako import exceptions
+
+from mako.cache import CacheImpl
+
+try:
+    from beaker import cache as beaker_cache
+except:
+    has_beaker = False
+else:
+    has_beaker = True
+
+_beaker_cache = None
+
+
+class BeakerCacheImpl(CacheImpl):
+    """A :class:`.CacheImpl` provided for the Beaker caching system.
+
+    This plugin is used by default, based on the default
+    value of ``'beaker'`` for the ``cache_impl`` parameter of the
+    :class:`.Template` or :class:`.TemplateLookup` classes.
+
+    """
+
+    def __init__(self, cache):
+        if not has_beaker:
+            raise exceptions.RuntimeException(
+                "Can't initialize Beaker plugin; Beaker is not installed.")
+        global _beaker_cache
+        if _beaker_cache is None:
+            if 'manager' in cache.template.cache_args:
+                _beaker_cache = cache.template.cache_args['manager']
+            else:
+                _beaker_cache = beaker_cache.CacheManager()
+        super(BeakerCacheImpl, self).__init__(cache)
+
+    def _get_cache(self, **kw):
+        expiretime = kw.pop('timeout', None)
+        if 'dir' in kw:
+            kw['data_dir'] = kw.pop('dir')
+        elif self.cache.template.module_directory:
+            kw['data_dir'] = self.cache.template.module_directory
+
+        if 'manager' in kw:
+            kw.pop('manager')
+
+        if kw.get('type') == 'memcached':
+            kw['type'] = 'ext:memcached'
+
+        if 'region' in kw:
+            region = kw.pop('region')
+            cache = _beaker_cache.get_cache_region(self.cache.id, region, **kw)
+        else:
+            cache = _beaker_cache.get_cache(self.cache.id, **kw)
+        cache_args = {'starttime': self.cache.starttime}
+        if expiretime:
+            cache_args['expiretime'] = expiretime
+        return cache, cache_args
+
+    def get_or_create(self, key, creation_function, **kw):
+        cache, kw = self._get_cache(**kw)
+        return cache.get(key, createfunc=creation_function, **kw)
+
+    def put(self, key, value, **kw):
+        cache, kw = self._get_cache(**kw)
+        cache.put(key, value, **kw)
+
+    def get(self, key, **kw):
+        cache, kw = self._get_cache(**kw)
+        return cache.get(key, **kw)
+
+    def invalidate(self, key, **kw):
+        cache, kw = self._get_cache(**kw)
+        cache.remove_value(key, **kw)
diff --git a/lib/mako/ext/extract.py b/lib/mako/ext/extract.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ce51757397ce6bf0e9b188a1d2433f1f3cb0271
--- /dev/null
+++ b/lib/mako/ext/extract.py
@@ -0,0 +1,101 @@
+import re
+from mako import compat
+from mako import lexer
+from mako import parsetree
+
+
+class MessageExtractor(object):
+    def process_file(self, fileobj):
+        template_node = lexer.Lexer(
+            fileobj.read(),
+            input_encoding=self.config['encoding']).parse()
+        for extracted in self.extract_nodes(template_node.get_children()):
+            yield extracted
+
+    def extract_nodes(self, nodes):
+        translator_comments = []
+        in_translator_comments = False
+        comment_tags = list(
+            filter(None, re.split(r'\s+', self.config['comment-tags'])))
+
+        for node in nodes:
+            child_nodes = None
+            if in_translator_comments and \
+                    isinstance(node, parsetree.Text) and \
+                    not node.content.strip():
+                # Ignore whitespace within translator comments
+                continue
+
+            if isinstance(node, parsetree.Comment):
+                value = node.text.strip()
+                if in_translator_comments:
+                    translator_comments.extend(
+                        self._split_comment(node.lineno, value))
+                    continue
+                for comment_tag in comment_tags:
+                    if value.startswith(comment_tag):
+                        in_translator_comments = True
+                        translator_comments.extend(
+                            self._split_comment(node.lineno, value))
+                continue
+
+            if isinstance(node, parsetree.DefTag):
+                code = node.function_decl.code
+                child_nodes = node.nodes
+            elif isinstance(node, parsetree.BlockTag):
+                code = node.body_decl.code
+                child_nodes = node.nodes
+            elif isinstance(node, parsetree.CallTag):
+                code = node.code.code
+                child_nodes = node.nodes
+            elif isinstance(node, parsetree.PageTag):
+                code = node.body_decl.code
+            elif isinstance(node, parsetree.CallNamespaceTag):
+                code = node.expression
+                child_nodes = node.nodes
+            elif isinstance(node, parsetree.ControlLine):
+                if node.isend:
+                    in_translator_comments = False
+                    continue
+                code = node.text
+            elif isinstance(node, parsetree.Code):
+                in_translator_comments = False
+                code = node.code.code
+            elif isinstance(node, parsetree.Expression):
+                code = node.code.code
+            else:
+                continue
+
+            # Comments don't apply unless they immediately preceed the message
+            if translator_comments and \
+                    translator_comments[-1][0] < node.lineno - 1:
+                translator_comments = []
+
+            translator_strings = [
+                comment[1] for comment in translator_comments]
+
+            if isinstance(code, compat.text_type):
+                code = code.encode('ascii', 'backslashreplace')
+
+            used_translator_comments = False
+            code = compat.byte_buffer(code)
+
+            for message in self.process_python(
+                    code, node.lineno, translator_strings):
+                yield message
+                used_translator_comments = True
+
+            if used_translator_comments:
+                translator_comments = []
+            in_translator_comments = False
+
+            if child_nodes:
+                for extracted in self.extract_nodes(child_nodes):
+                    yield extracted
+
+    @staticmethod
+    def _split_comment(lineno, comment):
+        """Return the multiline comment at lineno split into a list of
+        comment line numbers and the accompanying comment line"""
+        return [(lineno + index, line) for index, line in
+                enumerate(comment.splitlines())]
diff --git a/lib/mako/ext/linguaplugin.py b/lib/mako/ext/linguaplugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..a809072c5783fddf344b041e8da151de242d7364
--- /dev/null
+++ b/lib/mako/ext/linguaplugin.py
@@ -0,0 +1,38 @@
+import io
+from lingua.extractors import Extractor
+from lingua.extractors import Message
+from lingua.extractors import get_extractor
+from mako.ext.extract import MessageExtractor
+from mako import compat
+
+
+class LinguaMakoExtractor(Extractor, MessageExtractor):
+    '''Mako templates'''
+    extensions = ['.mako']
+    default_config = {
+        'encoding': 'utf-8',
+        'comment-tags': '',
+    }
+
+    def __call__(self, filename, options, fileobj=None):
+        self.options = options
+        self.filename = filename
+        self.python_extractor = get_extractor('x.py')
+        if fileobj is None:
+            fileobj = open(filename, 'rb')
+        return self.process_file(fileobj)
+
+    def process_python(self, code, code_lineno, translator_strings):
+        source = code.getvalue().strip()
+        if source.endswith(compat.b(':')):
+            source += compat.b(' pass')
+            code = io.BytesIO(source)
+        for msg in self.python_extractor(
+                self.filename, self.options, code, code_lineno):
+            if translator_strings:
+                msg = Message(msg.msgctxt, msg.msgid, msg.msgid_plural,
+                              msg.flags,
+                              compat.u(' ').join(
+                                  translator_strings + [msg.comment]),
+                              msg.tcomment, msg.location)
+            yield msg
diff --git a/lib/mako/ext/preprocessors.py b/lib/mako/ext/preprocessors.py
new file mode 100644
index 0000000000000000000000000000000000000000..c24893bd737431a37db9b7bf591404027d9f56dd
--- /dev/null
+++ b/lib/mako/ext/preprocessors.py
@@ -0,0 +1,20 @@
+# ext/preprocessors.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""preprocessing functions, used with the 'preprocessor' 
+argument on Template, TemplateLookup"""
+
+import re
+
+def convert_comments(text):
+    """preprocess old style comments.
+ 
+    example:
+ 
+    from mako.ext.preprocessors import convert_comments
+    t = Template(..., preprocessor=convert_comments)"""
+    return re.sub(r'(?<=\n)\s*#[^#]', "##", text)
+
diff --git a/lib/mako/ext/pygmentplugin.py b/lib/mako/ext/pygmentplugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..3adcfb8cb019064cb438beff2f656f1e72785889
--- /dev/null
+++ b/lib/mako/ext/pygmentplugin.py
@@ -0,0 +1,122 @@
+# ext/pygmentplugin.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from pygments.lexers.web import \
+     HtmlLexer, XmlLexer, JavascriptLexer, CssLexer
+from pygments.lexers.agile import PythonLexer, Python3Lexer
+from pygments.lexer import DelegatingLexer, RegexLexer, bygroups, \
+     include, using
+from pygments.token import \
+     Text, Comment, Operator, Keyword, Name, String, Other
+from pygments.formatters.html import HtmlFormatter
+from pygments import highlight
+from mako import compat
+
+class MakoLexer(RegexLexer):
+    name = 'Mako'
+    aliases = ['mako']
+    filenames = ['*.mao']
+
+    tokens = {
+        'root': [
+            (r'(\s*)(\%)(\s*end(?:\w+))(\n|\Z)',
+             bygroups(Text, Comment.Preproc, Keyword, Other)),
+            (r'(\s*)(\%(?!%))([^\n]*)(\n|\Z)',
+             bygroups(Text, Comment.Preproc, using(PythonLexer), Other)),
+            (r'(\s*)(##[^\n]*)(\n|\Z)',
+              bygroups(Text, Comment.Preproc, Other)),
+            (r'''(?s)<%doc>.*?</%doc>''', Comment.Preproc),
+            (r'(<%)([\w\.\:]+)',
+              bygroups(Comment.Preproc, Name.Builtin), 'tag'),
+            (r'(</%)([\w\.\:]+)(>)',
+              bygroups(Comment.Preproc, Name.Builtin, Comment.Preproc)),
+            (r'<%(?=([\w\.\:]+))', Comment.Preproc, 'ondeftags'),
+            (r'(<%(?:!?))(.*?)(%>)(?s)',
+              bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)),
+            (r'(\$\{)(.*?)(\})',
+             bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)),
+            (r'''(?sx)
+                (.+?)               # anything, followed by:
+                (?:
+                 (?<=\n)(?=%(?!%)|\#\#) |  # an eval or comment line
+                 (?=\#\*) |          # multiline comment
+                 (?=</?%) |         # a python block
+                                    # call start or end
+                 (?=\$\{) |         # a substitution
+                 (?<=\n)(?=\s*%) |
+                                    # - don't consume
+                 (\\\n) |           # an escaped newline
+                 \Z                 # end of string
+                )
+            ''', bygroups(Other, Operator)),
+            (r'\s+', Text),
+        ],
+        'ondeftags': [
+            (r'<%', Comment.Preproc),
+            (r'(?<=<%)(include|inherit|namespace|page)', Name.Builtin),
+            include('tag'),
+        ],
+        'tag': [
+            (r'((?:\w+)\s*=)\s*(".*?")',
+             bygroups(Name.Attribute, String)),
+            (r'/?\s*>', Comment.Preproc, '#pop'),
+            (r'\s+', Text),
+        ],
+        'attr': [
+            ('".*?"', String, '#pop'),
+            ("'.*?'", String, '#pop'),
+            (r'[^\s>]+', String, '#pop'),
+        ],
+    }
+
+
+class MakoHtmlLexer(DelegatingLexer):
+    name = 'HTML+Mako'
+    aliases = ['html+mako']
+
+    def __init__(self, **options):
+        super(MakoHtmlLexer, self).__init__(HtmlLexer, MakoLexer,
+                                              **options)
+
+class MakoXmlLexer(DelegatingLexer):
+    name = 'XML+Mako'
+    aliases = ['xml+mako']
+
+    def __init__(self, **options):
+        super(MakoXmlLexer, self).__init__(XmlLexer, MakoLexer,
+                                             **options)
+
+class MakoJavascriptLexer(DelegatingLexer):
+    name = 'JavaScript+Mako'
+    aliases = ['js+mako', 'javascript+mako']
+
+    def __init__(self, **options):
+        super(MakoJavascriptLexer, self).__init__(JavascriptLexer,
+                                                    MakoLexer, **options)
+
+class MakoCssLexer(DelegatingLexer):
+    name = 'CSS+Mako'
+    aliases = ['css+mako']
+
+    def __init__(self, **options):
+        super(MakoCssLexer, self).__init__(CssLexer, MakoLexer,
+                                             **options)
+
+
+pygments_html_formatter = HtmlFormatter(cssclass='syntax-highlighted',
+                                        linenos=True)
+def syntax_highlight(filename='', language=None):
+    mako_lexer = MakoLexer()
+    if compat.py3k:
+        python_lexer = Python3Lexer()
+    else:
+        python_lexer = PythonLexer()
+    if filename.startswith('memory:') or language == 'mako':
+        return lambda string: highlight(string, mako_lexer,
+                                        pygments_html_formatter)
+    return lambda string: highlight(string, python_lexer,
+                                    pygments_html_formatter)
+
diff --git a/lib/mako/ext/turbogears.py b/lib/mako/ext/turbogears.py
new file mode 100644
index 0000000000000000000000000000000000000000..d3976d97845c889e4364d68bc94be8ede71d2e95
--- /dev/null
+++ b/lib/mako/ext/turbogears.py
@@ -0,0 +1,58 @@
+# ext/turbogears.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import inspect
+from mako import compat
+from mako.lookup import TemplateLookup
+from mako.template import Template
+
+class TGPlugin(object):
+    """TurboGears compatible Template Plugin."""
+
+    def __init__(self, extra_vars_func=None, options=None, extension='mak'):
+        self.extra_vars_func = extra_vars_func
+        self.extension = extension
+        if not options:
+            options = {}
+
+        # Pull the options out and initialize the lookup
+        lookup_options = {}
+        for k, v in options.items():
+            if k.startswith('mako.'):
+                lookup_options[k[5:]] = v
+            elif k in ['directories', 'filesystem_checks', 'module_directory']:
+                lookup_options[k] = v
+        self.lookup = TemplateLookup(**lookup_options)
+
+        self.tmpl_options = {}
+        # transfer lookup args to template args, based on those available
+        # in getargspec
+        for kw in inspect.getargspec(Template.__init__)[0]:
+            if kw in lookup_options:
+                self.tmpl_options[kw] = lookup_options[kw]
+
+    def load_template(self, templatename, template_string=None):
+        """Loads a template from a file or a string"""
+        if template_string is not None:
+            return Template(template_string, **self.tmpl_options)
+        # Translate TG dot notation to normal / template path
+        if '/' not in templatename:
+            templatename = '/' + templatename.replace('.', '/') + '.' +\
+                    self.extension
+
+        # Lookup template
+        return self.lookup.get_template(templatename)
+
+    def render(self, info, format="html", fragment=False, template=None):
+        if isinstance(template, compat.string_types):
+            template = self.load_template(template)
+
+        # Load extra vars func if provided
+        if self.extra_vars_func:
+            info.update(self.extra_vars_func())
+
+        return template.render(**info)
+
diff --git a/lib/mako/filters.py b/lib/mako/filters.py
new file mode 100644
index 0000000000000000000000000000000000000000..d79ce2388f68016016026bad97d61c813345dd40
--- /dev/null
+++ b/lib/mako/filters.py
@@ -0,0 +1,201 @@
+# mako/filters.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+import re
+import codecs
+
+from mako.compat import quote_plus, unquote_plus, codepoint2name, \
+        name2codepoint
+
+from mako import compat
+
+xml_escapes = {
+    '&': '&amp;',
+    '>': '&gt;',
+    '<': '&lt;',
+    '"': '&#34;',   # also &quot; in html-only
+    "'": '&#39;'    # also &apos; in html-only
+}
+
+# XXX: &quot; is valid in HTML and XML
+#      &apos; is not valid HTML, but is valid XML
+
+def legacy_html_escape(s):
+    """legacy HTML escape for non-unicode mode."""
+    s = s.replace("&", "&amp;")
+    s = s.replace(">", "&gt;")
+    s = s.replace("<", "&lt;")
+    s = s.replace('"', "&#34;")
+    s = s.replace("'", "&#39;")
+    return s
+
+
+try:
+    import markupsafe
+    html_escape = markupsafe.escape
+except ImportError:
+    html_escape = legacy_html_escape
+
+def xml_escape(string):
+    return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
+
+def url_escape(string):
+    # convert into a list of octets
+    string = string.encode("utf8")
+    return quote_plus(string)
+
+def legacy_url_escape(string):
+    # convert into a list of octets
+    return quote_plus(string)
+
+def url_unescape(string):
+    text = unquote_plus(string)
+    if not is_ascii_str(text):
+        text = text.decode("utf8")
+    return text
+
+def trim(string):
+    return string.strip()
+
+
+class Decode(object):
+    def __getattr__(self, key):
+        def decode(x):
+            if isinstance(x, compat.text_type):
+                return x
+            elif not isinstance(x, compat.binary_type):
+                return decode(str(x))
+            else:
+                return compat.text_type(x, encoding=key)
+        return decode
+decode = Decode()
+
+
+_ASCII_re = re.compile(r'\A[\x00-\x7f]*\Z')
+
+def is_ascii_str(text):
+    return isinstance(text, str) and _ASCII_re.match(text)
+
+################################################################
+
+class XMLEntityEscaper(object):
+    def __init__(self, codepoint2name, name2codepoint):
+        self.codepoint2entity = dict([(c, compat.text_type('&%s;' % n))
+                                      for c, n in codepoint2name.items()])
+        self.name2codepoint = name2codepoint
+
+    def escape_entities(self, text):
+        """Replace characters with their character entity references.
+
+        Only characters corresponding to a named entity are replaced.
+        """
+        return compat.text_type(text).translate(self.codepoint2entity)
+
+    def __escape(self, m):
+        codepoint = ord(m.group())
+        try:
+            return self.codepoint2entity[codepoint]
+        except (KeyError, IndexError):
+            return '&#x%X;' % codepoint
+
+
+    __escapable = re.compile(r'["&<>]|[^\x00-\x7f]')
+
+    def escape(self, text):
+        """Replace characters with their character references.
+
+        Replace characters by their named entity references.
+        Non-ASCII characters, if they do not have a named entity reference,
+        are replaced by numerical character references.
+
+        The return value is guaranteed to be ASCII.
+        """
+        return self.__escapable.sub(self.__escape, compat.text_type(text)
+                                    ).encode('ascii')
+
+    # XXX: This regexp will not match all valid XML entity names__.
+    # (It punts on details involving involving CombiningChars and Extenders.)
+    #
+    # .. __: http://www.w3.org/TR/2000/REC-xml-20001006#NT-EntityRef
+    __characterrefs = re.compile(r'''& (?:
+                                          \#(\d+)
+                                          | \#x([\da-f]+)
+                                          | ( (?!\d) [:\w] [-.:\w]+ )
+                                          ) ;''',
+                                 re.X | re.UNICODE)
+
+    def __unescape(self, m):
+        dval, hval, name = m.groups()
+        if dval:
+            codepoint = int(dval)
+        elif hval:
+            codepoint = int(hval, 16)
+        else:
+            codepoint = self.name2codepoint.get(name, 0xfffd)
+            # U+FFFD = "REPLACEMENT CHARACTER"
+        if codepoint < 128:
+            return chr(codepoint)
+        return chr(codepoint)
+
+    def unescape(self, text):
+        """Unescape character references.
+
+        All character references (both entity references and numerical
+        character references) are unescaped.
+        """
+        return self.__characterrefs.sub(self.__unescape, text)
+
+
+_html_entities_escaper = XMLEntityEscaper(codepoint2name, name2codepoint)
+
+html_entities_escape = _html_entities_escaper.escape_entities
+html_entities_unescape = _html_entities_escaper.unescape
+
+
+def htmlentityreplace_errors(ex):
+    """An encoding error handler.
+
+    This python `codecs`_ error handler replaces unencodable
+    characters with HTML entities, or, if no HTML entity exists for
+    the character, XML character references.
+
+    >>> u'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
+    'The cost was &euro;12.'
+    """
+    if isinstance(ex, UnicodeEncodeError):
+        # Handle encoding errors
+        bad_text = ex.object[ex.start:ex.end]
+        text = _html_entities_escaper.escape(bad_text)
+        return (compat.text_type(text), ex.end)
+    raise ex
+
+codecs.register_error('htmlentityreplace', htmlentityreplace_errors)
+
+
+# TODO: options to make this dynamic per-compilation will be added in a later
+# release
+DEFAULT_ESCAPES = {
+    'x': 'filters.xml_escape',
+    'h': 'filters.html_escape',
+    'u': 'filters.url_escape',
+    'trim': 'filters.trim',
+    'entity': 'filters.html_entities_escape',
+    'unicode': 'unicode',
+    'decode': 'decode',
+    'str': 'str',
+    'n': 'n'
+}
+
+if compat.py3k:
+    DEFAULT_ESCAPES.update({
+        'unicode': 'str'
+    })
+
+NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy()
+NON_UNICODE_ESCAPES['h'] = 'filters.legacy_html_escape'
+NON_UNICODE_ESCAPES['u'] = 'filters.legacy_url_escape'
+
diff --git a/lib/mako/lexer.py b/lib/mako/lexer.py
new file mode 100644
index 0000000000000000000000000000000000000000..1dda398215d6c7623862340d1fd2a750f9ae301b
--- /dev/null
+++ b/lib/mako/lexer.py
@@ -0,0 +1,441 @@
+# mako/lexer.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides the Lexer class for parsing template strings into parse trees."""
+
+import re
+import codecs
+from mako import parsetree, exceptions, compat
+from mako.pygen import adjust_whitespace
+
+_regexp_cache = {}
+
+class Lexer(object):
+    def __init__(self, text, filename=None,
+                        disable_unicode=False,
+                        input_encoding=None, preprocessor=None):
+        self.text = text
+        self.filename = filename
+        self.template = parsetree.TemplateNode(self.filename)
+        self.matched_lineno = 1
+        self.matched_charpos = 0
+        self.lineno = 1
+        self.match_position = 0
+        self.tag = []
+        self.control_line = []
+        self.ternary_stack = []
+        self.disable_unicode = disable_unicode
+        self.encoding = input_encoding
+
+        if compat.py3k and disable_unicode:
+            raise exceptions.UnsupportedError(
+                                    "Mako for Python 3 does not "
+                                    "support disabling Unicode")
+
+        if preprocessor is None:
+            self.preprocessor = []
+        elif not hasattr(preprocessor, '__iter__'):
+            self.preprocessor = [preprocessor]
+        else:
+            self.preprocessor = preprocessor
+
+    @property
+    def exception_kwargs(self):
+        return {'source': self.text,
+                'lineno': self.matched_lineno,
+                'pos': self.matched_charpos,
+                'filename': self.filename}
+
+    def match(self, regexp, flags=None):
+        """compile the given regexp, cache the reg, and call match_reg()."""
+
+        try:
+            reg = _regexp_cache[(regexp, flags)]
+        except KeyError:
+            if flags:
+                reg = re.compile(regexp, flags)
+            else:
+                reg = re.compile(regexp)
+            _regexp_cache[(regexp, flags)] = reg
+
+        return self.match_reg(reg)
+
+    def match_reg(self, reg):
+        """match the given regular expression object to the current text
+        position.
+
+        if a match occurs, update the current text and line position.
+
+        """
+
+        mp = self.match_position
+
+        match = reg.match(self.text, self.match_position)
+        if match:
+            (start, end) = match.span()
+            if end == start:
+                self.match_position = end + 1
+            else:
+                self.match_position = end
+            self.matched_lineno = self.lineno
+            lines = re.findall(r"\n", self.text[mp:self.match_position])
+            cp = mp - 1
+            while (cp >= 0 and cp < self.textlength and self.text[cp] != '\n'):
+                cp -= 1
+            self.matched_charpos = mp - cp
+            self.lineno += len(lines)
+            #print "MATCHED:", match.group(0), "LINE START:",
+            # self.matched_lineno, "LINE END:", self.lineno
+        #print "MATCH:", regexp, "\n", self.text[mp : mp + 15], \
+        #          (match and "TRUE" or "FALSE")
+        return match
+
+    def parse_until_text(self, *text):
+        startpos = self.match_position
+        text_re = r'|'.join(text)
+        brace_level = 0
+        while True:
+            match = self.match(r'#.*\n')
+            if match:
+                continue
+            match = self.match(r'(\"\"\"|\'\'\'|\"|\')((?<!\\)\\\1|.)*?\1',
+                               re.S)
+            if match:
+                continue
+            match = self.match(r'(%s)' % text_re)
+            if match:
+                if match.group(1) == '}' and brace_level > 0:
+                    brace_level -= 1
+                    continue
+                return \
+                    self.text[startpos:
+                              self.match_position - len(match.group(1))],\
+                    match.group(1)
+            match = self.match(r"(.*?)(?=\"|\'|#|%s)" % text_re, re.S)
+            if match:
+                brace_level += match.group(1).count('{')
+                brace_level -= match.group(1).count('}')
+                continue
+            raise exceptions.SyntaxException(
+                        "Expected: %s" %
+                        ','.join(text),
+                        **self.exception_kwargs)
+
+    def append_node(self, nodecls, *args, **kwargs):
+        kwargs.setdefault('source', self.text)
+        kwargs.setdefault('lineno', self.matched_lineno)
+        kwargs.setdefault('pos', self.matched_charpos)
+        kwargs['filename'] = self.filename
+        node = nodecls(*args, **kwargs)
+        if len(self.tag):
+            self.tag[-1].nodes.append(node)
+        else:
+            self.template.nodes.append(node)
+        # build a set of child nodes for the control line
+        # (used for loop variable detection)
+        # also build a set of child nodes on ternary control lines
+        # (used for determining if a pass needs to be auto-inserted
+        if self.control_line:
+            control_frame = self.control_line[-1]
+            control_frame.nodes.append(node)
+            if not (isinstance(node, parsetree.ControlLine) and
+                    control_frame.is_ternary(node.keyword)):
+                if self.ternary_stack and self.ternary_stack[-1]:
+                    self.ternary_stack[-1][-1].nodes.append(node)
+        if isinstance(node, parsetree.Tag):
+            if len(self.tag):
+                node.parent = self.tag[-1]
+            self.tag.append(node)
+        elif isinstance(node, parsetree.ControlLine):
+            if node.isend:
+                self.control_line.pop()
+                self.ternary_stack.pop()
+            elif node.is_primary:
+                self.control_line.append(node)
+                self.ternary_stack.append([])
+            elif self.control_line and \
+                    self.control_line[-1].is_ternary(node.keyword):
+                self.ternary_stack[-1].append(node)
+            elif self.control_line and \
+                    not self.control_line[-1].is_ternary(node.keyword):
+                raise exceptions.SyntaxException(
+                        "Keyword '%s' not a legal ternary for keyword '%s'" %
+                        (node.keyword, self.control_line[-1].keyword),
+                        **self.exception_kwargs)
+
+    _coding_re = re.compile(r'#.*coding[:=]\s*([-\w.]+).*\r?\n')
+
+    def decode_raw_stream(self, text, decode_raw, known_encoding, filename):
+        """given string/unicode or bytes/string, determine encoding
+           from magic encoding comment, return body as unicode
+           or raw if decode_raw=False
+
+        """
+        if isinstance(text, compat.text_type):
+            m = self._coding_re.match(text)
+            encoding = m and m.group(1) or known_encoding or 'ascii'
+            return encoding, text
+
+        if text.startswith(codecs.BOM_UTF8):
+            text = text[len(codecs.BOM_UTF8):]
+            parsed_encoding = 'utf-8'
+            m = self._coding_re.match(text.decode('utf-8', 'ignore'))
+            if m is not None and m.group(1) != 'utf-8':
+                raise exceptions.CompileException(
+                                "Found utf-8 BOM in file, with conflicting "
+                                "magic encoding comment of '%s'" % m.group(1),
+                                text.decode('utf-8', 'ignore'),
+                                0, 0, filename)
+        else:
+            m = self._coding_re.match(text.decode('utf-8', 'ignore'))
+            if m:
+                parsed_encoding = m.group(1)
+            else:
+                parsed_encoding = known_encoding or 'ascii'
+
+        if decode_raw:
+            try:
+                text = text.decode(parsed_encoding)
+            except UnicodeDecodeError:
+                raise exceptions.CompileException(
+                        "Unicode decode operation of encoding '%s' failed" %
+                        parsed_encoding,
+                        text.decode('utf-8', 'ignore'),
+                        0, 0, filename)
+
+        return parsed_encoding, text
+
+    def parse(self):
+        self.encoding, self.text = self.decode_raw_stream(self.text,
+                                        not self.disable_unicode,
+                                        self.encoding,
+                                        self.filename,)
+
+        for preproc in self.preprocessor:
+            self.text = preproc(self.text)
+
+        # push the match marker past the
+        # encoding comment.
+        self.match_reg(self._coding_re)
+
+        self.textlength = len(self.text)
+
+        while (True):
+            if self.match_position > self.textlength:
+                break
+
+            if self.match_end():
+                break
+            if self.match_expression():
+                continue
+            if self.match_control_line():
+                continue
+            if self.match_comment():
+                continue
+            if self.match_tag_start():
+                continue
+            if self.match_tag_end():
+                continue
+            if self.match_python_block():
+                continue
+            if self.match_text():
+                continue
+
+            if self.match_position > self.textlength:
+                break
+            raise exceptions.CompileException("assertion failed")
+
+        if len(self.tag):
+            raise exceptions.SyntaxException("Unclosed tag: <%%%s>" %
+                                                self.tag[-1].keyword,
+                                                **self.exception_kwargs)
+        if len(self.control_line):
+            raise exceptions.SyntaxException(
+                                    "Unterminated control keyword: '%s'" %
+                                    self.control_line[-1].keyword,
+                                    self.text,
+                                    self.control_line[-1].lineno,
+                                    self.control_line[-1].pos, self.filename)
+        return self.template
+
+    def match_tag_start(self):
+        match = self.match(r'''
+            \<%     # opening tag
+
+            ([\w\.\:]+)   # keyword
+
+            ((?:\s+\w+|\s*=\s*|".*?"|'.*?')*)  # attrname, = \
+                                               #        sign, string expression
+
+            \s*     # more whitespace
+
+            (/)?>   # closing
+
+            ''',
+
+            re.I | re.S | re.X)
+
+        if match:
+            keyword, attr, isend = match.groups()
+            self.keyword = keyword
+            attributes = {}
+            if attr:
+                for att in re.findall(
+                           r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr):
+                    key, val1, val2 = att
+                    text = val1 or val2
+                    text = text.replace('\r\n', '\n')
+                    attributes[key] = text
+            self.append_node(parsetree.Tag, keyword, attributes)
+            if isend:
+                self.tag.pop()
+            else:
+                if keyword == 'text':
+                    match = self.match(r'(.*?)(?=\</%text>)',  re.S)
+                    if not match:
+                        raise exceptions.SyntaxException(
+                                            "Unclosed tag: <%%%s>" %
+                                            self.tag[-1].keyword,
+                                            **self.exception_kwargs)
+                    self.append_node(parsetree.Text, match.group(1))
+                    return self.match_tag_end()
+            return True
+        else:
+            return False
+
+    def match_tag_end(self):
+        match = self.match(r'\</%[\t ]*(.+?)[\t ]*>')
+        if match:
+            if not len(self.tag):
+                raise exceptions.SyntaxException(
+                                "Closing tag without opening tag: </%%%s>" %
+                                match.group(1),
+                                **self.exception_kwargs)
+            elif self.tag[-1].keyword != match.group(1):
+                raise exceptions.SyntaxException(
+                            "Closing tag </%%%s> does not match tag: <%%%s>" %
+                            (match.group(1), self.tag[-1].keyword),
+                            **self.exception_kwargs)
+            self.tag.pop()
+            return True
+        else:
+            return False
+
+    def match_end(self):
+        match = self.match(r'\Z', re.S)
+        if match:
+            string = match.group()
+            if string:
+                return string
+            else:
+                return True
+        else:
+            return False
+
+    def match_text(self):
+        match = self.match(r"""
+                (.*?)         # anything, followed by:
+                (
+                 (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based
+                                             # comment preceded by a
+                                             # consumed newline and whitespace
+                 |
+                 (?=\${)      # an expression
+                 |
+                 (?=</?[%&])  # a substitution or block or call start or end
+                              # - don't consume
+                 |
+                 (\\\r?\n)    # an escaped newline  - throw away
+                 |
+                 \Z           # end of string
+                )""", re.X | re.S)
+
+        if match:
+            text = match.group(1)
+            if text:
+                self.append_node(parsetree.Text, text)
+            return True
+        else:
+            return False
+
+    def match_python_block(self):
+        match = self.match(r"<%(!)?")
+        if match:
+            line, pos = self.matched_lineno, self.matched_charpos
+            text, end = self.parse_until_text(r'%>')
+            # the trailing newline helps
+            # compiler.parse() not complain about indentation
+            text = adjust_whitespace(text) + "\n"
+            self.append_node(
+                        parsetree.Code,
+                        text,
+                        match.group(1) == '!', lineno=line, pos=pos)
+            return True
+        else:
+            return False
+
+    def match_expression(self):
+        match = self.match(r"\${")
+        if match:
+            line, pos = self.matched_lineno, self.matched_charpos
+            text, end = self.parse_until_text(r'\|', r'}')
+            if end == '|':
+                escapes, end = self.parse_until_text(r'}')
+            else:
+                escapes = ""
+            text = text.replace('\r\n', '\n')
+            self.append_node(
+                            parsetree.Expression,
+                            text, escapes.strip(),
+                            lineno=line, pos=pos)
+            return True
+        else:
+            return False
+
+    def match_control_line(self):
+        match = self.match(
+                    r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)"
+                    r"(?:\r?\n|\Z)", re.M)
+        if match:
+            operator = match.group(1)
+            text = match.group(2)
+            if operator == '%':
+                m2 = re.match(r'(end)?(\w+)\s*(.*)', text)
+                if not m2:
+                    raise exceptions.SyntaxException(
+                                "Invalid control line: '%s'" %
+                                text,
+                                **self.exception_kwargs)
+                isend, keyword = m2.group(1, 2)
+                isend = (isend is not None)
+
+                if isend:
+                    if not len(self.control_line):
+                        raise exceptions.SyntaxException(
+                                "No starting keyword '%s' for '%s'" %
+                                (keyword, text),
+                                **self.exception_kwargs)
+                    elif self.control_line[-1].keyword != keyword:
+                        raise exceptions.SyntaxException(
+                                "Keyword '%s' doesn't match keyword '%s'" %
+                                (text, self.control_line[-1].keyword),
+                                **self.exception_kwargs)
+                self.append_node(parsetree.ControlLine, keyword, isend, text)
+            else:
+                self.append_node(parsetree.Comment, text)
+            return True
+        else:
+            return False
+
+    def match_comment(self):
+        """matches the multiline version of a comment"""
+        match = self.match(r"<%doc>(.*?)</%doc>", re.S)
+        if match:
+            self.append_node(parsetree.Comment, match.group(1))
+            return True
+        else:
+            return False
+
diff --git a/lib/mako/lookup.py b/lib/mako/lookup.py
new file mode 100644
index 0000000000000000000000000000000000000000..2af5411907ab930850cbc6b35a49504955281c79
--- /dev/null
+++ b/lib/mako/lookup.py
@@ -0,0 +1,359 @@
+# mako/lookup.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import os, stat, posixpath, re
+from mako import exceptions, util
+from mako.template import Template
+
+try:
+    import threading
+except:
+    import dummy_threading as threading
+
+class TemplateCollection(object):
+    """Represent a collection of :class:`.Template` objects,
+    identifiable via URI.
+
+    A :class:`.TemplateCollection` is linked to the usage of
+    all template tags that address other templates, such
+    as ``<%include>``, ``<%namespace>``, and ``<%inherit>``.
+    The ``file`` attribute of each of those tags refers
+    to a string URI that is passed to that :class:`.Template`
+    object's :class:`.TemplateCollection` for resolution.
+
+    :class:`.TemplateCollection` is an abstract class,
+    with the usual default implementation being :class:`.TemplateLookup`.
+
+     """
+
+    def has_template(self, uri):
+        """Return ``True`` if this :class:`.TemplateLookup` is
+        capable of returning a :class:`.Template` object for the
+        given ``uri``.
+
+        :param uri: String URI of the template to be resolved.
+
+        """
+        try:
+            self.get_template(uri)
+            return True
+        except exceptions.TemplateLookupException:
+            return False
+
+    def get_template(self, uri, relativeto=None):
+        """Return a :class:`.Template` object corresponding to the given
+        ``uri``.
+
+        The default implementation raises
+        :class:`.NotImplementedError`. Implementations should
+        raise :class:`.TemplateLookupException` if the given ``uri``
+        cannot be resolved.
+
+        :param uri: String URI of the template to be resolved.
+        :param relativeto: if present, the given ``uri`` is assumed to
+         be relative to this URI.
+
+        """
+        raise NotImplementedError()
+
+    def filename_to_uri(self, uri, filename):
+        """Convert the given ``filename`` to a URI relative to
+           this :class:`.TemplateCollection`."""
+
+        return uri
+
+    def adjust_uri(self, uri, filename):
+        """Adjust the given ``uri`` based on the calling ``filename``.
+
+        When this method is called from the runtime, the
+        ``filename`` parameter is taken directly to the ``filename``
+        attribute of the calling template. Therefore a custom
+        :class:`.TemplateCollection` subclass can place any string
+        identifier desired in the ``filename`` parameter of the
+        :class:`.Template` objects it constructs and have them come back
+        here.
+
+        """
+        return uri
+
+class TemplateLookup(TemplateCollection):
+    """Represent a collection of templates that locates template source files
+    from the local filesystem.
+
+    The primary argument is the ``directories`` argument, the list of
+    directories to search:
+
+    .. sourcecode:: python
+
+        lookup = TemplateLookup(["/path/to/templates"])
+        some_template = lookup.get_template("/index.html")
+
+    The :class:`.TemplateLookup` can also be given :class:`.Template` objects
+    programatically using :meth:`.put_string` or :meth:`.put_template`:
+
+    .. sourcecode:: python
+
+        lookup = TemplateLookup()
+        lookup.put_string("base.html", '''
+            <html><body>${self.next()}</body></html>
+        ''')
+        lookup.put_string("hello.html", '''
+            <%include file='base.html'/>
+
+            Hello, world !
+        ''')
+
+
+    :param directories: A list of directory names which will be
+     searched for a particular template URI. The URI is appended
+     to each directory and the filesystem checked.
+
+    :param collection_size: Approximate size of the collection used
+     to store templates. If left at its default of ``-1``, the size
+     is unbounded, and a plain Python dictionary is used to
+     relate URI strings to :class:`.Template` instances.
+     Otherwise, a least-recently-used cache object is used which
+     will maintain the size of the collection approximately to
+     the number given.
+
+    :param filesystem_checks: When at its default value of ``True``,
+     each call to :meth:`.TemplateLookup.get_template()` will
+     compare the filesystem last modified time to the time in
+     which an existing :class:`.Template` object was created.
+     This allows the :class:`.TemplateLookup` to regenerate a
+     new :class:`.Template` whenever the original source has
+     been updated. Set this to ``False`` for a very minor
+     performance increase.
+
+    :param modulename_callable: A callable which, when present,
+     is passed the path of the source file as well as the
+     requested URI, and then returns the full path of the
+     generated Python module file. This is used to inject
+     alternate schemes for Python module location. If left at
+     its default of ``None``, the built in system of generation
+     based on ``module_directory`` plus ``uri`` is used.
+
+    All other keyword parameters available for
+    :class:`.Template` are mirrored here. When new
+    :class:`.Template` objects are created, the keywords
+    established with this :class:`.TemplateLookup` are passed on
+    to each new :class:`.Template`.
+
+    """
+
+    def __init__(self,
+                        directories=None,
+                        module_directory=None,
+                        filesystem_checks=True,
+                        collection_size=-1,
+                        format_exceptions=False,
+                        error_handler=None,
+                        disable_unicode=False,
+                        bytestring_passthrough=False,
+                        output_encoding=None,
+                        encoding_errors='strict',
+
+                        cache_args=None,
+                        cache_impl='beaker',
+                        cache_enabled=True,
+                        cache_type=None,
+                        cache_dir=None,
+                        cache_url=None,
+
+                        modulename_callable=None,
+                        module_writer=None,
+                        default_filters=None,
+                        buffer_filters=(),
+                        strict_undefined=False,
+                        imports=None,
+                        future_imports=None,
+                        enable_loop=True,
+                        input_encoding=None,
+                        preprocessor=None,
+                        lexer_cls=None):
+
+        self.directories = [posixpath.normpath(d) for d in
+                            util.to_list(directories, ())
+                            ]
+        self.module_directory = module_directory
+        self.modulename_callable = modulename_callable
+        self.filesystem_checks = filesystem_checks
+        self.collection_size = collection_size
+
+        if cache_args is None:
+            cache_args = {}
+        # transfer deprecated cache_* args
+        if cache_dir:
+            cache_args.setdefault('dir', cache_dir)
+        if cache_url:
+            cache_args.setdefault('url', cache_url)
+        if cache_type:
+            cache_args.setdefault('type', cache_type)
+
+        self.template_args = {
+            'format_exceptions':format_exceptions,
+            'error_handler':error_handler,
+            'disable_unicode':disable_unicode,
+            'bytestring_passthrough':bytestring_passthrough,
+            'output_encoding':output_encoding,
+            'cache_impl':cache_impl,
+            'encoding_errors':encoding_errors,
+            'input_encoding':input_encoding,
+            'module_directory':module_directory,
+            'module_writer':module_writer,
+            'cache_args':cache_args,
+            'cache_enabled':cache_enabled,
+            'default_filters':default_filters,
+            'buffer_filters':buffer_filters,
+            'strict_undefined':strict_undefined,
+            'imports':imports,
+            'future_imports':future_imports,
+            'enable_loop':enable_loop,
+            'preprocessor':preprocessor,
+            'lexer_cls':lexer_cls
+        }
+
+        if collection_size == -1:
+            self._collection = {}
+            self._uri_cache = {}
+        else:
+            self._collection = util.LRUCache(collection_size)
+            self._uri_cache = util.LRUCache(collection_size)
+        self._mutex = threading.Lock()
+
+    def get_template(self, uri):
+        """Return a :class:`.Template` object corresponding to the given
+        ``uri``.
+
+        .. note:: The ``relativeto`` argument is not supported here at the moment.
+
+        """
+
+        try:
+            if self.filesystem_checks:
+                return self._check(uri, self._collection[uri])
+            else:
+                return self._collection[uri]
+        except KeyError:
+            u = re.sub(r'^\/+', '', uri)
+            for dir in self.directories:
+                srcfile = posixpath.normpath(posixpath.join(dir, u))
+                if os.path.isfile(srcfile):
+                    return self._load(srcfile, uri)
+            else:
+                raise exceptions.TopLevelLookupException(
+                                    "Cant locate template for uri %r" % uri)
+
+    def adjust_uri(self, uri, relativeto):
+        """Adjust the given ``uri`` based on the given relative URI."""
+
+        key = (uri, relativeto)
+        if key in self._uri_cache:
+            return self._uri_cache[key]
+
+        if uri[0] != '/':
+            if relativeto is not None:
+                v = self._uri_cache[key] = posixpath.join(
+                                            posixpath.dirname(relativeto), uri)
+            else:
+                v = self._uri_cache[key] = '/' + uri
+        else:
+            v = self._uri_cache[key] = uri
+        return v
+
+
+    def filename_to_uri(self, filename):
+        """Convert the given ``filename`` to a URI relative to
+           this :class:`.TemplateCollection`."""
+
+        try:
+            return self._uri_cache[filename]
+        except KeyError:
+            value = self._relativeize(filename)
+            self._uri_cache[filename] = value
+            return value
+
+    def _relativeize(self, filename):
+        """Return the portion of a filename that is 'relative'
+           to the directories in this lookup.
+
+        """
+
+        filename = posixpath.normpath(filename)
+        for dir in self.directories:
+            if filename[0:len(dir)] == dir:
+                return filename[len(dir):]
+        else:
+            return None
+
+    def _load(self, filename, uri):
+        self._mutex.acquire()
+        try:
+            try:
+                # try returning from collection one
+                # more time in case concurrent thread already loaded
+                return self._collection[uri]
+            except KeyError:
+                pass
+            try:
+                if self.modulename_callable is not None:
+                    module_filename = self.modulename_callable(filename, uri)
+                else:
+                    module_filename = None
+                self._collection[uri] = template = Template(
+                                        uri=uri,
+                                        filename=posixpath.normpath(filename),
+                                        lookup=self,
+                                        module_filename=module_filename,
+                                        **self.template_args)
+                return template
+            except:
+                # if compilation fails etc, ensure
+                # template is removed from collection,
+                # re-raise
+                self._collection.pop(uri, None)
+                raise
+        finally:
+            self._mutex.release()
+
+    def _check(self, uri, template):
+        if template.filename is None:
+            return template
+
+        try:
+            template_stat = os.stat(template.filename)
+            if template.module._modified_time < \
+                        template_stat[stat.ST_MTIME]:
+                self._collection.pop(uri, None)
+                return self._load(template.filename, uri)
+            else:
+                return template
+        except OSError:
+            self._collection.pop(uri, None)
+            raise exceptions.TemplateLookupException(
+                                "Cant locate template for uri %r" % uri)
+
+
+    def put_string(self, uri, text):
+        """Place a new :class:`.Template` object into this
+        :class:`.TemplateLookup`, based on the given string of
+        ``text``.
+
+        """
+        self._collection[uri] = Template(
+                                    text,
+                                    lookup=self,
+                                    uri=uri,
+                                    **self.template_args)
+
+    def put_template(self, uri, template):
+        """Place a new :class:`.Template` object into this
+        :class:`.TemplateLookup`, based on the given
+        :class:`.Template` object.
+
+        """
+        self._collection[uri] = template
+
diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py
new file mode 100644
index 0000000000000000000000000000000000000000..49ec4e0696c245397362529104793e05880ce5d7
--- /dev/null
+++ b/lib/mako/parsetree.py
@@ -0,0 +1,594 @@
+# mako/parsetree.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""defines the parse tree components for Mako templates."""
+
+from mako import exceptions, ast, util, filters, compat
+import re
+
+class Node(object):
+    """base class for a Node in the parse tree."""
+
+    def __init__(self, source, lineno, pos, filename):
+        self.source = source
+        self.lineno = lineno
+        self.pos = pos
+        self.filename = filename
+
+    @property
+    def exception_kwargs(self):
+        return {'source': self.source, 'lineno': self.lineno,
+                'pos': self.pos, 'filename': self.filename}
+
+    def get_children(self):
+        return []
+
+    def accept_visitor(self, visitor):
+        def traverse(node):
+            for n in node.get_children():
+                n.accept_visitor(visitor)
+
+        method = getattr(visitor, "visit" + self.__class__.__name__, traverse)
+        method(self)
+
+class TemplateNode(Node):
+    """a 'container' node that stores the overall collection of nodes."""
+
+    def __init__(self, filename):
+        super(TemplateNode, self).__init__('', 0, 0, filename)
+        self.nodes = []
+        self.page_attributes = {}
+
+    def get_children(self):
+        return self.nodes
+
+    def __repr__(self):
+        return "TemplateNode(%s, %r)" % (
+                    util.sorted_dict_repr(self.page_attributes),
+                    self.nodes)
+
+class ControlLine(Node):
+    """defines a control line, a line-oriented python line or end tag.
+
+    e.g.::
+
+        % if foo:
+            (markup)
+        % endif
+
+    """
+
+    has_loop_context = False
+
+    def __init__(self, keyword, isend, text, **kwargs):
+        super(ControlLine, self).__init__(**kwargs)
+        self.text = text
+        self.keyword = keyword
+        self.isend = isend
+        self.is_primary = keyword in ['for', 'if', 'while', 'try', 'with']
+        self.nodes = []
+        if self.isend:
+            self._declared_identifiers = []
+            self._undeclared_identifiers = []
+        else:
+            code = ast.PythonFragment(text, **self.exception_kwargs)
+            self._declared_identifiers = code.declared_identifiers
+            self._undeclared_identifiers = code.undeclared_identifiers
+
+    def get_children(self):
+        return self.nodes
+
+    def declared_identifiers(self):
+        return self._declared_identifiers
+
+    def undeclared_identifiers(self):
+        return self._undeclared_identifiers
+
+    def is_ternary(self, keyword):
+        """return true if the given keyword is a ternary keyword
+        for this ControlLine"""
+
+        return keyword in {
+            'if':set(['else', 'elif']),
+            'try':set(['except', 'finally']),
+            'for':set(['else'])
+        }.get(self.keyword, [])
+
+    def __repr__(self):
+        return "ControlLine(%r, %r, %r, %r)" % (
+            self.keyword,
+            self.text,
+            self.isend,
+            (self.lineno, self.pos)
+        )
+
+class Text(Node):
+    """defines plain text in the template."""
+
+    def __init__(self, content, **kwargs):
+        super(Text, self).__init__(**kwargs)
+        self.content = content
+
+    def __repr__(self):
+        return "Text(%r, %r)" % (self.content, (self.lineno, self.pos))
+
+class Code(Node):
+    """defines a Python code block, either inline or module level.
+
+    e.g.::
+
+        inline:
+        <%
+            x = 12
+        %>
+
+        module level:
+        <%!
+            import logger
+        %>
+
+    """
+
+    def __init__(self, text, ismodule, **kwargs):
+        super(Code, self).__init__(**kwargs)
+        self.text = text
+        self.ismodule = ismodule
+        self.code = ast.PythonCode(text, **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return self.code.declared_identifiers
+
+    def undeclared_identifiers(self):
+        return self.code.undeclared_identifiers
+
+    def __repr__(self):
+        return "Code(%r, %r, %r)" % (
+            self.text,
+            self.ismodule,
+            (self.lineno, self.pos)
+        )
+
+class Comment(Node):
+    """defines a comment line.
+
+    # this is a comment
+
+    """
+
+    def __init__(self, text, **kwargs):
+        super(Comment, self).__init__(**kwargs)
+        self.text = text
+
+    def __repr__(self):
+        return "Comment(%r, %r)" % (self.text, (self.lineno, self.pos))
+
+class Expression(Node):
+    """defines an inline expression.
+
+    ${x+y}
+
+    """
+
+    def __init__(self, text, escapes, **kwargs):
+        super(Expression, self).__init__(**kwargs)
+        self.text = text
+        self.escapes = escapes
+        self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)
+        self.code = ast.PythonCode(text, **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return []
+
+    def undeclared_identifiers(self):
+        # TODO: make the "filter" shortcut list configurable at parse/gen time
+        return self.code.undeclared_identifiers.union(
+                self.escapes_code.undeclared_identifiers.difference(
+                    set(filters.DEFAULT_ESCAPES.keys())
+                )
+            ).difference(self.code.declared_identifiers)
+
+    def __repr__(self):
+        return "Expression(%r, %r, %r)" % (
+            self.text,
+            self.escapes_code.args,
+            (self.lineno, self.pos)
+        )
+
+class _TagMeta(type):
+    """metaclass to allow Tag to produce a subclass according to
+    its keyword"""
+
+    _classmap = {}
+
+    def __init__(cls, clsname, bases, dict):
+        if getattr(cls, '__keyword__', None) is not None:
+            cls._classmap[cls.__keyword__] = cls
+        super(_TagMeta, cls).__init__(clsname, bases, dict)
+
+    def __call__(cls, keyword, attributes, **kwargs):
+        if ":" in keyword:
+            ns, defname = keyword.split(':')
+            return type.__call__(CallNamespaceTag, ns, defname,
+                                        attributes, **kwargs)
+
+        try:
+            cls = _TagMeta._classmap[keyword]
+        except KeyError:
+            raise exceptions.CompileException(
+                "No such tag: '%s'" % keyword,
+                source=kwargs['source'],
+                lineno=kwargs['lineno'],
+                pos=kwargs['pos'],
+                filename=kwargs['filename']
+            )
+        return type.__call__(cls, keyword, attributes, **kwargs)
+
+class Tag(compat.with_metaclass(_TagMeta, Node)):
+    """abstract base class for tags.
+
+    <%sometag/>
+
+    <%someothertag>
+        stuff
+    </%someothertag>
+
+    """
+    __keyword__ = None
+
+    def __init__(self, keyword, attributes, expressions,
+                        nonexpressions, required, **kwargs):
+        """construct a new Tag instance.
+
+        this constructor not called directly, and is only called
+        by subclasses.
+
+        :param keyword: the tag keyword
+
+        :param attributes: raw dictionary of attribute key/value pairs
+
+        :param expressions: a set of identifiers that are legal attributes,
+         which can also contain embedded expressions
+
+        :param nonexpressions: a set of identifiers that are legal
+         attributes, which cannot contain embedded expressions
+
+        :param \**kwargs:
+         other arguments passed to the Node superclass (lineno, pos)
+
+        """
+        super(Tag, self).__init__(**kwargs)
+        self.keyword = keyword
+        self.attributes = attributes
+        self._parse_attributes(expressions, nonexpressions)
+        missing = [r for r in required if r not in self.parsed_attributes]
+        if len(missing):
+            raise exceptions.CompileException(
+                "Missing attribute(s): %s" %
+                    ",".join([repr(m) for m in missing]),
+                **self.exception_kwargs)
+        self.parent = None
+        self.nodes = []
+
+    def is_root(self):
+        return self.parent is None
+
+    def get_children(self):
+        return self.nodes
+
+    def _parse_attributes(self, expressions, nonexpressions):
+        undeclared_identifiers = set()
+        self.parsed_attributes = {}
+        for key in self.attributes:
+            if key in expressions:
+                expr = []
+                for x in re.compile(r'(\${.+?})',
+                                    re.S).split(self.attributes[key]):
+                    m = re.compile(r'^\${(.+?)}$', re.S).match(x)
+                    if m:
+                        code = ast.PythonCode(m.group(1).rstrip(),
+                                **self.exception_kwargs)
+                        # we aren't discarding "declared_identifiers" here,
+                        # which we do so that list comprehension-declared
+                        # variables aren't counted.   As yet can't find a
+                        # condition that requires it here.
+                        undeclared_identifiers = \
+                            undeclared_identifiers.union(
+                                    code.undeclared_identifiers)
+                        expr.append('(%s)' % m.group(1))
+                    else:
+                        if x:
+                            expr.append(repr(x))
+                self.parsed_attributes[key] = " + ".join(expr) or repr('')
+            elif key in nonexpressions:
+                if re.search(r'\${.+?}', self.attributes[key]):
+                    raise exceptions.CompileException(
+                           "Attibute '%s' in tag '%s' does not allow embedded "
+                           "expressions"  % (key, self.keyword),
+                           **self.exception_kwargs)
+                self.parsed_attributes[key] = repr(self.attributes[key])
+            else:
+                raise exceptions.CompileException(
+                                    "Invalid attribute for tag '%s': '%s'" %
+                                    (self.keyword, key),
+                                    **self.exception_kwargs)
+        self.expression_undeclared_identifiers = undeclared_identifiers
+
+    def declared_identifiers(self):
+        return []
+
+    def undeclared_identifiers(self):
+        return self.expression_undeclared_identifiers
+
+    def __repr__(self):
+        return "%s(%r, %s, %r, %r)" % (self.__class__.__name__,
+                                    self.keyword,
+                                    util.sorted_dict_repr(self.attributes),
+                                    (self.lineno, self.pos),
+                                    self.nodes
+                                )
+
+class IncludeTag(Tag):
+    __keyword__ = 'include'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        super(IncludeTag, self).__init__(
+                                    keyword,
+                                    attributes,
+                                    ('file', 'import', 'args'),
+                                    (), ('file',), **kwargs)
+        self.page_args = ast.PythonCode(
+                                "__DUMMY(%s)" % attributes.get('args', ''),
+                                 **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return []
+
+    def undeclared_identifiers(self):
+        identifiers = self.page_args.undeclared_identifiers.\
+                            difference(set(["__DUMMY"])).\
+                            difference(self.page_args.declared_identifiers)
+        return identifiers.union(super(IncludeTag, self).
+                                    undeclared_identifiers())
+
+class NamespaceTag(Tag):
+    __keyword__ = 'namespace'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        super(NamespaceTag, self).__init__(
+                                        keyword, attributes,
+                                        ('file',),
+                                        ('name','inheritable',
+                                        'import','module'),
+                                        (), **kwargs)
+
+        self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self))))
+        if not 'name' in attributes and not 'import' in attributes:
+            raise exceptions.CompileException(
+                "'name' and/or 'import' attributes are required "
+                "for <%namespace>",
+                **self.exception_kwargs)
+        if 'file' in attributes and 'module' in attributes:
+            raise exceptions.CompileException(
+                "<%namespace> may only have one of 'file' or 'module'",
+                **self.exception_kwargs
+            )
+
+    def declared_identifiers(self):
+        return []
+
+class TextTag(Tag):
+    __keyword__ = 'text'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        super(TextTag, self).__init__(
+                                    keyword,
+                                    attributes, (),
+                                    ('filter'), (), **kwargs)
+        self.filter_args = ast.ArgumentList(
+                                    attributes.get('filter', ''),
+                                    **self.exception_kwargs)
+
+    def undeclared_identifiers(self):
+        return self.filter_args.\
+                            undeclared_identifiers.\
+                            difference(filters.DEFAULT_ESCAPES.keys()).union(
+                        self.expression_undeclared_identifiers
+                    )
+
+class DefTag(Tag):
+    __keyword__ = 'def'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        expressions = ['buffered', 'cached'] + [
+                c for c in attributes if c.startswith('cache_')]
+
+
+        super(DefTag, self).__init__(
+                keyword,
+                attributes,
+                expressions,
+                ('name', 'filter', 'decorator'),
+                ('name',),
+                **kwargs)
+        name = attributes['name']
+        if re.match(r'^[\w_]+$', name):
+            raise exceptions.CompileException(
+                                "Missing parenthesis in %def",
+                                **self.exception_kwargs)
+        self.function_decl = ast.FunctionDecl("def " + name + ":pass",
+                                                    **self.exception_kwargs)
+        self.name = self.function_decl.funcname
+        self.decorator = attributes.get('decorator', '')
+        self.filter_args = ast.ArgumentList(
+                                attributes.get('filter', ''),
+                                **self.exception_kwargs)
+
+    is_anonymous = False
+    is_block = False
+
+    @property
+    def funcname(self):
+        return self.function_decl.funcname
+
+    def get_argument_expressions(self, **kw):
+        return self.function_decl.get_argument_expressions(**kw)
+
+    def declared_identifiers(self):
+        return self.function_decl.allargnames
+
+    def undeclared_identifiers(self):
+        res = []
+        for c in self.function_decl.defaults:
+            res += list(ast.PythonCode(c, **self.exception_kwargs).
+                                    undeclared_identifiers)
+        return set(res).union(
+            self.filter_args.\
+                            undeclared_identifiers.\
+                            difference(filters.DEFAULT_ESCAPES.keys())
+        ).union(
+            self.expression_undeclared_identifiers
+        ).difference(
+            self.function_decl.allargnames
+        )
+
+class BlockTag(Tag):
+    __keyword__ = 'block'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        expressions = ['buffered', 'cached', 'args'] + [
+                 c for c in attributes if c.startswith('cache_')]
+
+        super(BlockTag, self).__init__(
+                keyword,
+                attributes,
+                expressions,
+                ('name','filter', 'decorator'),
+                (),
+                **kwargs)
+        name = attributes.get('name')
+        if name and not re.match(r'^[\w_]+$',name):
+            raise exceptions.CompileException(
+                               "%block may not specify an argument signature",
+                               **self.exception_kwargs)
+        if not name and attributes.get('args', None):
+            raise exceptions.CompileException(
+                                "Only named %blocks may specify args",
+                                **self.exception_kwargs
+                                )
+        self.body_decl = ast.FunctionArgs(attributes.get('args', ''),
+                                            **self.exception_kwargs)
+
+        self.name = name
+        self.decorator = attributes.get('decorator', '')
+        self.filter_args = ast.ArgumentList(
+                                attributes.get('filter', ''),
+                                **self.exception_kwargs)
+
+
+    is_block = True
+
+    @property
+    def is_anonymous(self):
+        return self.name is None
+
+    @property
+    def funcname(self):
+        return self.name or "__M_anon_%d" % (self.lineno, )
+
+    def get_argument_expressions(self, **kw):
+        return self.body_decl.get_argument_expressions(**kw)
+
+    def declared_identifiers(self):
+        return self.body_decl.allargnames
+
+    def undeclared_identifiers(self):
+        return (self.filter_args.\
+                            undeclared_identifiers.\
+                            difference(filters.DEFAULT_ESCAPES.keys())
+                ).union(self.expression_undeclared_identifiers)
+
+
+
+class CallTag(Tag):
+    __keyword__ = 'call'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        super(CallTag, self).__init__(keyword, attributes,
+                                    ('args'), ('expr',), ('expr',), **kwargs)
+        self.expression = attributes['expr']
+        self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
+        self.body_decl = ast.FunctionArgs(attributes.get('args', ''),
+                                            **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return self.code.declared_identifiers.union(self.body_decl.allargnames)
+
+    def undeclared_identifiers(self):
+        return self.code.undeclared_identifiers.\
+                    difference(self.code.declared_identifiers)
+
+class CallNamespaceTag(Tag):
+
+    def __init__(self, namespace, defname, attributes, **kwargs):
+        super(CallNamespaceTag, self).__init__(
+                    namespace + ":" + defname,
+                    attributes,
+                    tuple(attributes.keys()) + ('args', ),
+                    (),
+                    (),
+                    **kwargs)
+
+        self.expression = "%s.%s(%s)" % (
+                                namespace,
+                                defname,
+                                ",".join(["%s=%s" % (k, v) for k, v in
+                                            self.parsed_attributes.items()
+                                            if k != 'args'])
+                            )
+        self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
+        self.body_decl = ast.FunctionArgs(
+                                    attributes.get('args', ''),
+                                    **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return self.code.declared_identifiers.union(self.body_decl.allargnames)
+
+    def undeclared_identifiers(self):
+        return self.code.undeclared_identifiers.\
+                    difference(self.code.declared_identifiers)
+
+class InheritTag(Tag):
+    __keyword__ = 'inherit'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        super(InheritTag, self).__init__(
+                                keyword, attributes,
+                                ('file',), (), ('file',), **kwargs)
+
+class PageTag(Tag):
+    __keyword__ = 'page'
+
+    def __init__(self, keyword, attributes, **kwargs):
+        expressions =   ['cached', 'args', 'expression_filter', 'enable_loop'] + [
+                    c for c in attributes if c.startswith('cache_')]
+
+        super(PageTag, self).__init__(
+                keyword,
+                attributes,
+                expressions,
+                (),
+                (),
+                **kwargs)
+        self.body_decl = ast.FunctionArgs(attributes.get('args', ''),
+                                            **self.exception_kwargs)
+        self.filter_args = ast.ArgumentList(
+                                attributes.get('expression_filter', ''),
+                                **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return self.body_decl.allargnames
+
+
diff --git a/lib/mako/pygen.py b/lib/mako/pygen.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ba5125a4c71f991a212c8c0508000226e86e04d
--- /dev/null
+++ b/lib/mako/pygen.py
@@ -0,0 +1,299 @@
+# mako/pygen.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""utilities for generating and formatting literal Python code."""
+
+import re
+from mako import exceptions
+
+class PythonPrinter(object):
+    def __init__(self, stream):
+        # indentation counter
+        self.indent = 0
+
+        # a stack storing information about why we incremented
+        # the indentation counter, to help us determine if we
+        # should decrement it
+        self.indent_detail = []
+
+        # the string of whitespace multiplied by the indent
+        # counter to produce a line
+        self.indentstring = "    "
+
+        # the stream we are writing to
+        self.stream = stream
+
+        # current line number
+        self.lineno = 1
+
+        # a list of lines that represents a buffered "block" of code,
+        # which can be later printed relative to an indent level
+        self.line_buffer = []
+
+        self.in_indent_lines = False
+
+        self._reset_multi_line_flags()
+
+        # mapping of generated python lines to template
+        # source lines
+        self.source_map = {}
+
+    def _update_lineno(self, num):
+        self.lineno += num
+
+    def start_source(self, lineno):
+        if self.lineno not in self.source_map:
+            self.source_map[self.lineno] = lineno
+
+    def write_blanks(self, num):
+        self.stream.write("\n" * num)
+        self._update_lineno(num)
+
+    def write_indented_block(self, block):
+        """print a line or lines of python which already contain indentation.
+
+        The indentation of the total block of lines will be adjusted to that of
+        the current indent level."""
+        self.in_indent_lines = False
+        for l in re.split(r'\r?\n', block):
+            self.line_buffer.append(l)
+            self._update_lineno(1)
+
+    def writelines(self, *lines):
+        """print a series of lines of python."""
+        for line in lines:
+            self.writeline(line)
+
+    def writeline(self, line):
+        """print a line of python, indenting it according to the current
+        indent level.
+
+        this also adjusts the indentation counter according to the
+        content of the line.
+
+        """
+
+        if not self.in_indent_lines:
+            self._flush_adjusted_lines()
+            self.in_indent_lines = True
+
+        if (line is None or
+            re.match(r"^\s*#",line) or
+            re.match(r"^\s*$", line)
+            ):
+            hastext = False
+        else:
+            hastext = True
+
+        is_comment = line and len(line) and line[0] == '#'
+
+        # see if this line should decrease the indentation level
+        if (not is_comment and
+            (not hastext or self._is_unindentor(line))
+            ):
+
+            if self.indent > 0:
+                self.indent -= 1
+                # if the indent_detail stack is empty, the user
+                # probably put extra closures - the resulting
+                # module wont compile.
+                if len(self.indent_detail) == 0:
+                    raise exceptions.SyntaxException(
+                                    "Too many whitespace closures")
+                self.indent_detail.pop()
+
+        if line is None:
+            return
+
+        # write the line
+        self.stream.write(self._indent_line(line) + "\n")
+        self._update_lineno(len(line.split("\n")))
+
+        # see if this line should increase the indentation level.
+        # note that a line can both decrase (before printing) and
+        # then increase (after printing) the indentation level.
+
+        if re.search(r":[ \t]*(?:#.*)?$", line):
+            # increment indentation count, and also
+            # keep track of what the keyword was that indented us,
+            # if it is a python compound statement keyword
+            # where we might have to look for an "unindent" keyword
+            match = re.match(r"^\s*(if|try|elif|while|for|with)", line)
+            if match:
+                # its a "compound" keyword, so we will check for "unindentors"
+                indentor = match.group(1)
+                self.indent += 1
+                self.indent_detail.append(indentor)
+            else:
+                indentor = None
+                # its not a "compound" keyword.  but lets also
+                # test for valid Python keywords that might be indenting us,
+                # else assume its a non-indenting line
+                m2 = re.match(r"^\s*(def|class|else|elif|except|finally)",
+                              line)
+                if m2:
+                    self.indent += 1
+                    self.indent_detail.append(indentor)
+
+    def close(self):
+        """close this printer, flushing any remaining lines."""
+        self._flush_adjusted_lines()
+
+    def _is_unindentor(self, line):
+        """return true if the given line is an 'unindentor',
+        relative to the last 'indent' event received.
+
+        """
+
+        # no indentation detail has been pushed on; return False
+        if len(self.indent_detail) == 0:
+            return False
+
+        indentor = self.indent_detail[-1]
+
+        # the last indent keyword we grabbed is not a
+        # compound statement keyword; return False
+        if indentor is None:
+            return False
+
+        # if the current line doesnt have one of the "unindentor" keywords,
+        # return False
+        match = re.match(r"^\s*(else|elif|except|finally).*\:", line)
+        if not match:
+            return False
+
+        # whitespace matches up, we have a compound indentor,
+        # and this line has an unindentor, this
+        # is probably good enough
+        return True
+
+        # should we decide that its not good enough, heres
+        # more stuff to check.
+        #keyword = match.group(1)
+
+        # match the original indent keyword
+        #for crit in [
+        #   (r'if|elif', r'else|elif'),
+        #   (r'try', r'except|finally|else'),
+        #   (r'while|for', r'else'),
+        #]:
+        #   if re.match(crit[0], indentor) and re.match(crit[1], keyword):
+        #        return True
+
+        #return False
+
+    def _indent_line(self, line, stripspace=''):
+        """indent the given line according to the current indent level.
+
+        stripspace is a string of space that will be truncated from the
+        start of the line before indenting."""
+
+        return re.sub(r"^%s" % stripspace, self.indentstring
+                      * self.indent, line)
+
+    def _reset_multi_line_flags(self):
+        """reset the flags which would indicate we are in a backslashed
+        or triple-quoted section."""
+
+        self.backslashed, self.triplequoted = False, False
+
+    def _in_multi_line(self, line):
+        """return true if the given line is part of a multi-line block,
+        via backslash or triple-quote."""
+
+        # we are only looking for explicitly joined lines here, not
+        # implicit ones (i.e. brackets, braces etc.).  this is just to
+        # guard against the possibility of modifying the space inside of
+        # a literal multiline string with unfortunately placed
+        # whitespace
+
+        current_state = (self.backslashed or self.triplequoted)
+
+        if re.search(r"\\$", line):
+            self.backslashed = True
+        else:
+            self.backslashed = False
+
+        triples = len(re.findall(r"\"\"\"|\'\'\'", line))
+        if triples == 1 or triples % 2 != 0:
+            self.triplequoted = not self.triplequoted
+
+        return current_state
+
+    def _flush_adjusted_lines(self):
+        stripspace = None
+        self._reset_multi_line_flags()
+
+        for entry in self.line_buffer:
+            if self._in_multi_line(entry):
+                self.stream.write(entry + "\n")
+            else:
+                entry = entry.expandtabs()
+                if stripspace is None and re.search(r"^[ \t]*[^# \t]", entry):
+                    stripspace = re.match(r"^([ \t]*)", entry).group(1)
+                self.stream.write(self._indent_line(entry, stripspace) + "\n")
+
+        self.line_buffer = []
+        self._reset_multi_line_flags()
+
+
+def adjust_whitespace(text):
+    """remove the left-whitespace margin of a block of Python code."""
+
+    state = [False, False]
+    (backslashed, triplequoted) = (0, 1)
+
+    def in_multi_line(line):
+        start_state = (state[backslashed] or state[triplequoted])
+
+        if re.search(r"\\$", line):
+            state[backslashed] = True
+        else:
+            state[backslashed] = False
+
+        def match(reg, t):
+            m = re.match(reg, t)
+            if m:
+                return m, t[len(m.group(0)):]
+            else:
+                return None, t
+
+        while line:
+            if state[triplequoted]:
+                m, line = match(r"%s" % state[triplequoted], line)
+                if m:
+                    state[triplequoted] = False
+                else:
+                    m, line = match(r".*?(?=%s|$)" % state[triplequoted], line)
+            else:
+                m, line = match(r'#', line)
+                if m:
+                    return start_state
+
+                m, line = match(r"\"\"\"|\'\'\'", line)
+                if m:
+                    state[triplequoted] = m.group(0)
+                    continue
+
+                m, line = match(r".*?(?=\"\"\"|\'\'\'|#|$)", line)
+
+        return start_state
+
+    def _indent_line(line, stripspace=''):
+        return re.sub(r"^%s" % stripspace, '', line)
+
+    lines = []
+    stripspace = None
+
+    for line in re.split(r'\r?\n', text):
+        if in_multi_line(line):
+            lines.append(line)
+        else:
+            line = line.expandtabs()
+            if stripspace is None and re.search(r"^[ \t]*[^# \t]", line):
+                stripspace = re.match(r"^([ \t]*)", line).group(1)
+            lines.append(_indent_line(line, stripspace))
+    return "\n".join(lines)
diff --git a/lib/mako/pyparser.py b/lib/mako/pyparser.py
new file mode 100644
index 0000000000000000000000000000000000000000..bfa46a9fafd1afacec803a668eaa1cafbc7890ac
--- /dev/null
+++ b/lib/mako/pyparser.py
@@ -0,0 +1,232 @@
+# mako/pyparser.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Handles parsing of Python code.
+
+Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler
+module is used.
+"""
+
+from mako import exceptions, util, compat
+from mako.compat import arg_stringname
+import operator
+
+if compat.py3k:
+    # words that cannot be assigned to (notably
+    # smaller than the total keys in __builtins__)
+    reserved = set(['True', 'False', 'None', 'print'])
+
+    # the "id" attribute on a function node
+    arg_id = operator.attrgetter('arg')
+else:
+    # words that cannot be assigned to (notably
+    # smaller than the total keys in __builtins__)
+    reserved = set(['True', 'False', 'None'])
+
+    # the "id" attribute on a function node
+    arg_id = operator.attrgetter('id')
+
+import _ast
+util.restore__ast(_ast)
+from mako import _ast_util
+
+
+def parse(code, mode='exec', **exception_kwargs):
+    """Parse an expression into AST"""
+
+    try:
+        return _ast_util.parse(code, '<unknown>', mode)
+    except Exception:
+        raise exceptions.SyntaxException(
+                    "(%s) %s (%r)" % (
+                        compat.exception_as().__class__.__name__,
+                        compat.exception_as(),
+                        code[0:50]
+                    ), **exception_kwargs)
+
+
+class FindIdentifiers(_ast_util.NodeVisitor):
+
+    def __init__(self, listener, **exception_kwargs):
+        self.in_function = False
+        self.in_assign_targets = False
+        self.local_ident_stack = set()
+        self.listener = listener
+        self.exception_kwargs = exception_kwargs
+
+    def _add_declared(self, name):
+        if not self.in_function:
+            self.listener.declared_identifiers.add(name)
+        else:
+            self.local_ident_stack.add(name)
+
+    def visit_ClassDef(self, node):
+        self._add_declared(node.name)
+
+    def visit_Assign(self, node):
+
+        # flip around the visiting of Assign so the expression gets
+        # evaluated first, in the case of a clause like "x=x+5" (x
+        # is undeclared)
+
+        self.visit(node.value)
+        in_a = self.in_assign_targets
+        self.in_assign_targets = True
+        for n in node.targets:
+            self.visit(n)
+        self.in_assign_targets = in_a
+
+    if compat.py3k:
+
+        # ExceptHandler is in Python 2, but this block only works in
+        # Python 3 (and is required there)
+
+        def visit_ExceptHandler(self, node):
+            if node.name is not None:
+                self._add_declared(node.name)
+            if node.type is not None:
+                self.visit(node.type)
+            for statement in node.body:
+                self.visit(statement)
+
+    def visit_Lambda(self, node, *args):
+        self._visit_function(node, True)
+
+    def visit_FunctionDef(self, node):
+        self._add_declared(node.name)
+        self._visit_function(node, False)
+
+    def _expand_tuples(self, args):
+        for arg in args:
+            if isinstance(arg, _ast.Tuple):
+                for n in arg.elts:
+                    yield n
+            else:
+                yield arg
+
+    def _visit_function(self, node, islambda):
+
+        # push function state onto stack.  dont log any more
+        # identifiers as "declared" until outside of the function,
+        # but keep logging identifiers as "undeclared". track
+        # argument names in each function header so they arent
+        # counted as "undeclared"
+
+        inf = self.in_function
+        self.in_function = True
+
+        local_ident_stack = self.local_ident_stack
+        self.local_ident_stack = local_ident_stack.union([
+            arg_id(arg) for arg in self._expand_tuples(node.args.args)
+        ])
+        if islambda:
+            self.visit(node.body)
+        else:
+            for n in node.body:
+                self.visit(n)
+        self.in_function = inf
+        self.local_ident_stack = local_ident_stack
+
+    def visit_For(self, node):
+
+        # flip around visit
+
+        self.visit(node.iter)
+        self.visit(node.target)
+        for statement in node.body:
+            self.visit(statement)
+        for statement in node.orelse:
+            self.visit(statement)
+
+    def visit_Name(self, node):
+        if isinstance(node.ctx, _ast.Store):
+            # this is eqiuvalent to visit_AssName in
+            # compiler
+            self._add_declared(node.id)
+        elif node.id not in reserved and node.id \
+            not in self.listener.declared_identifiers and node.id \
+                not in self.local_ident_stack:
+            self.listener.undeclared_identifiers.add(node.id)
+
+    def visit_Import(self, node):
+        for name in node.names:
+            if name.asname is not None:
+                self._add_declared(name.asname)
+            else:
+                self._add_declared(name.name.split('.')[0])
+
+    def visit_ImportFrom(self, node):
+        for name in node.names:
+            if name.asname is not None:
+                self._add_declared(name.asname)
+            else:
+                if name.name == '*':
+                    raise exceptions.CompileException(
+                        "'import *' is not supported, since all identifier "
+                        "names must be explicitly declared.  Please use the "
+                        "form 'from <modulename> import <name1>, <name2>, "
+                        "...' instead.", **self.exception_kwargs)
+                self._add_declared(name.name)
+
+
+class FindTuple(_ast_util.NodeVisitor):
+
+    def __init__(self, listener, code_factory, **exception_kwargs):
+        self.listener = listener
+        self.exception_kwargs = exception_kwargs
+        self.code_factory = code_factory
+
+    def visit_Tuple(self, node):
+        for n in node.elts:
+            p = self.code_factory(n, **self.exception_kwargs)
+            self.listener.codeargs.append(p)
+            self.listener.args.append(ExpressionGenerator(n).value())
+            self.listener.declared_identifiers = \
+                self.listener.declared_identifiers.union(
+                                                p.declared_identifiers)
+            self.listener.undeclared_identifiers = \
+                self.listener.undeclared_identifiers.union(
+                                                p.undeclared_identifiers)
+
+
+class ParseFunc(_ast_util.NodeVisitor):
+
+    def __init__(self, listener, **exception_kwargs):
+        self.listener = listener
+        self.exception_kwargs = exception_kwargs
+
+    def visit_FunctionDef(self, node):
+        self.listener.funcname = node.name
+
+        argnames = [arg_id(arg) for arg in node.args.args]
+        if node.args.vararg:
+            argnames.append(arg_stringname(node.args.vararg))
+
+        if compat.py2k:
+            # kw-only args don't exist in Python 2
+            kwargnames = []
+        else:
+            kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
+        if node.args.kwarg:
+            kwargnames.append(arg_stringname(node.args.kwarg))
+        self.listener.argnames = argnames
+        self.listener.defaults = node.args.defaults  # ast
+        self.listener.kwargnames = kwargnames
+        if compat.py2k:
+            self.listener.kwdefaults = []
+        else:
+            self.listener.kwdefaults = node.args.kw_defaults
+        self.listener.varargs = node.args.vararg
+        self.listener.kwargs = node.args.kwarg
+
+class ExpressionGenerator(object):
+
+    def __init__(self, astnode):
+        self.generator = _ast_util.SourceGenerator(' ' * 4)
+        self.generator.visit(astnode)
+
+    def value(self):
+        return ''.join(self.generator.result)
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b6a35a921534a56ec6d590f41bfdb175135bce3
--- /dev/null
+++ b/lib/mako/runtime.py
@@ -0,0 +1,878 @@
+# mako/runtime.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides runtime services for templates, including Context,
+Namespace, and various helper functions."""
+
+from mako import exceptions, util, compat
+from mako.compat import compat_builtins
+import sys
+
+
+class Context(object):
+    """Provides runtime namespace, output buffer, and various
+    callstacks for templates.
+
+    See :ref:`runtime_toplevel` for detail on the usage of
+    :class:`.Context`.
+
+     """
+
+    def __init__(self, buffer, **data):
+        self._buffer_stack = [buffer]
+
+        self._data = data
+
+        self._kwargs = data.copy()
+        self._with_template = None
+        self._outputting_as_unicode = None
+        self.namespaces = {}
+
+        # "capture" function which proxies to the
+        # generic "capture" function
+        self._data['capture'] = compat.partial(capture, self)
+
+        # "caller" stack used by def calls with content
+        self.caller_stack = self._data['caller'] = CallerStack()
+
+    def _set_with_template(self, t):
+        self._with_template = t
+        illegal_names = t.reserved_names.intersection(self._data)
+        if illegal_names:
+            raise exceptions.NameConflictError(
+                "Reserved words passed to render(): %s" %
+                ", ".join(illegal_names))
+
+    @property
+    def lookup(self):
+        """Return the :class:`.TemplateLookup` associated
+        with this :class:`.Context`.
+
+        """
+        return self._with_template.lookup
+
+    @property
+    def kwargs(self):
+        """Return the dictionary of top level keyword arguments associated
+        with this :class:`.Context`.
+
+        This dictionary only includes the top-level arguments passed to
+        :meth:`.Template.render`.  It does not include names produced within
+        the template execution such as local variable names or special names
+        such as ``self``, ``next``, etc.
+
+        The purpose of this dictionary is primarily for the case that
+        a :class:`.Template` accepts arguments via its ``<%page>`` tag,
+        which are normally expected to be passed via :meth:`.Template.render`,
+        except the template is being called in an inheritance context,
+        using the ``body()`` method.   :attr:`.Context.kwargs` can then be
+        used to propagate these arguments to the inheriting template::
+
+            ${next.body(**context.kwargs)}
+
+        """
+        return self._kwargs.copy()
+
+    def push_caller(self, caller):
+        """Push a ``caller`` callable onto the callstack for
+        this :class:`.Context`."""
+
+
+        self.caller_stack.append(caller)
+
+    def pop_caller(self):
+        """Pop a ``caller`` callable onto the callstack for this
+        :class:`.Context`."""
+
+        del self.caller_stack[-1]
+
+    def keys(self):
+        """Return a list of all names established in this :class:`.Context`."""
+
+        return list(self._data.keys())
+
+    def __getitem__(self, key):
+        if key in self._data:
+            return self._data[key]
+        else:
+            return compat_builtins.__dict__[key]
+
+    def _push_writer(self):
+        """push a capturing buffer onto this Context and return
+        the new writer function."""
+
+        buf = util.FastEncodingBuffer()
+        self._buffer_stack.append(buf)
+        return buf.write
+
+    def _pop_buffer_and_writer(self):
+        """pop the most recent capturing buffer from this Context
+        and return the current writer after the pop.
+
+        """
+
+        buf = self._buffer_stack.pop()
+        return buf, self._buffer_stack[-1].write
+
+    def _push_buffer(self):
+        """push a capturing buffer onto this Context."""
+
+        self._push_writer()
+
+    def _pop_buffer(self):
+        """pop the most recent capturing buffer from this Context."""
+
+        return self._buffer_stack.pop()
+
+    def get(self, key, default=None):
+        """Return a value from this :class:`.Context`."""
+
+        return self._data.get(key, compat_builtins.__dict__.get(key, default))
+
+    def write(self, string):
+        """Write a string to this :class:`.Context` object's
+        underlying output buffer."""
+
+        self._buffer_stack[-1].write(string)
+
+    def writer(self):
+        """Return the current writer function."""
+
+        return self._buffer_stack[-1].write
+
+    def _copy(self):
+        c = Context.__new__(Context)
+        c._buffer_stack = self._buffer_stack
+        c._data = self._data.copy()
+        c._kwargs = self._kwargs
+        c._with_template = self._with_template
+        c._outputting_as_unicode = self._outputting_as_unicode
+        c.namespaces = self.namespaces
+        c.caller_stack = self.caller_stack
+        return c
+
+    def _locals(self, d):
+        """Create a new :class:`.Context` with a copy of this
+        :class:`.Context`'s current state,
+        updated with the given dictionary.
+
+        The :attr:`.Context.kwargs` collection remains
+        unaffected.
+
+
+        """
+
+        if not d:
+            return self
+        c = self._copy()
+        c._data.update(d)
+        return c
+
+    def _clean_inheritance_tokens(self):
+        """create a new copy of this :class:`.Context`. with
+        tokens related to inheritance state removed."""
+
+        c = self._copy()
+        x = c._data
+        x.pop('self', None)
+        x.pop('parent', None)
+        x.pop('next', None)
+        return c
+
+class CallerStack(list):
+    def __init__(self):
+        self.nextcaller = None
+
+    def __nonzero__(self):
+        return self.__bool__()
+
+    def __bool__(self):
+        return len(self) and self._get_caller() and True or False
+
+    def _get_caller(self):
+        # this method can be removed once
+        # codegen MAGIC_NUMBER moves past 7
+        return self[-1]
+
+    def __getattr__(self, key):
+        return getattr(self._get_caller(), key)
+
+    def _push_frame(self):
+        frame = self.nextcaller or None
+        self.append(frame)
+        self.nextcaller = None
+        return frame
+
+    def _pop_frame(self):
+        self.nextcaller = self.pop()
+
+
+class Undefined(object):
+    """Represents an undefined value in a template.
+
+    All template modules have a constant value
+    ``UNDEFINED`` present which is an instance of this
+    object.
+
+    """
+    def __str__(self):
+        raise NameError("Undefined")
+
+    def __nonzero__(self):
+        return self.__bool__()
+
+    def __bool__(self):
+        return False
+
+UNDEFINED = Undefined()
+
+class LoopStack(object):
+    """a stack for LoopContexts that implements the context manager protocol
+    to automatically pop off the top of the stack on context exit
+    """
+
+    def __init__(self):
+        self.stack = []
+
+    def _enter(self, iterable):
+        self._push(iterable)
+        return self._top
+
+    def _exit(self):
+        self._pop()
+        return self._top
+
+    @property
+    def _top(self):
+        if self.stack:
+            return self.stack[-1]
+        else:
+            return self
+
+    def _pop(self):
+        return self.stack.pop()
+
+    def _push(self, iterable):
+        new = LoopContext(iterable)
+        if self.stack:
+            new.parent = self.stack[-1]
+        return self.stack.append(new)
+
+    def __getattr__(self, key):
+        raise exceptions.RuntimeException("No loop context is established")
+
+    def __iter__(self):
+        return iter(self._top)
+
+
+class LoopContext(object):
+    """A magic loop variable.
+    Automatically accessible in any ``% for`` block.
+
+    See the section :ref:`loop_context` for usage
+    notes.
+
+    :attr:`parent` -> :class:`.LoopContext` or ``None``
+        The parent loop, if one exists.
+    :attr:`index` -> `int`
+        The 0-based iteration count.
+    :attr:`reverse_index` -> `int`
+        The number of iterations remaining.
+    :attr:`first` -> `bool`
+        ``True`` on the first iteration, ``False`` otherwise.
+    :attr:`last` -> `bool`
+        ``True`` on the last iteration, ``False`` otherwise.
+    :attr:`even` -> `bool`
+        ``True`` when ``index`` is even.
+    :attr:`odd` -> `bool`
+        ``True`` when ``index`` is odd.
+    """
+
+    def __init__(self, iterable):
+        self._iterable = iterable
+        self.index = 0
+        self.parent = None
+
+    def __iter__(self):
+        for i in self._iterable:
+            yield i
+            self.index += 1
+
+    @util.memoized_instancemethod
+    def __len__(self):
+        return len(self._iterable)
+
+    @property
+    def reverse_index(self):
+        return len(self) - self.index - 1
+
+    @property
+    def first(self):
+        return self.index == 0
+
+    @property
+    def last(self):
+        return self.index == len(self) - 1
+
+    @property
+    def even(self):
+        return not self.odd
+
+    @property
+    def odd(self):
+        return bool(self.index % 2)
+
+    def cycle(self, *values):
+        """Cycle through values as the loop progresses.
+        """
+        if not values:
+            raise ValueError("You must provide values to cycle through")
+        return values[self.index % len(values)]
+
+
+class _NSAttr(object):
+    def __init__(self, parent):
+        self.__parent = parent
+    def __getattr__(self, key):
+        ns = self.__parent
+        while ns:
+            if hasattr(ns.module, key):
+                return getattr(ns.module, key)
+            else:
+                ns = ns.inherits
+        raise AttributeError(key)
+
+class Namespace(object):
+    """Provides access to collections of rendering methods, which
+      can be local, from other templates, or from imported modules.
+
+      To access a particular rendering method referenced by a
+      :class:`.Namespace`, use plain attribute access:
+
+      .. sourcecode:: mako
+
+        ${some_namespace.foo(x, y, z)}
+
+      :class:`.Namespace` also contains several built-in attributes
+      described here.
+
+      """
+
+    def __init__(self, name, context,
+                            callables=None, inherits=None,
+                            populate_self=True, calling_uri=None):
+        self.name = name
+        self.context = context
+        self.inherits = inherits
+        if callables is not None:
+            self.callables = dict([(c.__name__, c) for c in callables])
+
+    callables = ()
+
+    module = None
+    """The Python module referenced by this :class:`.Namespace`.
+
+    If the namespace references a :class:`.Template`, then
+    this module is the equivalent of ``template.module``,
+    i.e. the generated module for the template.
+
+    """
+
+    template = None
+    """The :class:`.Template` object referenced by this
+        :class:`.Namespace`, if any.
+
+    """
+
+    context = None
+    """The :class:`.Context` object for this :class:`.Namespace`.
+
+    Namespaces are often created with copies of contexts that
+    contain slightly different data, particularly in inheritance
+    scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one
+    can traverse an entire chain of templates that inherit from
+    one-another.
+
+    """
+
+    filename = None
+    """The path of the filesystem file used for this
+    :class:`.Namespace`'s module or template.
+
+    If this is a pure module-based
+    :class:`.Namespace`, this evaluates to ``module.__file__``. If a
+    template-based namespace, it evaluates to the original
+    template file location.
+
+    """
+
+    uri = None
+    """The URI for this :class:`.Namespace`'s template.
+
+    I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.
+
+    This is the equivalent of :attr:`.Template.uri`.
+
+    """
+
+    _templateuri = None
+
+    @util.memoized_property
+    def attr(self):
+        """Access module level attributes by name.
+
+        This accessor allows templates to supply "scalar"
+        attributes which are particularly handy in inheritance
+        relationships.
+
+        .. seealso::
+
+            :ref:`inheritance_attr`
+
+            :ref:`namespace_attr_for_includes`
+
+        """
+        return _NSAttr(self)
+
+    def get_namespace(self, uri):
+        """Return a :class:`.Namespace` corresponding to the given ``uri``.
+
+        If the given ``uri`` is a relative URI (i.e. it does not
+        contain a leading slash ``/``), the ``uri`` is adjusted to
+        be relative to the ``uri`` of the namespace itself. This
+        method is therefore mostly useful off of the built-in
+        ``local`` namespace, described in :ref:`namespace_local`.
+
+        In
+        most cases, a template wouldn't need this function, and
+        should instead use the ``<%namespace>`` tag to load
+        namespaces. However, since all ``<%namespace>`` tags are
+        evaluated before the body of a template ever runs,
+        this method can be used to locate namespaces using
+        expressions that were generated within the body code of
+        the template, or to conditionally use a particular
+        namespace.
+
+        """
+        key = (self, uri)
+        if key in self.context.namespaces:
+            return self.context.namespaces[key]
+        else:
+            ns = TemplateNamespace(uri, self.context._copy(),
+                                templateuri=uri,
+                                calling_uri=self._templateuri)
+            self.context.namespaces[key] = ns
+            return ns
+
+    def get_template(self, uri):
+        """Return a :class:`.Template` from the given ``uri``.
+
+        The ``uri`` resolution is relative to the ``uri`` of this
+        :class:`.Namespace` object's :class:`.Template`.
+
+        """
+        return _lookup_template(self.context, uri, self._templateuri)
+
+    def get_cached(self, key, **kwargs):
+        """Return a value from the :class:`.Cache` referenced by this
+        :class:`.Namespace` object's :class:`.Template`.
+
+        The advantage to this method versus direct access to the
+        :class:`.Cache` is that the configuration parameters
+        declared in ``<%page>`` take effect here, thereby calling
+        up the same configured backend as that configured
+        by ``<%page>``.
+
+        """
+
+        return self.cache.get(key, **kwargs)
+
+    @property
+    def cache(self):
+        """Return the :class:`.Cache` object referenced
+        by this :class:`.Namespace` object's
+        :class:`.Template`.
+
+        """
+        return self.template.cache
+
+    def include_file(self, uri, **kwargs):
+        """Include a file at the given ``uri``."""
+
+        _include_file(self.context, uri, self._templateuri, **kwargs)
+
+    def _populate(self, d, l):
+        for ident in l:
+            if ident == '*':
+                for (k, v) in self._get_star():
+                    d[k] = v
+            else:
+                d[ident] = getattr(self, ident)
+
+    def _get_star(self):
+        if self.callables:
+            for key in self.callables:
+                yield (key, self.callables[key])
+
+    def __getattr__(self, key):
+        if key in self.callables:
+            val = self.callables[key]
+        elif self.inherits:
+            val = getattr(self.inherits, key)
+        else:
+            raise AttributeError(
+                    "Namespace '%s' has no member '%s'" %
+                    (self.name, key))
+        setattr(self, key, val)
+        return val
+
+class TemplateNamespace(Namespace):
+    """A :class:`.Namespace` specific to a :class:`.Template` instance."""
+
+    def __init__(self, name, context, template=None, templateuri=None,
+                            callables=None, inherits=None,
+                            populate_self=True, calling_uri=None):
+        self.name = name
+        self.context = context
+        self.inherits = inherits
+        if callables is not None:
+            self.callables = dict([(c.__name__, c) for c in callables])
+
+        if templateuri is not None:
+            self.template = _lookup_template(context, templateuri,
+                                                calling_uri)
+            self._templateuri = self.template.module._template_uri
+        elif template is not None:
+            self.template = template
+            self._templateuri = template.module._template_uri
+        else:
+            raise TypeError("'template' argument is required.")
+
+        if populate_self:
+            lclcallable, lclcontext = \
+                        _populate_self_namespace(context, self.template,
+                                                    self_ns=self)
+
+    @property
+    def module(self):
+        """The Python module referenced by this :class:`.Namespace`.
+
+        If the namespace references a :class:`.Template`, then
+        this module is the equivalent of ``template.module``,
+        i.e. the generated module for the template.
+
+        """
+        return self.template.module
+
+    @property
+    def filename(self):
+        """The path of the filesystem file used for this
+        :class:`.Namespace`'s module or template.
+        """
+        return self.template.filename
+
+    @property
+    def uri(self):
+        """The URI for this :class:`.Namespace`'s template.
+
+        I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.
+
+        This is the equivalent of :attr:`.Template.uri`.
+
+        """
+        return self.template.uri
+
+    def _get_star(self):
+        if self.callables:
+            for key in self.callables:
+                yield (key, self.callables[key])
+        def get(key):
+            callable_ = self.template._get_def_callable(key)
+            return compat.partial(callable_, self.context)
+        for k in self.template.module._exports:
+            yield (k, get(k))
+
+    def __getattr__(self, key):
+        if key in self.callables:
+            val = self.callables[key]
+        elif self.template.has_def(key):
+            callable_ = self.template._get_def_callable(key)
+            val = compat.partial(callable_, self.context)
+        elif self.inherits:
+            val = getattr(self.inherits, key)
+
+        else:
+            raise AttributeError(
+                    "Namespace '%s' has no member '%s'" %
+                    (self.name, key))
+        setattr(self, key, val)
+        return val
+
+class ModuleNamespace(Namespace):
+    """A :class:`.Namespace` specific to a Python module instance."""
+
+    def __init__(self, name, context, module,
+                            callables=None, inherits=None,
+                            populate_self=True, calling_uri=None):
+        self.name = name
+        self.context = context
+        self.inherits = inherits
+        if callables is not None:
+            self.callables = dict([(c.__name__, c) for c in callables])
+
+        mod = __import__(module)
+        for token in module.split('.')[1:]:
+            mod = getattr(mod, token)
+        self.module = mod
+
+    @property
+    def filename(self):
+        """The path of the filesystem file used for this
+        :class:`.Namespace`'s module or template.
+        """
+        return self.module.__file__
+
+    def _get_star(self):
+        if self.callables:
+            for key in self.callables:
+                yield (key, self.callables[key])
+        for key in dir(self.module):
+            if key[0] != '_':
+                callable_ = getattr(self.module, key)
+                if compat.callable(callable_):
+                    yield key, compat.partial(callable_, self.context)
+
+
+    def __getattr__(self, key):
+        if key in self.callables:
+            val = self.callables[key]
+        elif hasattr(self.module, key):
+            callable_ = getattr(self.module, key)
+            val = compat.partial(callable_, self.context)
+        elif self.inherits:
+            val = getattr(self.inherits, key)
+        else:
+            raise AttributeError(
+                    "Namespace '%s' has no member '%s'" %
+                    (self.name, key))
+        setattr(self, key, val)
+        return val
+
+def supports_caller(func):
+    """Apply a caller_stack compatibility decorator to a plain
+    Python function.
+
+    See the example in :ref:`namespaces_python_modules`.
+
+    """
+
+    def wrap_stackframe(context, *args, **kwargs):
+        context.caller_stack._push_frame()
+        try:
+            return func(context, *args, **kwargs)
+        finally:
+            context.caller_stack._pop_frame()
+    return wrap_stackframe
+
+def capture(context, callable_, *args, **kwargs):
+    """Execute the given template def, capturing the output into
+    a buffer.
+
+    See the example in :ref:`namespaces_python_modules`.
+
+    """
+
+    if not compat.callable(callable_):
+        raise exceptions.RuntimeException(
+                        "capture() function expects a callable as "
+                        "its argument (i.e. capture(func, *args, **kwargs))"
+                        )
+    context._push_buffer()
+    try:
+        callable_(*args, **kwargs)
+    finally:
+        buf = context._pop_buffer()
+    return buf.getvalue()
+
+def _decorate_toplevel(fn):
+    def decorate_render(render_fn):
+        def go(context, *args, **kw):
+            def y(*args, **kw):
+                return render_fn(context, *args, **kw)
+            try:
+                y.__name__ = render_fn.__name__[7:]
+            except TypeError:
+                # < Python 2.4
+                pass
+            return fn(y)(context, *args, **kw)
+        return go
+    return decorate_render
+
+def _decorate_inline(context, fn):
+    def decorate_render(render_fn):
+        dec = fn(render_fn)
+        def go(*args, **kw):
+            return dec(context, *args, **kw)
+        return go
+    return decorate_render
+
+def _include_file(context, uri, calling_uri, **kwargs):
+    """locate the template from the given uri and include it in
+    the current output."""
+
+    template = _lookup_template(context, uri, calling_uri)
+    (callable_, ctx) = _populate_self_namespace(
+                                context._clean_inheritance_tokens(),
+                                template)
+    callable_(ctx, **_kwargs_for_include(callable_, context._data, **kwargs))
+
+def _inherit_from(context, uri, calling_uri):
+    """called by the _inherit method in template modules to set
+    up the inheritance chain at the start of a template's
+    execution."""
+
+    if uri is None:
+        return None
+    template = _lookup_template(context, uri, calling_uri)
+    self_ns = context['self']
+    ih = self_ns
+    while ih.inherits is not None:
+        ih = ih.inherits
+    lclcontext = context._locals({'next': ih})
+    ih.inherits = TemplateNamespace("self:%s" % template.uri,
+                                lclcontext,
+                                template=template,
+                                populate_self=False)
+    context._data['parent'] = lclcontext._data['local'] = ih.inherits
+    callable_ = getattr(template.module, '_mako_inherit', None)
+    if callable_ is not None:
+        ret = callable_(template, lclcontext)
+        if ret:
+            return ret
+
+    gen_ns = getattr(template.module, '_mako_generate_namespaces', None)
+    if gen_ns is not None:
+        gen_ns(context)
+    return (template.callable_, lclcontext)
+
+def _lookup_template(context, uri, relativeto):
+    lookup = context._with_template.lookup
+    if lookup is None:
+        raise exceptions.TemplateLookupException(
+                            "Template '%s' has no TemplateLookup associated" %
+                            context._with_template.uri)
+    uri = lookup.adjust_uri(uri, relativeto)
+    try:
+        return lookup.get_template(uri)
+    except exceptions.TopLevelLookupException:
+        raise exceptions.TemplateLookupException(str(compat.exception_as()))
+
+def _populate_self_namespace(context, template, self_ns=None):
+    if self_ns is None:
+        self_ns = TemplateNamespace('self:%s' % template.uri,
+                                context, template=template,
+                                populate_self=False)
+    context._data['self'] = context._data['local'] = self_ns
+    if hasattr(template.module, '_mako_inherit'):
+        ret = template.module._mako_inherit(template, context)
+        if ret:
+            return ret
+    return (template.callable_, context)
+
+def _render(template, callable_, args, data, as_unicode=False):
+    """create a Context and return the string
+    output of the given template and template callable."""
+
+    if as_unicode:
+        buf = util.FastEncodingBuffer(as_unicode=True)
+    elif template.bytestring_passthrough:
+        buf = compat.StringIO()
+    else:
+        buf = util.FastEncodingBuffer(
+                        as_unicode=as_unicode,
+                        encoding=template.output_encoding,
+                        errors=template.encoding_errors)
+    context = Context(buf, **data)
+    context._outputting_as_unicode = as_unicode
+    context._set_with_template(template)
+
+    _render_context(template, callable_, context, *args,
+                            **_kwargs_for_callable(callable_, data))
+    return context._pop_buffer().getvalue()
+
+def _kwargs_for_callable(callable_, data):
+    argspec = compat.inspect_func_args(callable_)
+    # for normal pages, **pageargs is usually present
+    if argspec[2]:
+        return data
+
+    # for rendering defs from the top level, figure out the args
+    namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
+    kwargs = {}
+    for arg in namedargs:
+        if arg != 'context' and arg in data and arg not in kwargs:
+            kwargs[arg] = data[arg]
+    return kwargs
+
+def _kwargs_for_include(callable_, data, **kwargs):
+    argspec = compat.inspect_func_args(callable_)
+    namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
+    for arg in namedargs:
+        if arg != 'context' and arg in data and arg not in kwargs:
+            kwargs[arg] = data[arg]
+    return kwargs
+
+def _render_context(tmpl, callable_, context, *args, **kwargs):
+    import mako.template as template
+    # create polymorphic 'self' namespace for this
+    # template with possibly updated context
+    if not isinstance(tmpl, template.DefTemplate):
+        # if main render method, call from the base of the inheritance stack
+        (inherit, lclcontext) = _populate_self_namespace(context, tmpl)
+        _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)
+    else:
+        # otherwise, call the actual rendering method specified
+        (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent)
+        _exec_template(callable_, context, args=args, kwargs=kwargs)
+
+def _exec_template(callable_, context, args=None, kwargs=None):
+    """execute a rendering callable given the callable, a
+    Context, and optional explicit arguments
+
+    the contextual Template will be located if it exists, and
+    the error handling options specified on that Template will
+    be interpreted here.
+    """
+    template = context._with_template
+    if template is not None and \
+            (template.format_exceptions or template.error_handler):
+        try:
+            callable_(context, *args, **kwargs)
+        except Exception:
+            _render_error(template, context, compat.exception_as())
+        except:
+            e = sys.exc_info()[0]
+            _render_error(template, context, e)
+    else:
+        callable_(context, *args, **kwargs)
+
+def _render_error(template, context, error):
+    if template.error_handler:
+        result = template.error_handler(context, error)
+        if not result:
+            compat.reraise(*sys.exc_info())
+    else:
+        error_template = exceptions.html_error_template()
+        if context._outputting_as_unicode:
+            context._buffer_stack[:] = [
+                                    util.FastEncodingBuffer(as_unicode=True)]
+        else:
+            context._buffer_stack[:] = [util.FastEncodingBuffer(
+                                            error_template.output_encoding,
+                                            error_template.encoding_errors)]
+
+        context._set_with_template(error_template)
+        error_template.render_context(context, error=error)
diff --git a/lib/mako/template.py b/lib/mako/template.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb6106289fa39c73db569be5af8f684ab1dddc0b
--- /dev/null
+++ b/lib/mako/template.py
@@ -0,0 +1,705 @@
+# mako/template.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Provides the Template class, a facade for parsing, generating and executing
+template strings, as well as template runtime operations."""
+
+from mako.lexer import Lexer
+from mako import runtime, util, exceptions, codegen, cache, compat
+import os
+import re
+import shutil
+import stat
+import sys
+import tempfile
+import types
+import weakref
+
+
+class Template(object):
+    """Represents a compiled template.
+
+    :class:`.Template` includes a reference to the original
+    template source (via the :attr:`.source` attribute)
+    as well as the source code of the
+    generated Python module (i.e. the :attr:`.code` attribute),
+    as well as a reference to an actual Python module.
+
+    :class:`.Template` is constructed using either a literal string
+    representing the template text, or a filename representing a filesystem
+    path to a source file.
+
+    :param text: textual template source.  This argument is mutually
+     exclusive versus the ``filename`` parameter.
+
+    :param filename: filename of the source template.  This argument is
+     mutually exclusive versus the ``text`` parameter.
+
+    :param buffer_filters: string list of filters to be applied
+     to the output of ``%def``\ s which are buffered, cached, or otherwise
+     filtered, after all filters
+     defined with the ``%def`` itself have been applied. Allows the
+     creation of default expression filters that let the output
+     of return-valued ``%def``\ s "opt out" of that filtering via
+     passing special attributes or objects.
+
+    :param bytestring_passthrough: When ``True``, and ``output_encoding`` is
+     set to ``None``, and :meth:`.Template.render` is used to render,
+     the `StringIO` or `cStringIO` buffer will be used instead of the
+     default "fast" buffer.   This allows raw bytestrings in the
+     output stream, such as in expressions, to pass straight
+     through to the buffer.  This flag is forced
+     to ``True`` if ``disable_unicode`` is also configured.
+
+     .. versionadded:: 0.4
+        Added to provide the same behavior as that of the previous series.
+
+    :param cache_args: Dictionary of cache configuration arguments that
+     will be passed to the :class:`.CacheImpl`.   See :ref:`caching_toplevel`.
+
+    :param cache_dir:
+
+     .. deprecated:: 0.6
+        Use the ``'dir'`` argument in the ``cache_args`` dictionary.
+        See :ref:`caching_toplevel`.
+
+    :param cache_enabled: Boolean flag which enables caching of this
+     template.  See :ref:`caching_toplevel`.
+
+    :param cache_impl: String name of a :class:`.CacheImpl` caching
+     implementation to use.   Defaults to ``'beaker'``.
+
+    :param cache_type:
+
+     .. deprecated:: 0.6
+        Use the ``'type'`` argument in the ``cache_args`` dictionary.
+        See :ref:`caching_toplevel`.
+
+    :param cache_url:
+
+     .. deprecated:: 0.6
+        Use the ``'url'`` argument in the ``cache_args`` dictionary.
+        See :ref:`caching_toplevel`.
+
+    :param default_filters: List of string filter names that will
+     be applied to all expressions.  See :ref:`filtering_default_filters`.
+
+    :param disable_unicode: Disables all awareness of Python Unicode
+     objects.  See :ref:`unicode_disabled`.
+
+    :param enable_loop: When ``True``, enable the ``loop`` context variable.
+     This can be set to ``False`` to support templates that may
+     be making usage of the name "``loop``".   Individual templates can
+     re-enable the "loop" context by placing the directive
+     ``enable_loop="True"`` inside the ``<%page>`` tag -- see
+     :ref:`migrating_loop`.
+
+    :param encoding_errors: Error parameter passed to ``encode()`` when
+     string encoding is performed. See :ref:`usage_unicode`.
+
+    :param error_handler: Python callable which is called whenever
+     compile or runtime exceptions occur. The callable is passed
+     the current context as well as the exception. If the
+     callable returns ``True``, the exception is considered to
+     be handled, else it is re-raised after the function
+     completes. Is used to provide custom error-rendering
+     functions.
+
+    :param format_exceptions: if ``True``, exceptions which occur during
+     the render phase of this template will be caught and
+     formatted into an HTML error page, which then becomes the
+     rendered result of the :meth:`.render` call. Otherwise,
+     runtime exceptions are propagated outwards.
+
+    :param imports: String list of Python statements, typically individual
+     "import" lines, which will be placed into the module level
+     preamble of all generated Python modules. See the example
+     in :ref:`filtering_default_filters`.
+
+    :param future_imports: String list of names to import from `__future__`.
+     These will be concatenated into a comma-separated string and inserted
+     into the beginning of the template, e.g. ``futures_imports=['FOO',
+     'BAR']`` results in ``from __future__ import FOO, BAR``.  If you're
+     interested in using features like the new division operator, you must
+     use future_imports to convey that to the renderer, as otherwise the
+     import will not appear as the first executed statement in the generated
+     code and will therefore not have the desired effect.
+
+    :param input_encoding: Encoding of the template's source code.  Can
+     be used in lieu of the coding comment. See
+     :ref:`usage_unicode` as well as :ref:`unicode_toplevel` for
+     details on source encoding.
+
+    :param lookup: a :class:`.TemplateLookup` instance that will be used
+     for all file lookups via the ``<%namespace>``,
+     ``<%include>``, and ``<%inherit>`` tags. See
+     :ref:`usage_templatelookup`.
+
+    :param module_directory: Filesystem location where generated
+     Python module files will be placed.
+
+    :param module_filename: Overrides the filename of the generated
+     Python module file. For advanced usage only.
+
+    :param module_writer: A callable which overrides how the Python
+     module is written entirely.  The callable is passed the
+     encoded source content of the module and the destination
+     path to be written to.   The default behavior of module writing
+     uses a tempfile in conjunction with a file move in order
+     to make the operation atomic.   So a user-defined module
+     writing function that mimics the default behavior would be:
+
+     .. sourcecode:: python
+
+         import tempfile
+         import os
+         import shutil
+
+         def module_writer(source, outputpath):
+             (dest, name) = \\
+                 tempfile.mkstemp(
+                     dir=os.path.dirname(outputpath)
+                 )
+
+             os.write(dest, source)
+             os.close(dest)
+             shutil.move(name, outputpath)
+
+         from mako.template import Template
+         mytemplate = Template(
+                         filename="index.html",
+                         module_directory="/path/to/modules",
+                         module_writer=module_writer
+                     )
+
+     The function is provided for unusual configurations where
+     certain platform-specific permissions or other special
+     steps are needed.
+
+    :param output_encoding: The encoding to use when :meth:`.render`
+     is called.
+     See :ref:`usage_unicode` as well as :ref:`unicode_toplevel`.
+
+    :param preprocessor: Python callable which will be passed
+     the full template source before it is parsed. The return
+     result of the callable will be used as the template source
+     code.
+
+    :param lexer_cls: A :class:`.Lexer` class used to parse
+     the template.   The :class:`.Lexer` class is used by
+     default.
+
+     .. versionadded:: 0.7.4
+
+    :param strict_undefined: Replaces the automatic usage of
+     ``UNDEFINED`` for any undeclared variables not located in
+     the :class:`.Context` with an immediate raise of
+     ``NameError``. The advantage is immediate reporting of
+     missing variables which include the name.
+
+     .. versionadded:: 0.3.6
+
+    :param uri: string URI or other identifier for this template.
+     If not provided, the ``uri`` is generated from the filesystem
+     path, or from the in-memory identity of a non-file-based
+     template. The primary usage of the ``uri`` is to provide a key
+     within :class:`.TemplateLookup`, as well as to generate the
+     file path of the generated Python module file, if
+     ``module_directory`` is specified.
+
+    """
+
+    lexer_cls = Lexer
+
+    def __init__(self,
+                    text=None,
+                    filename=None,
+                    uri=None,
+                    format_exceptions=False,
+                    error_handler=None,
+                    lookup=None,
+                    output_encoding=None,
+                    encoding_errors='strict',
+                    module_directory=None,
+                    cache_args=None,
+                    cache_impl='beaker',
+                    cache_enabled=True,
+                    cache_type=None,
+                    cache_dir=None,
+                    cache_url=None,
+                    module_filename=None,
+                    input_encoding=None,
+                    disable_unicode=False,
+                    module_writer=None,
+                    bytestring_passthrough=False,
+                    default_filters=None,
+                    buffer_filters=(),
+                    strict_undefined=False,
+                    imports=None,
+                    future_imports=None,
+                    enable_loop=True,
+                    preprocessor=None,
+                    lexer_cls=None):
+        if uri:
+            self.module_id = re.sub(r'\W', "_", uri)
+            self.uri = uri
+        elif filename:
+            self.module_id = re.sub(r'\W', "_", filename)
+            drive, path = os.path.splitdrive(filename)
+            path = os.path.normpath(path).replace(os.path.sep, "/")
+            self.uri = path
+        else:
+            self.module_id = "memory:" + hex(id(self))
+            self.uri = self.module_id
+
+        u_norm = self.uri
+        if u_norm.startswith("/"):
+            u_norm = u_norm[1:]
+        u_norm = os.path.normpath(u_norm)
+        if u_norm.startswith(".."):
+            raise exceptions.TemplateLookupException(
+                    "Template uri \"%s\" is invalid - "
+                    "it cannot be relative outside "
+                    "of the root path." % self.uri)
+
+        self.input_encoding = input_encoding
+        self.output_encoding = output_encoding
+        self.encoding_errors = encoding_errors
+        self.disable_unicode = disable_unicode
+        self.bytestring_passthrough = bytestring_passthrough or disable_unicode
+        self.enable_loop = enable_loop
+        self.strict_undefined = strict_undefined
+        self.module_writer = module_writer
+
+        if compat.py3k and disable_unicode:
+            raise exceptions.UnsupportedError(
+                                    "Mako for Python 3 does not "
+                                    "support disabling Unicode")
+        elif output_encoding and disable_unicode:
+            raise exceptions.UnsupportedError(
+                                    "output_encoding must be set to "
+                                    "None when disable_unicode is used.")
+        if default_filters is None:
+            if compat.py3k or self.disable_unicode:
+                self.default_filters = ['str']
+            else:
+                self.default_filters = ['unicode']
+        else:
+            self.default_filters = default_filters
+        self.buffer_filters = buffer_filters
+
+        self.imports = imports
+        self.future_imports = future_imports
+        self.preprocessor = preprocessor
+
+        if lexer_cls is not None:
+            self.lexer_cls = lexer_cls
+
+        # if plain text, compile code in memory only
+        if text is not None:
+            (code, module) = _compile_text(self, text, filename)
+            self._code = code
+            self._source = text
+            ModuleInfo(module, None, self, filename, code, text)
+        elif filename is not None:
+            # if template filename and a module directory, load
+            # a filesystem-based module file, generating if needed
+            if module_filename is not None:
+                path = module_filename
+            elif module_directory is not None:
+                path = os.path.abspath(
+                        os.path.join(
+                            os.path.normpath(module_directory),
+                            u_norm + ".py"
+                            )
+                        )
+            else:
+                path = None
+            module = self._compile_from_file(path, filename)
+        else:
+            raise exceptions.RuntimeException(
+                                "Template requires text or filename")
+
+        self.module = module
+        self.filename = filename
+        self.callable_ = self.module.render_body
+        self.format_exceptions = format_exceptions
+        self.error_handler = error_handler
+        self.lookup = lookup
+
+        self.module_directory = module_directory
+
+        self._setup_cache_args(
+            cache_impl, cache_enabled, cache_args,
+            cache_type, cache_dir, cache_url
+        )
+
+
+    @util.memoized_property
+    def reserved_names(self):
+        if self.enable_loop:
+            return codegen.RESERVED_NAMES
+        else:
+            return codegen.RESERVED_NAMES.difference(['loop'])
+
+    def _setup_cache_args(self,
+                cache_impl, cache_enabled, cache_args,
+                cache_type, cache_dir, cache_url):
+        self.cache_impl = cache_impl
+        self.cache_enabled = cache_enabled
+        if cache_args:
+            self.cache_args = cache_args
+        else:
+            self.cache_args = {}
+
+        # transfer deprecated cache_* args
+        if cache_type:
+            self.cache_args['type'] = cache_type
+        if cache_dir:
+            self.cache_args['dir'] = cache_dir
+        if cache_url:
+            self.cache_args['url'] = cache_url
+
+    def _compile_from_file(self, path, filename):
+        if path is not None:
+            util.verify_directory(os.path.dirname(path))
+            filemtime = os.stat(filename)[stat.ST_MTIME]
+            if not os.path.exists(path) or \
+                        os.stat(path)[stat.ST_MTIME] < filemtime:
+                data = util.read_file(filename)
+                _compile_module_file(
+                            self,
+                            data,
+                            filename,
+                            path,
+                            self.module_writer)
+            module = compat.load_module(self.module_id, path)
+            del sys.modules[self.module_id]
+            if module._magic_number != codegen.MAGIC_NUMBER:
+                data = util.read_file(filename)
+                _compile_module_file(
+                            self,
+                            data,
+                            filename,
+                            path,
+                            self.module_writer)
+                module = compat.load_module(self.module_id, path)
+                del sys.modules[self.module_id]
+            ModuleInfo(module, path, self, filename, None, None)
+        else:
+            # template filename and no module directory, compile code
+            # in memory
+            data = util.read_file(filename)
+            code, module = _compile_text(
+                                self,
+                                data,
+                                filename)
+            self._source = None
+            self._code = code
+            ModuleInfo(module, None, self, filename, code, None)
+        return module
+
+    @property
+    def source(self):
+        """Return the template source code for this :class:`.Template`."""
+
+        return _get_module_info_from_callable(self.callable_).source
+
+    @property
+    def code(self):
+        """Return the module source code for this :class:`.Template`."""
+
+        return _get_module_info_from_callable(self.callable_).code
+
+    @util.memoized_property
+    def cache(self):
+        return cache.Cache(self)
+
+    @property
+    def cache_dir(self):
+        return self.cache_args['dir']
+    @property
+    def cache_url(self):
+        return self.cache_args['url']
+    @property
+    def cache_type(self):
+        return self.cache_args['type']
+
+    def render(self, *args, **data):
+        """Render the output of this template as a string.
+
+        If the template specifies an output encoding, the string
+        will be encoded accordingly, else the output is raw (raw
+        output uses `cStringIO` and can't handle multibyte
+        characters). A :class:`.Context` object is created corresponding
+        to the given data. Arguments that are explicitly declared
+        by this template's internal rendering method are also
+        pulled from the given ``*args``, ``**data`` members.
+
+        """
+        return runtime._render(self, self.callable_, args, data)
+
+    def render_unicode(self, *args, **data):
+        """Render the output of this template as a unicode object."""
+
+        return runtime._render(self,
+                                self.callable_,
+                                args,
+                                data,
+                                as_unicode=True)
+
+    def render_context(self, context, *args, **kwargs):
+        """Render this :class:`.Template` with the given context.
+
+        The data is written to the context's buffer.
+
+        """
+        if getattr(context, '_with_template', None) is None:
+            context._set_with_template(self)
+        runtime._render_context(self,
+                                self.callable_,
+                                context,
+                                *args,
+                                **kwargs)
+
+    def has_def(self, name):
+        return hasattr(self.module, "render_%s" % name)
+
+    def get_def(self, name):
+        """Return a def of this template as a :class:`.DefTemplate`."""
+
+        return DefTemplate(self, getattr(self.module, "render_%s" % name))
+
+    def _get_def_callable(self, name):
+        return getattr(self.module, "render_%s" % name)
+
+    @property
+    def last_modified(self):
+        return self.module._modified_time
+
+class ModuleTemplate(Template):
+    """A Template which is constructed given an existing Python module.
+
+        e.g.::
+
+        t = Template("this is a template")
+        f = file("mymodule.py", "w")
+        f.write(t.code)
+        f.close()
+
+        import mymodule
+
+        t = ModuleTemplate(mymodule)
+        print t.render()
+
+    """
+
+    def __init__(self, module,
+                        module_filename=None,
+                        template=None,
+                        template_filename=None,
+                        module_source=None,
+                        template_source=None,
+                        output_encoding=None,
+                        encoding_errors='strict',
+                        disable_unicode=False,
+                        bytestring_passthrough=False,
+                        format_exceptions=False,
+                        error_handler=None,
+                        lookup=None,
+                        cache_args=None,
+                        cache_impl='beaker',
+                        cache_enabled=True,
+                        cache_type=None,
+                        cache_dir=None,
+                        cache_url=None,
+    ):
+        self.module_id = re.sub(r'\W', "_", module._template_uri)
+        self.uri = module._template_uri
+        self.input_encoding = module._source_encoding
+        self.output_encoding = output_encoding
+        self.encoding_errors = encoding_errors
+        self.disable_unicode = disable_unicode
+        self.bytestring_passthrough = bytestring_passthrough or disable_unicode
+        self.enable_loop = module._enable_loop
+
+        if compat.py3k and disable_unicode:
+            raise exceptions.UnsupportedError(
+                                    "Mako for Python 3 does not "
+                                    "support disabling Unicode")
+        elif output_encoding and disable_unicode:
+            raise exceptions.UnsupportedError(
+                                    "output_encoding must be set to "
+                                    "None when disable_unicode is used.")
+
+        self.module = module
+        self.filename = template_filename
+        ModuleInfo(module,
+                        module_filename,
+                        self,
+                        template_filename,
+                        module_source,
+                        template_source)
+
+        self.callable_ = self.module.render_body
+        self.format_exceptions = format_exceptions
+        self.error_handler = error_handler
+        self.lookup = lookup
+        self._setup_cache_args(
+            cache_impl, cache_enabled, cache_args,
+            cache_type, cache_dir, cache_url
+        )
+
+class DefTemplate(Template):
+    """A :class:`.Template` which represents a callable def in a parent
+    template."""
+
+    def __init__(self, parent, callable_):
+        self.parent = parent
+        self.callable_ = callable_
+        self.output_encoding = parent.output_encoding
+        self.module = parent.module
+        self.encoding_errors = parent.encoding_errors
+        self.format_exceptions = parent.format_exceptions
+        self.error_handler = parent.error_handler
+        self.enable_loop = parent.enable_loop
+        self.lookup = parent.lookup
+        self.bytestring_passthrough = parent.bytestring_passthrough
+
+    def get_def(self, name):
+        return self.parent.get_def(name)
+
+class ModuleInfo(object):
+    """Stores information about a module currently loaded into
+    memory, provides reverse lookups of template source, module
+    source code based on a module's identifier.
+
+     """
+    _modules = weakref.WeakValueDictionary()
+
+    def __init__(self,
+                    module,
+                    module_filename,
+                    template,
+                    template_filename,
+                    module_source,
+                    template_source):
+        self.module = module
+        self.module_filename = module_filename
+        self.template_filename = template_filename
+        self.module_source = module_source
+        self.template_source = template_source
+        self._modules[module.__name__] = template._mmarker = self
+        if module_filename:
+            self._modules[module_filename] = self
+
+    @classmethod
+    def get_module_source_metadata(cls, module_source, full_line_map=False):
+        source_map = re.search(
+                        r"__M_BEGIN_METADATA(.+?)__M_END_METADATA",
+                        module_source, re.S).group(1)
+        source_map = compat.json.loads(source_map)
+        source_map['line_map'] = dict((int(k), int(v))
+                                    for k, v in source_map['line_map'].items())
+        if full_line_map:
+            f_line_map = source_map['full_line_map'] = []
+            line_map = source_map['line_map']
+
+            curr_templ_line = 1
+            for mod_line in range(1, max(line_map)):
+                if mod_line in line_map:
+                    curr_templ_line = line_map[mod_line]
+                f_line_map.append(curr_templ_line)
+        return source_map
+
+    @property
+    def code(self):
+        if self.module_source is not None:
+            return self.module_source
+        else:
+            return util.read_python_file(self.module_filename)
+
+    @property
+    def source(self):
+        if self.template_source is not None:
+            if self.module._source_encoding and \
+                    not isinstance(self.template_source, compat.text_type):
+                return self.template_source.decode(
+                                self.module._source_encoding)
+            else:
+                return self.template_source
+        else:
+            data = util.read_file(self.template_filename)
+            if self.module._source_encoding:
+                return data.decode(self.module._source_encoding)
+            else:
+                return data
+
+def _compile(template, text, filename, generate_magic_comment):
+    lexer = template.lexer_cls(text,
+                           filename,
+                           disable_unicode=template.disable_unicode,
+                           input_encoding=template.input_encoding,
+                           preprocessor=template.preprocessor)
+    node = lexer.parse()
+    source = codegen.compile(node,
+                            template.uri,
+                            filename,
+                            default_filters=template.default_filters,
+                            buffer_filters=template.buffer_filters,
+                            imports=template.imports,
+                            future_imports=template.future_imports,
+                            source_encoding=lexer.encoding,
+                            generate_magic_comment=generate_magic_comment,
+                            disable_unicode=template.disable_unicode,
+                            strict_undefined=template.strict_undefined,
+                            enable_loop=template.enable_loop,
+                            reserved_names=template.reserved_names)
+    return source, lexer
+
+def _compile_text(template, text, filename):
+    identifier = template.module_id
+    source, lexer = _compile(template, text, filename,
+                        generate_magic_comment=template.disable_unicode)
+
+    cid = identifier
+    if not compat.py3k and isinstance(cid, compat.text_type):
+        cid = cid.encode()
+    module = types.ModuleType(cid)
+    code = compile(source, cid, 'exec')
+
+    # this exec() works for 2.4->3.3.
+    exec(code, module.__dict__, module.__dict__)
+    return (source, module)
+
+def _compile_module_file(template, text, filename, outputpath, module_writer):
+    source, lexer = _compile(template, text, filename,
+                        generate_magic_comment=True)
+
+    if isinstance(source, compat.text_type):
+        source = source.encode(lexer.encoding or 'ascii')
+
+    if module_writer:
+        module_writer(source, outputpath)
+    else:
+        # make tempfiles in the same location as the ultimate
+        # location.   this ensures they're on the same filesystem,
+        # avoiding synchronization issues.
+        (dest, name) = tempfile.mkstemp(dir=os.path.dirname(outputpath))
+
+        os.write(dest, source)
+        os.close(dest)
+        shutil.move(name, outputpath)
+
+def _get_module_info_from_callable(callable_):
+    if compat.py3k:
+        return _get_module_info(callable_.__globals__['__name__'])
+    else:
+        return _get_module_info(callable_.func_globals['__name__'])
+
+def _get_module_info(filename):
+    return ModuleInfo._modules[filename]
+
diff --git a/lib/mako/util.py b/lib/mako/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..cba2ab7920c74d3b75802d4584b9e84a8ec1431d
--- /dev/null
+++ b/lib/mako/util.py
@@ -0,0 +1,360 @@
+# mako/util.py
+# Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import re
+import collections
+import codecs
+import os
+from mako import compat
+import operator
+
+def update_wrapper(decorated, fn):
+    decorated.__wrapped__ = fn
+    decorated.__name__ = fn.__name__
+    return decorated
+
+
+class PluginLoader(object):
+    def __init__(self, group):
+        self.group = group
+        self.impls = {}
+
+    def load(self, name):
+        if name in self.impls:
+            return self.impls[name]()
+        else:
+            import pkg_resources
+            for impl in pkg_resources.iter_entry_points(
+                                self.group,
+                                name):
+                self.impls[name] = impl.load
+                return impl.load()
+            else:
+                from mako import exceptions
+                raise exceptions.RuntimeException(
+                        "Can't load plugin %s %s" %
+                        (self.group, name))
+
+    def register(self, name, modulepath, objname):
+        def load():
+            mod = __import__(modulepath)
+            for token in modulepath.split(".")[1:]:
+                mod = getattr(mod, token)
+            return getattr(mod, objname)
+        self.impls[name] = load
+
+def verify_directory(dir):
+    """create and/or verify a filesystem directory."""
+
+    tries = 0
+
+    while not os.path.exists(dir):
+        try:
+            tries += 1
+            os.makedirs(dir, compat.octal("0775"))
+        except:
+            if tries > 5:
+                raise
+
+def to_list(x, default=None):
+    if x is None:
+        return default
+    if not isinstance(x, (list, tuple)):
+        return [x]
+    else:
+        return x
+
+
+class memoized_property(object):
+    """A read-only @property that is only evaluated once."""
+    def __init__(self, fget, doc=None):
+        self.fget = fget
+        self.__doc__ = doc or fget.__doc__
+        self.__name__ = fget.__name__
+
+    def __get__(self, obj, cls):
+        if obj is None:
+            return self
+        obj.__dict__[self.__name__] = result = self.fget(obj)
+        return result
+
+class memoized_instancemethod(object):
+    """Decorate a method memoize its return value.
+
+    Best applied to no-arg methods: memoization is not sensitive to
+    argument values, and will always return the same value even when
+    called with different arguments.
+
+    """
+    def __init__(self, fget, doc=None):
+        self.fget = fget
+        self.__doc__ = doc or fget.__doc__
+        self.__name__ = fget.__name__
+
+    def __get__(self, obj, cls):
+        if obj is None:
+            return self
+        def oneshot(*args, **kw):
+            result = self.fget(obj, *args, **kw)
+            memo = lambda *a, **kw: result
+            memo.__name__ = self.__name__
+            memo.__doc__ = self.__doc__
+            obj.__dict__[self.__name__] = memo
+            return result
+        oneshot.__name__ = self.__name__
+        oneshot.__doc__ = self.__doc__
+        return oneshot
+
+class SetLikeDict(dict):
+    """a dictionary that has some setlike methods on it"""
+    def union(self, other):
+        """produce a 'union' of this dict and another (at the key level).
+
+        values in the second dict take precedence over that of the first"""
+        x = SetLikeDict(**self)
+        x.update(other)
+        return x
+
+class FastEncodingBuffer(object):
+    """a very rudimentary buffer that is faster than StringIO,
+    but doesn't crash on unicode data like cStringIO."""
+
+    def __init__(self, encoding=None, errors='strict', as_unicode=False):
+        self.data = collections.deque()
+        self.encoding = encoding
+        if as_unicode:
+            self.delim = compat.u('')
+        else:
+            self.delim = ''
+        self.as_unicode = as_unicode
+        self.errors = errors
+        self.write = self.data.append
+
+    def truncate(self):
+        self.data = collections.deque()
+        self.write = self.data.append
+
+    def getvalue(self):
+        if self.encoding:
+            return self.delim.join(self.data).encode(self.encoding,
+                                                     self.errors)
+        else:
+            return self.delim.join(self.data)
+
+class LRUCache(dict):
+    """A dictionary-like object that stores a limited number of items,
+    discarding lesser used items periodically.
+
+    this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based
+    paradigm so that synchronization is not really needed.  the size management
+    is inexact.
+    """
+
+    class _Item(object):
+        def __init__(self, key, value):
+            self.key = key
+            self.value = value
+            self.timestamp = compat.time_func()
+        def __repr__(self):
+            return repr(self.value)
+
+    def __init__(self, capacity, threshold=.5):
+        self.capacity = capacity
+        self.threshold = threshold
+
+    def __getitem__(self, key):
+        item = dict.__getitem__(self, key)
+        item.timestamp = compat.time_func()
+        return item.value
+
+    def values(self):
+        return [i.value for i in dict.values(self)]
+
+    def setdefault(self, key, value):
+        if key in self:
+            return self[key]
+        else:
+            self[key] = value
+            return value
+
+    def __setitem__(self, key, value):
+        item = dict.get(self, key)
+        if item is None:
+            item = self._Item(key, value)
+            dict.__setitem__(self, key, item)
+        else:
+            item.value = value
+        self._manage_size()
+
+    def _manage_size(self):
+        while len(self) > self.capacity + self.capacity * self.threshold:
+            bytime = sorted(dict.values(self),
+                            key=operator.attrgetter('timestamp'), reverse=True)
+            for item in bytime[self.capacity:]:
+                try:
+                    del self[item.key]
+                except KeyError:
+                    # if we couldn't find a key, most likely some other thread
+                    # broke in on us. loop around and try again
+                    break
+
+# Regexp to match python magic encoding line
+_PYTHON_MAGIC_COMMENT_re = re.compile(
+    r'[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)',
+    re.VERBOSE)
+
+def parse_encoding(fp):
+    """Deduce the encoding of a Python source file (binary mode) from magic
+    comment.
+
+    It does this in the same way as the `Python interpreter`__
+
+    .. __: http://docs.python.org/ref/encodings.html
+
+    The ``fp`` argument should be a seekable file object in binary mode.
+    """
+    pos = fp.tell()
+    fp.seek(0)
+    try:
+        line1 = fp.readline()
+        has_bom = line1.startswith(codecs.BOM_UTF8)
+        if has_bom:
+            line1 = line1[len(codecs.BOM_UTF8):]
+
+        m = _PYTHON_MAGIC_COMMENT_re.match(line1.decode('ascii', 'ignore'))
+        if not m:
+            try:
+                import parser
+                parser.suite(line1.decode('ascii', 'ignore'))
+            except (ImportError, SyntaxError):
+                # Either it's a real syntax error, in which case the source
+                # is not valid python source, or line2 is a continuation of
+                # line1, in which case we don't want to scan line2 for a magic
+                # comment.
+                pass
+            else:
+                line2 = fp.readline()
+                m = _PYTHON_MAGIC_COMMENT_re.match(
+                                               line2.decode('ascii', 'ignore'))
+
+        if has_bom:
+            if m:
+                raise SyntaxError("python refuses to compile code with both a UTF8" \
+                      " byte-order-mark and a magic encoding comment")
+            return 'utf_8'
+        elif m:
+            return m.group(1)
+        else:
+            return None
+    finally:
+        fp.seek(pos)
+
+def sorted_dict_repr(d):
+    """repr() a dictionary with the keys in order.
+
+    Used by the lexer unit test to compare parse trees based on strings.
+
+    """
+    keys = list(d.keys())
+    keys.sort()
+    return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
+
+def restore__ast(_ast):
+    """Attempt to restore the required classes to the _ast module if it
+    appears to be missing them
+    """
+    if hasattr(_ast, 'AST'):
+        return
+    _ast.PyCF_ONLY_AST = 2 << 9
+    m = compile("""\
+def foo(): pass
+class Bar(object): pass
+if False: pass
+baz = 'mako'
+1 + 2 - 3 * 4 / 5
+6 // 7 % 8 << 9 >> 10
+11 & 12 ^ 13 | 14
+15 and 16 or 17
+-baz + (not +18) - ~17
+baz and 'foo' or 'bar'
+(mako is baz == baz) is not baz != mako
+mako > baz < mako >= baz <= mako
+mako in baz not in mako""", '<unknown>', 'exec', _ast.PyCF_ONLY_AST)
+    _ast.Module = type(m)
+
+    for cls in _ast.Module.__mro__:
+        if cls.__name__ == 'mod':
+            _ast.mod = cls
+        elif cls.__name__ == 'AST':
+            _ast.AST = cls
+
+    _ast.FunctionDef = type(m.body[0])
+    _ast.ClassDef = type(m.body[1])
+    _ast.If = type(m.body[2])
+
+    _ast.Name = type(m.body[3].targets[0])
+    _ast.Store = type(m.body[3].targets[0].ctx)
+    _ast.Str = type(m.body[3].value)
+
+    _ast.Sub = type(m.body[4].value.op)
+    _ast.Add = type(m.body[4].value.left.op)
+    _ast.Div = type(m.body[4].value.right.op)
+    _ast.Mult = type(m.body[4].value.right.left.op)
+
+    _ast.RShift = type(m.body[5].value.op)
+    _ast.LShift = type(m.body[5].value.left.op)
+    _ast.Mod = type(m.body[5].value.left.left.op)
+    _ast.FloorDiv = type(m.body[5].value.left.left.left.op)
+
+    _ast.BitOr = type(m.body[6].value.op)
+    _ast.BitXor = type(m.body[6].value.left.op)
+    _ast.BitAnd = type(m.body[6].value.left.left.op)
+
+    _ast.Or = type(m.body[7].value.op)
+    _ast.And = type(m.body[7].value.values[0].op)
+
+    _ast.Invert = type(m.body[8].value.right.op)
+    _ast.Not = type(m.body[8].value.left.right.op)
+    _ast.UAdd = type(m.body[8].value.left.right.operand.op)
+    _ast.USub = type(m.body[8].value.left.left.op)
+
+    _ast.Or = type(m.body[9].value.op)
+    _ast.And = type(m.body[9].value.values[0].op)
+
+    _ast.IsNot = type(m.body[10].value.ops[0])
+    _ast.NotEq = type(m.body[10].value.ops[1])
+    _ast.Is = type(m.body[10].value.left.ops[0])
+    _ast.Eq = type(m.body[10].value.left.ops[1])
+
+    _ast.Gt = type(m.body[11].value.ops[0])
+    _ast.Lt = type(m.body[11].value.ops[1])
+    _ast.GtE = type(m.body[11].value.ops[2])
+    _ast.LtE = type(m.body[11].value.ops[3])
+
+    _ast.In = type(m.body[12].value.ops[0])
+    _ast.NotIn = type(m.body[12].value.ops[1])
+
+
+
+def read_file(path, mode='rb'):
+    fp = open(path, mode)
+    try:
+        data = fp.read()
+        return data
+    finally:
+        fp.close()
+
+def read_python_file(path):
+    fp = open(path, "rb")
+    try:
+        encoding = parse_encoding(fp)
+        data = fp.read()
+        if encoding:
+            data = data.decode(encoding)
+        return data
+    finally:
+        fp.close()
+
diff --git a/lib/markupsafe/__init__.py b/lib/markupsafe/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..275540154ea5b5a7e3031939c952d774e42c29ed
--- /dev/null
+++ b/lib/markupsafe/__init__.py
@@ -0,0 +1,298 @@
+# -*- coding: utf-8 -*-
+"""
+    markupsafe
+    ~~~~~~~~~~
+
+    Implements a Markup string.
+
+    :copyright: (c) 2010 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import string
+from collections import Mapping
+from markupsafe._compat import text_type, string_types, int_types, \
+     unichr, iteritems, PY2
+
+
+__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
+
+
+_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
+_entity_re = re.compile(r'&([^;]+);')
+
+
+class Markup(text_type):
+    r"""Marks a string as being safe for inclusion in HTML/XML output without
+    needing to be escaped.  This implements the `__html__` interface a couple
+    of frameworks and web applications use.  :class:`Markup` is a direct
+    subclass of `unicode` and provides all the methods of `unicode` just that
+    it escapes arguments passed and always returns `Markup`.
+
+    The `escape` function returns markup objects so that double escaping can't
+    happen.
+
+    The constructor of the :class:`Markup` class can be used for three
+    different things:  When passed an unicode object it's assumed to be safe,
+    when passed an object with an HTML representation (has an `__html__`
+    method) that representation is used, otherwise the object passed is
+    converted into a unicode string and then assumed to be safe:
+
+    >>> Markup("Hello <em>World</em>!")
+    Markup(u'Hello <em>World</em>!')
+    >>> class Foo(object):
+    ...  def __html__(self):
+    ...   return '<a href="#">foo</a>'
+    ...
+    >>> Markup(Foo())
+    Markup(u'<a href="#">foo</a>')
+
+    If you want object passed being always treated as unsafe you can use the
+    :meth:`escape` classmethod to create a :class:`Markup` object:
+
+    >>> Markup.escape("Hello <em>World</em>!")
+    Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
+
+    Operations on a markup string are markup aware which means that all
+    arguments are passed through the :func:`escape` function:
+
+    >>> em = Markup("<em>%s</em>")
+    >>> em % "foo & bar"
+    Markup(u'<em>foo &amp; bar</em>')
+    >>> strong = Markup("<strong>%(text)s</strong>")
+    >>> strong % {'text': '<blink>hacker here</blink>'}
+    Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
+    >>> Markup("<em>Hello</em> ") + "<foo>"
+    Markup(u'<em>Hello</em> &lt;foo&gt;')
+    """
+    __slots__ = ()
+
+    def __new__(cls, base=u'', encoding=None, errors='strict'):
+        if hasattr(base, '__html__'):
+            base = base.__html__()
+        if encoding is None:
+            return text_type.__new__(cls, base)
+        return text_type.__new__(cls, base, encoding, errors)
+
+    def __html__(self):
+        return self
+
+    def __add__(self, other):
+        if isinstance(other, string_types) or hasattr(other, '__html__'):
+            return self.__class__(super(Markup, self).__add__(self.escape(other)))
+        return NotImplemented
+
+    def __radd__(self, other):
+        if hasattr(other, '__html__') or isinstance(other, string_types):
+            return self.escape(other).__add__(self)
+        return NotImplemented
+
+    def __mul__(self, num):
+        if isinstance(num, int_types):
+            return self.__class__(text_type.__mul__(self, num))
+        return NotImplemented
+    __rmul__ = __mul__
+
+    def __mod__(self, arg):
+        if isinstance(arg, tuple):
+            arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
+        else:
+            arg = _MarkupEscapeHelper(arg, self.escape)
+        return self.__class__(text_type.__mod__(self, arg))
+
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__,
+            text_type.__repr__(self)
+        )
+
+    def join(self, seq):
+        return self.__class__(text_type.join(self, map(self.escape, seq)))
+    join.__doc__ = text_type.join.__doc__
+
+    def split(self, *args, **kwargs):
+        return list(map(self.__class__, text_type.split(self, *args, **kwargs)))
+    split.__doc__ = text_type.split.__doc__
+
+    def rsplit(self, *args, **kwargs):
+        return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs)))
+    rsplit.__doc__ = text_type.rsplit.__doc__
+
+    def splitlines(self, *args, **kwargs):
+        return list(map(self.__class__, text_type.splitlines(
+            self, *args, **kwargs)))
+    splitlines.__doc__ = text_type.splitlines.__doc__
+
+    def unescape(self):
+        r"""Unescape markup again into an text_type string.  This also resolves
+        known HTML4 and XHTML entities:
+
+        >>> Markup("Main &raquo; <em>About</em>").unescape()
+        u'Main \xbb <em>About</em>'
+        """
+        from markupsafe._constants import HTML_ENTITIES
+        def handle_match(m):
+            name = m.group(1)
+            if name in HTML_ENTITIES:
+                return unichr(HTML_ENTITIES[name])
+            try:
+                if name[:2] in ('#x', '#X'):
+                    return unichr(int(name[2:], 16))
+                elif name.startswith('#'):
+                    return unichr(int(name[1:]))
+            except ValueError:
+                pass
+            return u''
+        return _entity_re.sub(handle_match, text_type(self))
+
+    def striptags(self):
+        r"""Unescape markup into an text_type string and strip all tags.  This
+        also resolves known HTML4 and XHTML entities.  Whitespace is
+        normalized to one:
+
+        >>> Markup("Main &raquo;  <em>About</em>").striptags()
+        u'Main \xbb About'
+        """
+        stripped = u' '.join(_striptags_re.sub('', self).split())
+        return Markup(stripped).unescape()
+
+    @classmethod
+    def escape(cls, s):
+        """Escape the string.  Works like :func:`escape` with the difference
+        that for subclasses of :class:`Markup` this function would return the
+        correct subclass.
+        """
+        rv = escape(s)
+        if rv.__class__ is not cls:
+            return cls(rv)
+        return rv
+
+    def make_simple_escaping_wrapper(name):
+        orig = getattr(text_type, name)
+        def func(self, *args, **kwargs):
+            args = _escape_argspec(list(args), enumerate(args), self.escape)
+            _escape_argspec(kwargs, iteritems(kwargs), self.escape)
+            return self.__class__(orig(self, *args, **kwargs))
+        func.__name__ = orig.__name__
+        func.__doc__ = orig.__doc__
+        return func
+
+    for method in '__getitem__', 'capitalize', \
+                  'title', 'lower', 'upper', 'replace', 'ljust', \
+                  'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
+                  'translate', 'expandtabs', 'swapcase', 'zfill':
+        locals()[method] = make_simple_escaping_wrapper(method)
+
+    # new in python 2.5
+    if hasattr(text_type, 'partition'):
+        def partition(self, sep):
+            return tuple(map(self.__class__,
+                             text_type.partition(self, self.escape(sep))))
+        def rpartition(self, sep):
+            return tuple(map(self.__class__,
+                             text_type.rpartition(self, self.escape(sep))))
+
+    # new in python 2.6
+    if hasattr(text_type, 'format'):
+        def format(*args, **kwargs):
+            self, args = args[0], args[1:]
+            formatter = EscapeFormatter(self.escape)
+            kwargs = _MagicFormatMapping(args, kwargs)
+            return self.__class__(formatter.vformat(self, args, kwargs))
+
+        def __html_format__(self, format_spec):
+            if format_spec:
+                raise ValueError('Unsupported format specification '
+                                 'for Markup.')
+            return self
+
+    # not in python 3
+    if hasattr(text_type, '__getslice__'):
+        __getslice__ = make_simple_escaping_wrapper('__getslice__')
+
+    del method, make_simple_escaping_wrapper
+
+
+class _MagicFormatMapping(Mapping):
+    """This class implements a dummy wrapper to fix a bug in the Python
+    standard library for string formatting.
+
+    See http://bugs.python.org/issue13598 for information about why
+    this is necessary.
+    """
+
+    def __init__(self, args, kwargs):
+        self._args = args
+        self._kwargs = kwargs
+        self._last_index = 0
+
+    def __getitem__(self, key):
+        if key == '':
+            idx = self._last_index
+            self._last_index += 1
+            try:
+                return self._args[idx]
+            except LookupError:
+                pass
+            key = str(idx)
+        return self._kwargs[key]
+
+    def __iter__(self):
+        return iter(self._kwargs)
+
+    def __len__(self):
+        return len(self._kwargs)
+
+
+if hasattr(text_type, 'format'):
+    class EscapeFormatter(string.Formatter):
+
+        def __init__(self, escape):
+            self.escape = escape
+
+        def format_field(self, value, format_spec):
+            if hasattr(value, '__html_format__'):
+                rv = value.__html_format__(format_spec)
+            elif hasattr(value, '__html__'):
+                if format_spec:
+                    raise ValueError('No format specification allowed '
+                                     'when formatting an object with '
+                                     'its __html__ method.')
+                rv = value.__html__()
+            else:
+                rv = string.Formatter.format_field(self, value, format_spec)
+            return text_type(self.escape(rv))
+
+
+def _escape_argspec(obj, iterable, escape):
+    """Helper for various string-wrapped functions."""
+    for key, value in iterable:
+        if hasattr(value, '__html__') or isinstance(value, string_types):
+            obj[key] = escape(value)
+    return obj
+
+
+class _MarkupEscapeHelper(object):
+    """Helper for Markup.__mod__"""
+
+    def __init__(self, obj, escape):
+        self.obj = obj
+        self.escape = escape
+
+    __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
+    __unicode__ = __str__ = lambda s: text_type(s.escape(s.obj))
+    __repr__ = lambda s: str(s.escape(repr(s.obj)))
+    __int__ = lambda s: int(s.obj)
+    __float__ = lambda s: float(s.obj)
+
+
+# we have to import it down here as the speedups and native
+# modules imports the markup type which is define above.
+try:
+    from markupsafe._speedups import escape, escape_silent, soft_unicode
+except ImportError:
+    from markupsafe._native import escape, escape_silent, soft_unicode
+
+if not PY2:
+    soft_str = soft_unicode
+    __all__.append('soft_str')
diff --git a/lib/markupsafe/_compat.py b/lib/markupsafe/_compat.py
new file mode 100644
index 0000000000000000000000000000000000000000..62e5632ad8fd531fbc17dc99ac062d413e93a002
--- /dev/null
+++ b/lib/markupsafe/_compat.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""
+    markupsafe._compat
+    ~~~~~~~~~~~~~~~~~~
+
+    Compatibility module for different Python versions.
+
+    :copyright: (c) 2013 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import sys
+
+PY2 = sys.version_info[0] == 2
+
+if not PY2:
+    text_type = str
+    string_types = (str,)
+    unichr = chr
+    int_types = (int,)
+    iteritems = lambda x: iter(x.items())
+else:
+    text_type = unicode
+    string_types = (str, unicode)
+    unichr = unichr
+    int_types = (int, long)
+    iteritems = lambda x: x.iteritems()
diff --git a/lib/markupsafe/_constants.py b/lib/markupsafe/_constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..919bf03c5092e557b164e7e2322b12ed74d349fb
--- /dev/null
+++ b/lib/markupsafe/_constants.py
@@ -0,0 +1,267 @@
+# -*- coding: utf-8 -*-
+"""
+    markupsafe._constants
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Highlevel implementation of the Markup string.
+
+    :copyright: (c) 2010 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+
+
+HTML_ENTITIES = {
+    'AElig': 198,
+    'Aacute': 193,
+    'Acirc': 194,
+    'Agrave': 192,
+    'Alpha': 913,
+    'Aring': 197,
+    'Atilde': 195,
+    'Auml': 196,
+    'Beta': 914,
+    'Ccedil': 199,
+    'Chi': 935,
+    'Dagger': 8225,
+    'Delta': 916,
+    'ETH': 208,
+    'Eacute': 201,
+    'Ecirc': 202,
+    'Egrave': 200,
+    'Epsilon': 917,
+    'Eta': 919,
+    'Euml': 203,
+    'Gamma': 915,
+    'Iacute': 205,
+    'Icirc': 206,
+    'Igrave': 204,
+    'Iota': 921,
+    'Iuml': 207,
+    'Kappa': 922,
+    'Lambda': 923,
+    'Mu': 924,
+    'Ntilde': 209,
+    'Nu': 925,
+    'OElig': 338,
+    'Oacute': 211,
+    'Ocirc': 212,
+    'Ograve': 210,
+    'Omega': 937,
+    'Omicron': 927,
+    'Oslash': 216,
+    'Otilde': 213,
+    'Ouml': 214,
+    'Phi': 934,
+    'Pi': 928,
+    'Prime': 8243,
+    'Psi': 936,
+    'Rho': 929,
+    'Scaron': 352,
+    'Sigma': 931,
+    'THORN': 222,
+    'Tau': 932,
+    'Theta': 920,
+    'Uacute': 218,
+    'Ucirc': 219,
+    'Ugrave': 217,
+    'Upsilon': 933,
+    'Uuml': 220,
+    'Xi': 926,
+    'Yacute': 221,
+    'Yuml': 376,
+    'Zeta': 918,
+    'aacute': 225,
+    'acirc': 226,
+    'acute': 180,
+    'aelig': 230,
+    'agrave': 224,
+    'alefsym': 8501,
+    'alpha': 945,
+    'amp': 38,
+    'and': 8743,
+    'ang': 8736,
+    'apos': 39,
+    'aring': 229,
+    'asymp': 8776,
+    'atilde': 227,
+    'auml': 228,
+    'bdquo': 8222,
+    'beta': 946,
+    'brvbar': 166,
+    'bull': 8226,
+    'cap': 8745,
+    'ccedil': 231,
+    'cedil': 184,
+    'cent': 162,
+    'chi': 967,
+    'circ': 710,
+    'clubs': 9827,
+    'cong': 8773,
+    'copy': 169,
+    'crarr': 8629,
+    'cup': 8746,
+    'curren': 164,
+    'dArr': 8659,
+    'dagger': 8224,
+    'darr': 8595,
+    'deg': 176,
+    'delta': 948,
+    'diams': 9830,
+    'divide': 247,
+    'eacute': 233,
+    'ecirc': 234,
+    'egrave': 232,
+    'empty': 8709,
+    'emsp': 8195,
+    'ensp': 8194,
+    'epsilon': 949,
+    'equiv': 8801,
+    'eta': 951,
+    'eth': 240,
+    'euml': 235,
+    'euro': 8364,
+    'exist': 8707,
+    'fnof': 402,
+    'forall': 8704,
+    'frac12': 189,
+    'frac14': 188,
+    'frac34': 190,
+    'frasl': 8260,
+    'gamma': 947,
+    'ge': 8805,
+    'gt': 62,
+    'hArr': 8660,
+    'harr': 8596,
+    'hearts': 9829,
+    'hellip': 8230,
+    'iacute': 237,
+    'icirc': 238,
+    'iexcl': 161,
+    'igrave': 236,
+    'image': 8465,
+    'infin': 8734,
+    'int': 8747,
+    'iota': 953,
+    'iquest': 191,
+    'isin': 8712,
+    'iuml': 239,
+    'kappa': 954,
+    'lArr': 8656,
+    'lambda': 955,
+    'lang': 9001,
+    'laquo': 171,
+    'larr': 8592,
+    'lceil': 8968,
+    'ldquo': 8220,
+    'le': 8804,
+    'lfloor': 8970,
+    'lowast': 8727,
+    'loz': 9674,
+    'lrm': 8206,
+    'lsaquo': 8249,
+    'lsquo': 8216,
+    'lt': 60,
+    'macr': 175,
+    'mdash': 8212,
+    'micro': 181,
+    'middot': 183,
+    'minus': 8722,
+    'mu': 956,
+    'nabla': 8711,
+    'nbsp': 160,
+    'ndash': 8211,
+    'ne': 8800,
+    'ni': 8715,
+    'not': 172,
+    'notin': 8713,
+    'nsub': 8836,
+    'ntilde': 241,
+    'nu': 957,
+    'oacute': 243,
+    'ocirc': 244,
+    'oelig': 339,
+    'ograve': 242,
+    'oline': 8254,
+    'omega': 969,
+    'omicron': 959,
+    'oplus': 8853,
+    'or': 8744,
+    'ordf': 170,
+    'ordm': 186,
+    'oslash': 248,
+    'otilde': 245,
+    'otimes': 8855,
+    'ouml': 246,
+    'para': 182,
+    'part': 8706,
+    'permil': 8240,
+    'perp': 8869,
+    'phi': 966,
+    'pi': 960,
+    'piv': 982,
+    'plusmn': 177,
+    'pound': 163,
+    'prime': 8242,
+    'prod': 8719,
+    'prop': 8733,
+    'psi': 968,
+    'quot': 34,
+    'rArr': 8658,
+    'radic': 8730,
+    'rang': 9002,
+    'raquo': 187,
+    'rarr': 8594,
+    'rceil': 8969,
+    'rdquo': 8221,
+    'real': 8476,
+    'reg': 174,
+    'rfloor': 8971,
+    'rho': 961,
+    'rlm': 8207,
+    'rsaquo': 8250,
+    'rsquo': 8217,
+    'sbquo': 8218,
+    'scaron': 353,
+    'sdot': 8901,
+    'sect': 167,
+    'shy': 173,
+    'sigma': 963,
+    'sigmaf': 962,
+    'sim': 8764,
+    'spades': 9824,
+    'sub': 8834,
+    'sube': 8838,
+    'sum': 8721,
+    'sup': 8835,
+    'sup1': 185,
+    'sup2': 178,
+    'sup3': 179,
+    'supe': 8839,
+    'szlig': 223,
+    'tau': 964,
+    'there4': 8756,
+    'theta': 952,
+    'thetasym': 977,
+    'thinsp': 8201,
+    'thorn': 254,
+    'tilde': 732,
+    'times': 215,
+    'trade': 8482,
+    'uArr': 8657,
+    'uacute': 250,
+    'uarr': 8593,
+    'ucirc': 251,
+    'ugrave': 249,
+    'uml': 168,
+    'upsih': 978,
+    'upsilon': 965,
+    'uuml': 252,
+    'weierp': 8472,
+    'xi': 958,
+    'yacute': 253,
+    'yen': 165,
+    'yuml': 255,
+    'zeta': 950,
+    'zwj': 8205,
+    'zwnj': 8204
+}
diff --git a/lib/markupsafe/_native.py b/lib/markupsafe/_native.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e83f10a117c4717975327337ef43d0a14a91e96
--- /dev/null
+++ b/lib/markupsafe/_native.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+"""
+    markupsafe._native
+    ~~~~~~~~~~~~~~~~~~
+
+    Native Python implementation the C module is not compiled.
+
+    :copyright: (c) 2010 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from markupsafe import Markup
+from markupsafe._compat import text_type
+
+
+def escape(s):
+    """Convert the characters &, <, >, ' and " in string s to HTML-safe
+    sequences.  Use this if you need to display text that might contain
+    such characters in HTML.  Marks return value as markup string.
+    """
+    if hasattr(s, '__html__'):
+        return s.__html__()
+    return Markup(text_type(s)
+        .replace('&', '&amp;')
+        .replace('>', '&gt;')
+        .replace('<', '&lt;')
+        .replace("'", '&#39;')
+        .replace('"', '&#34;')
+    )
+
+
+def escape_silent(s):
+    """Like :func:`escape` but converts `None` into an empty
+    markup string.
+    """
+    if s is None:
+        return Markup()
+    return escape(s)
+
+
+def soft_unicode(s):
+    """Make a string unicode if it isn't already.  That way a markup
+    string is not converted back to unicode.
+    """
+    if not isinstance(s, text_type):
+        s = text_type(s)
+    return s
diff --git a/lib/markupsafe/_speedups.c b/lib/markupsafe/_speedups.c
new file mode 100644
index 0000000000000000000000000000000000000000..f349febf22d59ec7dfe440b65faf5838c1234b90
--- /dev/null
+++ b/lib/markupsafe/_speedups.c
@@ -0,0 +1,239 @@
+/**
+ * markupsafe._speedups
+ * ~~~~~~~~~~~~~~~~~~~~
+ *
+ * This module implements functions for automatic escaping in C for better
+ * performance.
+ *
+ * :copyright: (c) 2010 by Armin Ronacher.
+ * :license: BSD.
+ */
+
+#include <Python.h>
+
+#define ESCAPED_CHARS_TABLE_SIZE 63
+#define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL)));
+
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+
+static PyObject* markup;
+static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
+static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
+
+static int
+init_constants(void)
+{
+	PyObject *module;
+	/* happing of characters to replace */
+	escaped_chars_repl['"'] = UNICHR("&#34;");
+	escaped_chars_repl['\''] = UNICHR("&#39;");
+	escaped_chars_repl['&'] = UNICHR("&amp;");
+	escaped_chars_repl['<'] = UNICHR("&lt;");
+	escaped_chars_repl['>'] = UNICHR("&gt;");
+
+	/* lengths of those characters when replaced - 1 */
+	memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
+	escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \
+		escaped_chars_delta_len['&'] = 4;
+	escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3;
+	
+	/* import markup type so that we can mark the return value */
+	module = PyImport_ImportModule("markupsafe");
+	if (!module)
+		return 0;
+	markup = PyObject_GetAttrString(module, "Markup");
+	Py_DECREF(module);
+
+	return 1;
+}
+
+static PyObject*
+escape_unicode(PyUnicodeObject *in)
+{
+	PyUnicodeObject *out;
+	Py_UNICODE *inp = PyUnicode_AS_UNICODE(in);
+	const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in);
+	Py_UNICODE *next_escp;
+	Py_UNICODE *outp;
+	Py_ssize_t delta=0, erepl=0, delta_len=0;
+
+	/* First we need to figure out how long the escaped string will be */
+	while (*(inp) || inp < inp_end) {
+		if (*inp < ESCAPED_CHARS_TABLE_SIZE) {
+			delta += escaped_chars_delta_len[*inp];
+			erepl += !!escaped_chars_delta_len[*inp];
+		}
+		++inp;
+	}
+
+	/* Do we need to escape anything at all? */
+	if (!erepl) {
+		Py_INCREF(in);
+		return (PyObject*)in;
+	}
+
+	out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta);
+	if (!out)
+		return NULL;
+
+	outp = PyUnicode_AS_UNICODE(out);
+	inp = PyUnicode_AS_UNICODE(in);
+	while (erepl-- > 0) {
+		/* look for the next substitution */
+		next_escp = inp;
+		while (next_escp < inp_end) {
+			if (*next_escp < ESCAPED_CHARS_TABLE_SIZE &&
+			    (delta_len = escaped_chars_delta_len[*next_escp])) {
+				++delta_len;
+				break;
+			}
+			++next_escp;
+		}
+		
+		if (next_escp > inp) {
+			/* copy unescaped chars between inp and next_escp */
+			Py_UNICODE_COPY(outp, inp, next_escp-inp);
+			outp += next_escp - inp;
+		}
+
+		/* escape 'next_escp' */
+		Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
+		outp += delta_len;
+
+		inp = next_escp + 1;
+	}
+	if (inp < inp_end)
+		Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in)));
+
+	return (PyObject*)out;
+}
+
+
+static PyObject*
+escape(PyObject *self, PyObject *text)
+{
+	PyObject *s = NULL, *rv = NULL, *html;
+
+	/* we don't have to escape integers, bools or floats */
+	if (PyLong_CheckExact(text) ||
+#if PY_MAJOR_VERSION < 3
+	    PyInt_CheckExact(text) ||
+#endif
+	    PyFloat_CheckExact(text) || PyBool_Check(text) ||
+	    text == Py_None)
+		return PyObject_CallFunctionObjArgs(markup, text, NULL);
+
+	/* if the object has an __html__ method that performs the escaping */
+	html = PyObject_GetAttrString(text, "__html__");
+	if (html) {
+		rv = PyObject_CallObject(html, NULL);
+		Py_DECREF(html);
+		return rv;
+	}
+
+	/* otherwise make the object unicode if it isn't, then escape */
+	PyErr_Clear();
+	if (!PyUnicode_Check(text)) {
+#if PY_MAJOR_VERSION < 3
+		PyObject *unicode = PyObject_Unicode(text);
+#else
+		PyObject *unicode = PyObject_Str(text);
+#endif
+		if (!unicode)
+			return NULL;
+		s = escape_unicode((PyUnicodeObject*)unicode);
+		Py_DECREF(unicode);
+	}
+	else
+		s = escape_unicode((PyUnicodeObject*)text);
+
+	/* convert the unicode string into a markup object. */
+	rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
+	Py_DECREF(s);
+	return rv;
+}
+
+
+static PyObject*
+escape_silent(PyObject *self, PyObject *text)
+{
+	if (text != Py_None)
+		return escape(self, text);
+	return PyObject_CallFunctionObjArgs(markup, NULL);
+}
+
+
+static PyObject*
+soft_unicode(PyObject *self, PyObject *s)
+{
+	if (!PyUnicode_Check(s))
+#if PY_MAJOR_VERSION < 3
+		return PyObject_Unicode(s);
+#else
+		return PyObject_Str(s);
+#endif
+	Py_INCREF(s);
+	return s;
+}
+
+
+static PyMethodDef module_methods[] = {
+	{"escape", (PyCFunction)escape, METH_O,
+	 "escape(s) -> markup\n\n"
+	 "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n"
+	 "sequences.  Use this if you need to display text that might contain\n"
+	 "such characters in HTML.  Marks return value as markup string."},
+	{"escape_silent", (PyCFunction)escape_silent, METH_O,
+	 "escape_silent(s) -> markup\n\n"
+	 "Like escape but converts None to an empty string."},
+	{"soft_unicode", (PyCFunction)soft_unicode, METH_O,
+	 "soft_unicode(object) -> string\n\n"
+         "Make a string unicode if it isn't already.  That way a markup\n"
+         "string is not converted back to unicode."},
+	{NULL, NULL, 0, NULL}		/* Sentinel */
+};
+
+
+#if PY_MAJOR_VERSION < 3
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_speedups(void)
+{
+	if (!init_constants())
+		return;
+
+	Py_InitModule3("markupsafe._speedups", module_methods, "");
+}
+
+#else /* Python 3.x module initialization */
+
+static struct PyModuleDef module_definition = {
+        PyModuleDef_HEAD_INIT,
+	"markupsafe._speedups",
+	NULL,
+	-1,
+	module_methods,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+PyMODINIT_FUNC
+PyInit__speedups(void)
+{
+	if (!init_constants())
+		return NULL;
+
+	return PyModule_Create(&module_definition);
+}
+
+#endif
diff --git a/lib/markupsafe/tests.py b/lib/markupsafe/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..6369936296e2905865b20c7f52170357bc870d8c
--- /dev/null
+++ b/lib/markupsafe/tests.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+import gc
+import sys
+import unittest
+from markupsafe import Markup, escape, escape_silent
+from markupsafe._compat import text_type
+
+
+class MarkupTestCase(unittest.TestCase):
+
+    def test_adding(self):
+        # adding two strings should escape the unsafe one
+        unsafe = '<script type="application/x-some-script">alert("foo");</script>'
+        safe = Markup('<em>username</em>')
+        assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe)
+
+    def test_string_interpolation(self):
+        # string interpolations are safe to use too
+        assert Markup('<em>%s</em>') % '<bad user>' == \
+               '<em>&lt;bad user&gt;</em>'
+        assert Markup('<em>%(username)s</em>') % {
+            'username': '<bad user>'
+        } == '<em>&lt;bad user&gt;</em>'
+
+        assert Markup('%i') % 3.14 == '3'
+        assert Markup('%.2f') % 3.14 == '3.14'
+
+    def test_type_behavior(self):
+        # an escaped object is markup too
+        assert type(Markup('foo') + 'bar') is Markup
+
+        # and it implements __html__ by returning itself
+        x = Markup("foo")
+        assert x.__html__() is x
+
+    def test_html_interop(self):
+        # it also knows how to treat __html__ objects
+        class Foo(object):
+            def __html__(self):
+                return '<em>awesome</em>'
+            def __unicode__(self):
+                return 'awesome'
+            __str__ = __unicode__
+        assert Markup(Foo()) == '<em>awesome</em>'
+        assert Markup('<strong>%s</strong>') % Foo() == \
+            '<strong><em>awesome</em></strong>'
+
+    def test_tuple_interpol(self):
+        self.assertEqual(Markup('<em>%s:%s</em>') % (
+            '<foo>',
+            '<bar>',
+        ), Markup(u'<em>&lt;foo&gt;:&lt;bar&gt;</em>'))
+
+    def test_dict_interpol(self):
+        self.assertEqual(Markup('<em>%(foo)s</em>') % {
+            'foo': '<foo>',
+        }, Markup(u'<em>&lt;foo&gt;</em>'))
+        self.assertEqual(Markup('<em>%(foo)s:%(bar)s</em>') % {
+            'foo': '<foo>',
+            'bar': '<bar>',
+        }, Markup(u'<em>&lt;foo&gt;:&lt;bar&gt;</em>'))
+
+    def test_escaping(self):
+        # escaping and unescaping
+        assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
+        assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
+        assert Markup("&lt;test&gt;").unescape() == "<test>"
+
+    def test_formatting(self):
+        for actual, expected in (
+            (Markup('%i') % 3.14, '3'),
+            (Markup('%.2f') % 3.14159, '3.14'),
+            (Markup('%s %s %s') % ('<', 123, '>'), '&lt; 123 &gt;'),
+            (Markup('<em>{awesome}</em>').format(awesome='<awesome>'),
+             '<em>&lt;awesome&gt;</em>'),
+            (Markup('{0[1][bar]}').format([0, {'bar': '<bar/>'}]),
+             '&lt;bar/&gt;'),
+            (Markup('{0[1][bar]}').format([0, {'bar': Markup('<bar/>')}]),
+             '<bar/>')):
+            assert actual == expected, "%r should be %r!" % (actual, expected)
+
+    # This is new in 2.7
+    if sys.version_info >= (2, 7):
+        def test_formatting_empty(self):
+            formatted = Markup('{}').format(0)
+            assert formatted == Markup('0')
+
+    def test_custom_formatting(self):
+        class HasHTMLOnly(object):
+            def __html__(self):
+                return Markup('<foo>')
+
+        class HasHTMLAndFormat(object):
+            def __html__(self):
+                return Markup('<foo>')
+            def __html_format__(self, spec):
+                return Markup('<FORMAT>')
+
+        assert Markup('{0}').format(HasHTMLOnly()) == Markup('<foo>')
+        assert Markup('{0}').format(HasHTMLAndFormat()) == Markup('<FORMAT>')
+
+    def test_complex_custom_formatting(self):
+        class User(object):
+            def __init__(self, id, username):
+                self.id = id
+                self.username = username
+            def __html_format__(self, format_spec):
+                if format_spec == 'link':
+                    return Markup('<a href="/user/{0}">{1}</a>').format(
+                        self.id,
+                        self.__html__(),
+                    )
+                elif format_spec:
+                    raise ValueError('Invalid format spec')
+                return self.__html__()
+            def __html__(self):
+                return Markup('<span class=user>{0}</span>').format(self.username)
+
+        user = User(1, 'foo')
+        assert Markup('<p>User: {0:link}').format(user) == \
+            Markup('<p>User: <a href="/user/1"><span class=user>foo</span></a>')
+
+    def test_all_set(self):
+        import markupsafe as markup
+        for item in markup.__all__:
+            getattr(markup, item)
+
+    def test_escape_silent(self):
+        assert escape_silent(None) == Markup()
+        assert escape(None) == Markup(None)
+        assert escape_silent('<foo>') == Markup(u'&lt;foo&gt;')
+
+    def test_splitting(self):
+        self.assertEqual(Markup('a b').split(), [
+            Markup('a'),
+            Markup('b')
+        ])
+        self.assertEqual(Markup('a b').rsplit(), [
+            Markup('a'),
+            Markup('b')
+        ])
+        self.assertEqual(Markup('a\nb').splitlines(), [
+            Markup('a'),
+            Markup('b')
+        ])
+
+    def test_mul(self):
+        self.assertEqual(Markup('a') * 3, Markup('aaa'))
+
+
+class MarkupLeakTestCase(unittest.TestCase):
+
+    def test_markup_leaks(self):
+        counts = set()
+        for count in range(20):
+            for item in range(1000):
+                escape("foo")
+                escape("<foo>")
+                escape(u"foo")
+                escape(u"<foo>")
+            counts.add(len(gc.get_objects()))
+        assert len(counts) == 1, 'ouch, c extension seems to leak objects'
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(MarkupTestCase))
+
+    # this test only tests the c extension
+    if not hasattr(escape, 'func_code'):
+        suite.addTest(unittest.makeSuite(MarkupLeakTestCase))
+
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')
+
+# vim:sts=4:sw=4:et:
diff --git a/lib/synchronousdeluge/__init__.py b/lib/synchronousdeluge/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf5b20fe04a03ddf47caea0ffd9832f2c932fe40
--- /dev/null
+++ b/lib/synchronousdeluge/__init__.py
@@ -0,0 +1,24 @@
+"""A synchronous implementation of the Deluge RPC protocol
+   based on gevent-deluge by Christopher Rosell.
+   
+   https://github.com/chrippa/gevent-deluge
+
+Example usage:
+
+    from synchronousdeluge import DelgueClient
+
+    client = DelugeClient()
+    client.connect()
+
+    # Wait for value
+    download_location = client.core.get_config_value("download_location").get()
+"""
+
+
+__title__ = "synchronous-deluge"
+__version__ = "0.1"
+__author__ = "Christian Dale"
+
+from synchronousdeluge.client import DelugeClient
+from synchronousdeluge.exceptions import DelugeRPCError
+
diff --git a/lib/synchronousdeluge/client.py b/lib/synchronousdeluge/client.py
new file mode 100644
index 0000000000000000000000000000000000000000..22419e80bab5dafe57d51cba872c7886ab9dfce0
--- /dev/null
+++ b/lib/synchronousdeluge/client.py
@@ -0,0 +1,162 @@
+import os
+import platform
+
+from collections import defaultdict
+from itertools import imap
+
+from synchronousdeluge.exceptions import DelugeRPCError
+from synchronousdeluge.protocol import DelugeRPCRequest, DelugeRPCResponse
+from synchronousdeluge.transfer import DelugeTransfer
+
+__all__ = ["DelugeClient"]
+
+
+RPC_RESPONSE = 1
+RPC_ERROR = 2
+RPC_EVENT = 3
+
+
+class DelugeClient(object):
+    def __init__(self):
+        """A deluge client session."""
+        self.transfer = DelugeTransfer()
+        self.modules = []
+        self._request_counter = 0
+
+    def _get_local_auth(self):
+        auth_file = ""
+        username = password = ""
+        if platform.system() in ('Windows', 'Microsoft'):
+            appDataPath = os.environ.get("APPDATA")
+            if not appDataPath:
+                import _winreg
+                hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")
+                appDataReg = _winreg.QueryValueEx(hkey, "AppData")
+                appDataPath = appDataReg[0]
+                _winreg.CloseKey(hkey)
+
+            auth_file = os.path.join(appDataPath, "deluge", "auth")
+        else:
+            from xdg.BaseDirectory import save_config_path
+            try:
+                auth_file = os.path.join(save_config_path("deluge"), "auth")
+            except OSError, e:
+                return username, password
+
+
+        if os.path.exists(auth_file):
+            for line in open(auth_file):
+                if line.startswith("#"):
+                    # This is a comment line
+                    continue
+                line = line.strip()
+                try:
+                    lsplit = line.split(":")
+                except Exception, e:
+                    continue
+
+                if len(lsplit) == 2:
+                    username, password = lsplit
+                elif len(lsplit) == 3:
+                    username, password, level = lsplit
+                else:
+                    continue
+
+                if username == "localclient":
+                    return (username, password)
+
+        return ("", "")
+
+    def _create_module_method(self, module, method):
+        fullname = "{0}.{1}".format(module, method)
+
+        def func(obj, *args, **kwargs):
+            return self.remote_call(fullname, *args, **kwargs)
+
+        func.__name__ = method
+
+        return func
+
+    def _introspect(self):
+        self.modules = []
+
+        methods = self.remote_call("daemon.get_method_list").get()
+        methodmap = defaultdict(dict)
+        splitter = lambda v: v.split(".")
+
+        for module, method in imap(splitter, methods):
+            methodmap[module][method] = self._create_module_method(module, method)
+
+        for module, methods in methodmap.items():
+            clsname = "DelugeModule{0}".format(module.capitalize())
+            cls = type(clsname, (), methods)
+            setattr(self, module, cls())
+            self.modules.append(module)
+
+    def remote_call(self, method, *args, **kwargs):
+        req = DelugeRPCRequest(self._request_counter, method, *args, **kwargs)
+        message = next(self.transfer.send_request(req))
+
+        response = DelugeRPCResponse()
+
+        if not isinstance(message, tuple):
+            return
+
+        if len(message) < 3:
+            return
+
+        message_type = message[0]
+
+#        if message_type == RPC_EVENT:
+#            event = message[1]
+#            values = message[2]
+#
+#            if event in self._event_handlers:
+#                for handler in self._event_handlers[event]:
+#                    gevent.spawn(handler, *values)
+#
+#        elif message_type in (RPC_RESPONSE, RPC_ERROR):
+        if message_type in (RPC_RESPONSE, RPC_ERROR):
+            request_id = message[1]
+            value = message[2]
+
+            if request_id == self._request_counter :
+                if message_type == RPC_RESPONSE:
+                    response.set(value)
+                elif message_type == RPC_ERROR:
+                    err = DelugeRPCError(*value)
+                    response.set_exception(err)
+
+        self._request_counter += 1
+        return response
+
+    def connect(self, host="127.0.0.1", port=58846, username="", password=""):
+        """Connects to a daemon process.
+
+        :param host: str, the hostname of the daemon
+        :param port: int, the port of the daemon
+        :param username: str, the username to login with
+        :param password: str, the password to login with
+        """
+
+        # Connect transport
+        self.transfer.connect((host, port))
+
+        # Attempt to fetch local auth info if needed
+        if not username and host in ("127.0.0.1", "localhost"):
+            username, password = self._get_local_auth()
+
+        # Authenticate
+        self.remote_call("daemon.login", username, password).get()
+
+        # Introspect available methods
+        self._introspect()
+
+    @property
+    def connected(self):
+        return self.transfer.connected
+
+    def disconnect(self):
+        """Disconnects from the daemon."""
+        self.transfer.disconnect()
+
diff --git a/lib/synchronousdeluge/exceptions.py b/lib/synchronousdeluge/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..da6cf0228d97ef70a743a4a026db29d27658b4a4
--- /dev/null
+++ b/lib/synchronousdeluge/exceptions.py
@@ -0,0 +1,11 @@
+__all__ = ["DelugeRPCError"]
+
+class DelugeRPCError(Exception):
+    def __init__(self, name, msg, traceback):
+        self.name = name
+        self.msg = msg
+        self.traceback = traceback
+
+    def __str__(self):
+        return "{0}: {1}: {2}".format(self.__class__.__name__, self.name, self.msg)
+
diff --git a/lib/synchronousdeluge/protocol.py b/lib/synchronousdeluge/protocol.py
new file mode 100644
index 0000000000000000000000000000000000000000..756d4dfc72727bd56029d9b2891c9ae00178b1b5
--- /dev/null
+++ b/lib/synchronousdeluge/protocol.py
@@ -0,0 +1,38 @@
+__all__ = ["DelugeRPCRequest", "DelugeRPCResponse"]
+
+class DelugeRPCRequest(object):
+    def __init__(self, request_id, method, *args, **kwargs):
+        self.request_id = request_id
+        self.method = method
+        self.args = args
+        self.kwargs = kwargs
+
+    def format(self):
+        return (self.request_id, self.method, self.args, self.kwargs)
+
+class DelugeRPCResponse(object):
+    def __init__(self):
+        self.value = None
+        self._exception = None
+
+    def successful(self):
+        return self._exception is None
+
+    @property
+    def exception(self):
+        if self._exception is not None:
+            return self._exception
+
+    def set(self, value=None):
+        self.value = value
+        self._exception = None
+
+    def set_exception(self, exception):
+        self._exception = exception
+
+    def get(self):
+        if self._exception is None:
+            return self.value
+        else:
+            raise self._exception
+
diff --git a/lib/synchronousdeluge/rencode.py b/lib/synchronousdeluge/rencode.py
new file mode 100644
index 0000000000000000000000000000000000000000..e58c715422e7a428cec515db93956df25ea4d296
--- /dev/null
+++ b/lib/synchronousdeluge/rencode.py
@@ -0,0 +1,433 @@
+
+"""
+rencode -- Web safe object pickling/unpickling.
+
+Public domain, Connelly Barnes 2006-2007.
+
+The rencode module is a modified version of bencode from the
+BitTorrent project.  For complex, heterogeneous data structures with
+many small elements, r-encodings take up significantly less space than
+b-encodings:
+
+ >>> len(rencode.dumps({'a':0, 'b':[1,2], 'c':99}))
+ 13
+ >>> len(bencode.bencode({'a':0, 'b':[1,2], 'c':99}))
+ 26
+
+The rencode format is not standardized, and may change with different
+rencode module versions, so you should check that you are using the
+same rencode version throughout your project.
+"""
+
+__version__ = '1.0.1'
+__all__ = ['dumps', 'loads']
+
+# Original bencode module by Petru Paler, et al.
+#
+# Modifications by Connelly Barnes:
+#
+#  - Added support for floats (sent as 32-bit or 64-bit in network
+#    order), bools, None.
+#  - Allowed dict keys to be of any serializable type.
+#  - Lists/tuples are always decoded as tuples (thus, tuples can be
+#    used as dict keys).
+#  - Embedded extra information in the 'typecodes' to save some space.
+#  - Added a restriction on integer length, so that malicious hosts
+#    cannot pass us large integers which take a long time to decode.
+#
+# Licensed by Bram Cohen under the "MIT license":
+#
+#  "Copyright (C) 2001-2002 Bram Cohen
+#
+#  Permission is hereby granted, free of charge, to any person
+#  obtaining a copy of this software and associated documentation files
+#  (the "Software"), to deal in the Software without restriction,
+#  including without limitation the rights to use, copy, modify, merge,
+#  publish, distribute, sublicense, and/or sell copies of the Software,
+#  and to permit persons to whom the Software is furnished to do so,
+#  subject to the following conditions:
+#
+#  The above copyright notice and this permission notice shall be
+#  included in all copies or substantial portions of the Software.
+#
+#  The Software is provided "AS IS", without warranty of any kind,
+#  express or implied, including but not limited to the warranties of
+#  merchantability,  fitness for a particular purpose and
+#  noninfringement. In no event shall the  authors or copyright holders
+#  be liable for any claim, damages or other liability, whether in an
+#  action of contract, tort or otherwise, arising from, out of or in
+#  connection with the Software or the use or other dealings in the
+#  Software."
+#
+# (The rencode module is licensed under the above license as well).
+#
+
+import struct
+import string
+from threading import Lock
+
+# Default number of bits for serialized floats, either 32 or 64 (also a parameter for dumps()).
+DEFAULT_FLOAT_BITS = 32
+
+# Maximum length of integer when written as base 10 string.
+MAX_INT_LENGTH = 64
+
+# The bencode 'typecodes' such as i, d, etc have been extended and
+# relocated on the base-256 character set.
+CHR_LIST    = chr(59)
+CHR_DICT    = chr(60)
+CHR_INT     = chr(61)
+CHR_INT1    = chr(62)
+CHR_INT2    = chr(63)
+CHR_INT4    = chr(64)
+CHR_INT8    = chr(65)
+CHR_FLOAT32 = chr(66)
+CHR_FLOAT64 = chr(44)
+CHR_TRUE    = chr(67)
+CHR_FALSE   = chr(68)
+CHR_NONE    = chr(69)
+CHR_TERM    = chr(127)
+
+# Positive integers with value embedded in typecode.
+INT_POS_FIXED_START = 0
+INT_POS_FIXED_COUNT = 44
+
+# Dictionaries with length embedded in typecode.
+DICT_FIXED_START = 102
+DICT_FIXED_COUNT = 25
+
+# Negative integers with value embedded in typecode.
+INT_NEG_FIXED_START = 70
+INT_NEG_FIXED_COUNT = 32
+
+# Strings with length embedded in typecode.
+STR_FIXED_START = 128
+STR_FIXED_COUNT = 64
+
+# Lists with length embedded in typecode.
+LIST_FIXED_START = STR_FIXED_START+STR_FIXED_COUNT
+LIST_FIXED_COUNT = 64
+
+def decode_int(x, f):
+    f += 1
+    newf = x.index(CHR_TERM, f)
+    if newf - f >= MAX_INT_LENGTH:
+        raise ValueError('overflow')
+    try:
+        n = int(x[f:newf])
+    except (OverflowError, ValueError):
+        n = long(x[f:newf])
+    if x[f] == '-':
+        if x[f + 1] == '0':
+            raise ValueError
+    elif x[f] == '0' and newf != f+1:
+        raise ValueError
+    return (n, newf+1)
+
+def decode_intb(x, f):
+    f += 1
+    return (struct.unpack('!b', x[f:f+1])[0], f+1)
+
+def decode_inth(x, f):
+    f += 1
+    return (struct.unpack('!h', x[f:f+2])[0], f+2)
+
+def decode_intl(x, f):
+    f += 1
+    return (struct.unpack('!l', x[f:f+4])[0], f+4)
+
+def decode_intq(x, f):
+    f += 1
+    return (struct.unpack('!q', x[f:f+8])[0], f+8)
+
+def decode_float32(x, f):
+    f += 1
+    n = struct.unpack('!f', x[f:f+4])[0]
+    return (n, f+4)
+
+def decode_float64(x, f):
+    f += 1
+    n = struct.unpack('!d', x[f:f+8])[0]
+    return (n, f+8)
+
+def decode_string(x, f):
+    colon = x.index(':', f)
+    try:
+        n = int(x[f:colon])
+    except (OverflowError, ValueError):
+        n = long(x[f:colon])
+    if x[f] == '0' and colon != f+1:
+        raise ValueError
+    colon += 1
+    s = x[colon:colon+n]
+    try:
+        t = s.decode("utf8")
+        if len(t) != len(s):
+            s = t
+    except UnicodeDecodeError:
+        pass
+    return (s, colon+n)
+
+def decode_list(x, f):
+    r, f = [], f+1
+    while x[f] != CHR_TERM:
+        v, f = decode_func[x[f]](x, f)
+        r.append(v)
+    return (tuple(r), f + 1)
+
+def decode_dict(x, f):
+    r, f = {}, f+1
+    while x[f] != CHR_TERM:
+        k, f = decode_func[x[f]](x, f)
+        r[k], f = decode_func[x[f]](x, f)
+    return (r, f + 1)
+
+def decode_true(x, f):
+  return (True, f+1)
+
+def decode_false(x, f):
+  return (False, f+1)
+
+def decode_none(x, f):
+  return (None, f+1)
+
+decode_func = {}
+decode_func['0'] = decode_string
+decode_func['1'] = decode_string
+decode_func['2'] = decode_string
+decode_func['3'] = decode_string
+decode_func['4'] = decode_string
+decode_func['5'] = decode_string
+decode_func['6'] = decode_string
+decode_func['7'] = decode_string
+decode_func['8'] = decode_string
+decode_func['9'] = decode_string
+decode_func[CHR_LIST   ] = decode_list
+decode_func[CHR_DICT   ] = decode_dict
+decode_func[CHR_INT    ] = decode_int
+decode_func[CHR_INT1   ] = decode_intb
+decode_func[CHR_INT2   ] = decode_inth
+decode_func[CHR_INT4   ] = decode_intl
+decode_func[CHR_INT8   ] = decode_intq
+decode_func[CHR_FLOAT32] = decode_float32
+decode_func[CHR_FLOAT64] = decode_float64
+decode_func[CHR_TRUE   ] = decode_true
+decode_func[CHR_FALSE  ] = decode_false
+decode_func[CHR_NONE   ] = decode_none
+
+def make_fixed_length_string_decoders():
+    def make_decoder(slen):
+        def f(x, f):
+            s = x[f+1:f+1+slen]
+            try:
+                t = s.decode("utf8")
+                if len(t) != len(s):
+                    s = t
+            except UnicodeDecodeError:
+                pass
+            return (s, f+1+slen)
+        return f
+    for i in range(STR_FIXED_COUNT):
+        decode_func[chr(STR_FIXED_START+i)] = make_decoder(i)
+
+make_fixed_length_string_decoders()
+
+def make_fixed_length_list_decoders():
+    def make_decoder(slen):
+        def f(x, f):
+            r, f = [], f+1
+            for i in range(slen):
+                v, f = decode_func[x[f]](x, f)
+                r.append(v)
+            return (tuple(r), f)
+        return f
+    for i in range(LIST_FIXED_COUNT):
+        decode_func[chr(LIST_FIXED_START+i)] = make_decoder(i)
+
+make_fixed_length_list_decoders()
+
+def make_fixed_length_int_decoders():
+    def make_decoder(j):
+        def f(x, f):
+            return (j, f+1)
+        return f
+    for i in range(INT_POS_FIXED_COUNT):
+        decode_func[chr(INT_POS_FIXED_START+i)] = make_decoder(i)
+    for i in range(INT_NEG_FIXED_COUNT):
+        decode_func[chr(INT_NEG_FIXED_START+i)] = make_decoder(-1-i)
+
+make_fixed_length_int_decoders()
+
+def make_fixed_length_dict_decoders():
+    def make_decoder(slen):
+        def f(x, f):
+            r, f = {}, f+1
+            for j in range(slen):
+                k, f = decode_func[x[f]](x, f)
+                r[k], f = decode_func[x[f]](x, f)
+            return (r, f)
+        return f
+    for i in range(DICT_FIXED_COUNT):
+        decode_func[chr(DICT_FIXED_START+i)] = make_decoder(i)
+
+make_fixed_length_dict_decoders()
+
+def encode_dict(x,r):
+    r.append(CHR_DICT)
+    for k, v in x.items():
+        encode_func[type(k)](k, r)
+        encode_func[type(v)](v, r)
+    r.append(CHR_TERM)
+
+
+def loads(x):
+    try:
+        r, l = decode_func[x[0]](x, 0)
+    except (IndexError, KeyError):
+        raise ValueError
+    if l != len(x):
+        raise ValueError
+    return r
+
+from types import StringType, IntType, LongType, DictType, ListType, TupleType, FloatType, NoneType, UnicodeType
+
+def encode_int(x, r):
+    if 0 <= x < INT_POS_FIXED_COUNT:
+        r.append(chr(INT_POS_FIXED_START+x))
+    elif -INT_NEG_FIXED_COUNT <= x < 0:
+        r.append(chr(INT_NEG_FIXED_START-1-x))
+    elif -128 <= x < 128:
+        r.extend((CHR_INT1, struct.pack('!b', x)))
+    elif -32768 <= x < 32768:
+        r.extend((CHR_INT2, struct.pack('!h', x)))
+    elif -2147483648 <= x < 2147483648:
+        r.extend((CHR_INT4, struct.pack('!l', x)))
+    elif -9223372036854775808 <= x < 9223372036854775808:
+        r.extend((CHR_INT8, struct.pack('!q', x)))
+    else:
+        s = str(x)
+        if len(s) >= MAX_INT_LENGTH:
+            raise ValueError('overflow')
+        r.extend((CHR_INT, s, CHR_TERM))
+
+def encode_float32(x, r):
+    r.extend((CHR_FLOAT32, struct.pack('!f', x)))
+
+def encode_float64(x, r):
+    r.extend((CHR_FLOAT64, struct.pack('!d', x)))
+
+def encode_bool(x, r):
+    r.extend({False: CHR_FALSE, True: CHR_TRUE}[bool(x)])
+
+def encode_none(x, r):
+    r.extend(CHR_NONE)
+
+def encode_string(x, r):
+    if len(x) < STR_FIXED_COUNT:
+        r.extend((chr(STR_FIXED_START + len(x)), x))
+    else:
+        r.extend((str(len(x)), ':', x))
+
+def encode_unicode(x, r):
+    encode_string(x.encode("utf8"), r)
+
+def encode_list(x, r):
+    if len(x) < LIST_FIXED_COUNT:
+        r.append(chr(LIST_FIXED_START + len(x)))
+        for i in x:
+            encode_func[type(i)](i, r)
+    else:
+        r.append(CHR_LIST)
+        for i in x:
+            encode_func[type(i)](i, r)
+        r.append(CHR_TERM)
+
+def encode_dict(x,r):
+    if len(x) < DICT_FIXED_COUNT:
+        r.append(chr(DICT_FIXED_START + len(x)))
+        for k, v in x.items():
+            encode_func[type(k)](k, r)
+            encode_func[type(v)](v, r)
+    else:
+        r.append(CHR_DICT)
+        for k, v in x.items():
+            encode_func[type(k)](k, r)
+            encode_func[type(v)](v, r)
+        r.append(CHR_TERM)
+
+encode_func = {}
+encode_func[IntType] = encode_int
+encode_func[LongType] = encode_int
+encode_func[StringType] = encode_string
+encode_func[ListType] = encode_list
+encode_func[TupleType] = encode_list
+encode_func[DictType] = encode_dict
+encode_func[NoneType] = encode_none
+encode_func[UnicodeType] = encode_unicode
+
+lock = Lock()
+
+try:
+    from types import BooleanType
+    encode_func[BooleanType] = encode_bool
+except ImportError:
+    pass
+
+def dumps(x, float_bits=DEFAULT_FLOAT_BITS):
+    """
+    Dump data structure to str.
+
+    Here float_bits is either 32 or 64.
+    """
+    lock.acquire()
+    try:
+        if float_bits == 32:
+            encode_func[FloatType] = encode_float32
+        elif float_bits == 64:
+            encode_func[FloatType] = encode_float64
+        else:
+            raise ValueError('Float bits (%d) is not 32 or 64' % float_bits)
+        r = []
+        encode_func[type(x)](x, r)
+    finally:
+        lock.release()
+    return ''.join(r)
+
+def test():
+    f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0]
+    f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0]
+    f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0]
+    L = (({'a':15, 'bb':f1, 'ccc':f2, '':(f3,(),False,True,'')},('a',10**20),tuple(range(-100000,100000)),'b'*31,'b'*62,'b'*64,2**30,2**33,2**62,2**64,2**30,2**33,2**62,2**64,False,False, True, -1, 2, 0),)
+    assert loads(dumps(L)) == L
+    d = dict(zip(range(-100000,100000),range(-100000,100000)))
+    d.update({'a':20, 20:40, 40:41, f1:f2, f2:f3, f3:False, False:True, True:False})
+    L = (d, {}, {5:6}, {7:7,True:8}, {9:10, 22:39, 49:50, 44: ''})
+    assert loads(dumps(L)) == L
+    L = ('', 'a'*10, 'a'*100, 'a'*1000, 'a'*10000, 'a'*100000, 'a'*1000000, 'a'*10000000)
+    assert loads(dumps(L)) == L
+    L = tuple([dict(zip(range(n),range(n))) for n in range(100)]) + ('b',)
+    assert loads(dumps(L)) == L
+    L = tuple([dict(zip(range(n),range(-n,0))) for n in range(100)]) + ('b',)
+    assert loads(dumps(L)) == L
+    L = tuple([tuple(range(n)) for n in range(100)]) + ('b',)
+    assert loads(dumps(L)) == L
+    L = tuple(['a'*n for n in range(1000)]) + ('b',)
+    assert loads(dumps(L)) == L
+    L = tuple(['a'*n for n in range(1000)]) + (None,True,None)
+    assert loads(dumps(L)) == L
+    assert loads(dumps(None)) == None
+    assert loads(dumps({None:None})) == {None:None}
+    assert 1e-10<abs(loads(dumps(1.1))-1.1)<1e-6
+    assert 1e-10<abs(loads(dumps(1.1,32))-1.1)<1e-6
+    assert abs(loads(dumps(1.1,64))-1.1)<1e-12
+    assert loads(dumps(u"Hello World!!"))
+try:
+    import psyco
+    psyco.bind(dumps)
+    psyco.bind(loads)
+except ImportError:
+    pass
+
+
+if __name__ == '__main__':
+  test()
diff --git a/lib/synchronousdeluge/transfer.py b/lib/synchronousdeluge/transfer.py
new file mode 100644
index 0000000000000000000000000000000000000000..472bb125c493292336001bfc0bcee45ca0a2bdf4
--- /dev/null
+++ b/lib/synchronousdeluge/transfer.py
@@ -0,0 +1,57 @@
+import zlib
+import struct
+import socket
+import ssl
+
+from synchronousdeluge import rencode
+
+
+__all__ = ["DelugeTransfer"]
+
+class DelugeTransfer(object):
+    def __init__(self):
+        self.sock = None
+        self.conn = None
+        self.connected = False
+
+    def connect(self, hostport):
+        if self.connected:
+            self.disconnect()
+
+        self.sock = socket.create_connection(hostport)
+        self.conn = ssl.wrap_socket(self.sock, None, None, False, ssl.CERT_NONE, ssl.PROTOCOL_TLSv1)
+        self.connected = True
+
+    def disconnect(self):
+        if self.conn:
+            self.conn.close()
+            self.connected = False
+
+    def send_request(self, request):
+        data = (request.format(),)
+        payload = zlib.compress(rencode.dumps(data))
+        self.conn.sendall(payload)
+
+        buf = b""
+
+        while True:
+            data = self.conn.recv(1024)
+
+            if not data:
+                self.connected = False
+                break
+
+            buf += data
+            dobj = zlib.decompressobj()
+
+            try:
+                message = rencode.loads(dobj.decompress(buf))
+            except (ValueError, zlib.error, struct.error):
+                # Probably incomplete data, read more
+                continue
+            else:
+                buf = dobj.unused_data
+
+            yield message
+
+
diff --git a/readme.md b/readme.md
index 8f93e86656b32a17216667b4edcf479b336c3fb5..4779a9fc73dd58c2737f6d5695e6b7f7dc7edc4a 100644
--- a/readme.md
+++ b/readme.md
@@ -31,7 +31,7 @@ Automatic Video Library Manager for TV Shows. It watches for new episodes of you
 -[Mobile](http://imgur.com/a/WPyG6)
 
 ## Dependencies
- To run SickRage from source you will need Python 2.7.x, Cheetah 2.1.0+, and PyOpenSSL
+ To run SickRage from source you will need Python 2.7.x, Mako, and PyOpenSSL
 
 ## Forums
  Any questions or setup info your looking for can be found at out forums https://www.sickrage.tv
diff --git a/requirements.txt b/requirements.txt
index 0257d09bc2fd1718965df5fe4d5097a07178234f..4c95144c6133068d85e7e7d50074a6266f01a93e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-cheetah
+mako
 pyopenssl==0.13.1
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index eaa058492a1194949976e679250dc02c7a3feecb..ad34a71150232b98d2128a950d6fb73997d383eb 100644
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -34,26 +34,26 @@ import sys
 
 from github import Github
 
-from sickbeard import providers, metadata, config, webserveInit
-from sickbeard.providers.generic import GenericProvider
-from providers import btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
-    omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, hounddawgs, nextgen, speedcd, nyaatorrents, animenzb, bluetigers, cpasbien, fnt, xthor, torrentbytes, animezb, \
+from . import providers, metadata, config, webserveInit
+from .providers.generic import GenericProvider
+from .providers import btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
+    omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, hounddawgs, nextgen, speedcd, nyaatorrents, animenzb, bluetigers, cpasbien, fnt, xthor, torrentbytes, \
     frenchtorrentdb, freshontv, titansoftv, libertalia, morethantv, bitsoup, t411, tokyotoshokan, shazbat, rarbg, alpharatio, tntvillage, binsearch, scenetime, btdigg
-from sickbeard.config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \
+from .config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \
     naming_ep_type
-from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \
+from . import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \
     subtitles, traktChecker
-from sickbeard import helpers, db, exceptions, show_queue, search_queue, scheduler, show_name_helpers
-from sickbeard import logger
-from sickbeard import naming
-from sickbeard import dailysearcher
-from sickbeard import scene_numbering, scene_exceptions, name_cache
-from indexers.indexer_api import indexerApi
-from indexers.indexer_exceptions import indexer_shownotfound, indexer_showincomplete, indexer_exception, indexer_error, \
+from . import helpers, db, exceptions, show_queue, search_queue, scheduler, show_name_helpers
+from . import logger
+from . import naming
+from . import dailysearcher
+from . import scene_numbering, scene_exceptions, name_cache
+from .indexers.indexer_api import indexerApi
+from .indexers.indexer_exceptions import indexer_shownotfound, indexer_showincomplete, indexer_exception, indexer_error, \
     indexer_episodenotfound, indexer_attributenotfound, indexer_seasonnotfound, indexer_userabort, indexerExcepts
-from sickbeard.common import SD, SKIPPED, WANTED, NAMING_REPEAT
-from sickbeard.databases import mainDB, cache_db, failed_db
-from sickbeard.helpers import ex
+from .common import SD, SKIPPED, WANTED, NAMING_REPEAT
+from .databases import mainDB, cache_db, failed_db
+from .helpers import ex
 
 from configobj import ConfigObj
 
@@ -373,6 +373,8 @@ TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD = False
 TWITTER_USERNAME = None
 TWITTER_PASSWORD = None
 TWITTER_PREFIX = None
+TWITTER_DMTO = None
+TWITTER_USEDM = False
 
 USE_BOXCAR = False
 BOXCAR_NOTIFY_ONSNATCH = False
@@ -395,6 +397,7 @@ PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD = False
 PUSHOVER_USERKEY = None
 PUSHOVER_APIKEY = None
 PUSHOVER_DEVICE = None
+PUSHOVER_SOUND = None
 
 USE_LIBNOTIFY = False
 LIBNOTIFY_NOTIFY_ONSNATCH = False
@@ -426,6 +429,8 @@ SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH = False
 SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD = False
 SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = False
 
+USE_IMDB_POPULAR = True
+
 USE_TRAKT = False
 TRAKT_USERNAME = None
 TRAKT_ACCESS_TOKEN = None
@@ -560,7 +565,7 @@ def initialize(consoleLogging=True):
             TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, TORRENT_RPCURL, TORRENT_AUTH_TYPE, \
             USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \
             KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, BACKLOG_FREQUENCY, \
-            USE_TRAKT, TRAKT_USERNAME, TRAKT_ACCESS_TOKEN, TRAKT_REFRESH_TOKEN, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_REMOVE_SHOW_FROM_SICKRAGE, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, traktRollingScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_SYNC_REMOVE, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_TIMEOUT, TRAKT_BLACKLIST_NAME, TRAKT_USE_ROLLING_DOWNLOAD, TRAKT_ROLLING_NUM_EP, TRAKT_ROLLING_ADD_PAUSED, TRAKT_ROLLING_FREQUENCY, \
+            USE_TRAKT, TRAKT_USERNAME, TRAKT_ACCESS_TOKEN, TRAKT_REFRESH_TOKEN, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_REMOVE_SHOW_FROM_SICKRAGE, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, traktRollingScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_SYNC_REMOVE, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_TIMEOUT, TRAKT_BLACKLIST_NAME, TRAKT_USE_ROLLING_DOWNLOAD, TRAKT_ROLLING_NUM_EP, TRAKT_ROLLING_ADD_PAUSED, TRAKT_ROLLING_FREQUENCY, USE_IMDB_POPULAR, \
             USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, USE_PLEX_CLIENT, PLEX_CLIENT_USERNAME, PLEX_CLIENT_PASSWORD, \
             PLEX_SERVER_HOST, PLEX_SERVER_TOKEN, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
             USE_EMBY, EMBY_HOST, EMBY_APIKEY, \
@@ -579,10 +584,10 @@ def initialize(consoleLogging=True):
             NAMING_PATTERN, NAMING_MULTI_EP, NAMING_ANIME_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, NAMING_SPORTS_PATTERN, NAMING_CUSTOM_SPORTS, NAMING_ANIME_PATTERN, NAMING_CUSTOM_ANIME, NAMING_STRIP_YEAR, \
             RENAME_EPISODES, AIRDATE_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \
             WOMBLE, BINSEARCH, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \
-            EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, DAILYSEARCH_FREQUENCY, \
+            EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, DAILYSEARCH_FREQUENCY, TWITTER_DMTO, TWITTER_USEDM, \
             USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \
             USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, \
-            USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_DEVICE, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, \
+            USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_DEVICE, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, PUSHOVER_SOUND, \
             USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \
             USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
             USE_EMAIL, EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_FROM, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_LIST, \
@@ -638,7 +643,7 @@ def initialize(consoleLogging=True):
 
         # debugging
         DEBUG = bool(check_setting_int(CFG, 'General', 'debug', 0))
-        
+
         DEFAULT_PAGE = check_setting_str(CFG, 'General', 'default_page', 'home')
 
         ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs')
@@ -829,7 +834,7 @@ def initialize(consoleLogging=True):
             NZB_METHOD = 'blackhole'
 
         TORRENT_METHOD = check_setting_str(CFG, 'General', 'torrent_method', 'blackhole')
-        if TORRENT_METHOD not in ('blackhole', 'utorrent', 'transmission', 'deluge', 'download_station', 'rtorrent', 'qbittorrent'):
+        if TORRENT_METHOD not in ('blackhole', 'utorrent', 'transmission', 'deluge', 'deluged', 'download_station', 'rtorrent', 'qbittorrent'):
             TORRENT_METHOD = 'blackhole'
 
         DOWNLOAD_PROPERS = bool(check_setting_int(CFG, 'General', 'download_propers', 1))
@@ -989,6 +994,8 @@ def initialize(consoleLogging=True):
         TWITTER_USERNAME = check_setting_str(CFG, 'Twitter', 'twitter_username', '', censor_log=True)
         TWITTER_PASSWORD = check_setting_str(CFG, 'Twitter', 'twitter_password', '', censor_log=True)
         TWITTER_PREFIX = check_setting_str(CFG, 'Twitter', 'twitter_prefix', 'SickRage')
+        TWITTER_DMTO = check_setting_str(CFG, 'Twitter', 'twitter_dmto', '')
+        TWITTER_USEDM = bool(check_setting_int(CFG, 'Twitter', 'twitter_usedm', 0))
 
         USE_BOXCAR = bool(check_setting_int(CFG, 'Boxcar', 'use_boxcar', 0))
         BOXCAR_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_notify_onsnatch', 0))
@@ -1009,6 +1016,7 @@ def initialize(consoleLogging=True):
         PUSHOVER_USERKEY = check_setting_str(CFG, 'Pushover', 'pushover_userkey', '', censor_log=True)
         PUSHOVER_APIKEY = check_setting_str(CFG, 'Pushover', 'pushover_apikey', '', censor_log=True)
         PUSHOVER_DEVICE = check_setting_str(CFG, 'Pushover', 'pushover_device', '')
+        PUSHOVER_SOUND = check_setting_str(CFG, 'Pushover', 'pushover_sound', 'pushover')
 
         USE_LIBNOTIFY = bool(check_setting_int(CFG, 'Libnotify', 'use_libnotify', 0))
         LIBNOTIFY_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Libnotify', 'libnotify_notify_onsnatch', 0))
@@ -1057,7 +1065,9 @@ def initialize(consoleLogging=True):
         TRAKT_ROLLING_FREQUENCY = check_setting_int(CFG, 'Trakt', 'trakt_rolling_frequency', 8)
         if TRAKT_ROLLING_FREQUENCY < 4:
             TRAKT_ROLLING_FREQUENCY = 4
-       
+
+        USE_IMDB_POPULAR = bool(check_setting_int(CFG, 'IMDB', 'use_imdb_popular', 1))
+
         USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0))
         PYTIVO_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_onsnatch', 0))
         PYTIVO_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_ondownload', 0))
@@ -1963,6 +1973,8 @@ def save_config():
     new_config['Twitter']['twitter_username'] = TWITTER_USERNAME
     new_config['Twitter']['twitter_password'] = helpers.encrypt(TWITTER_PASSWORD, ENCRYPTION_VERSION)
     new_config['Twitter']['twitter_prefix'] = TWITTER_PREFIX
+    new_config['Twitter']['twitter_dmto'] = TWITTER_DMTO
+    new_config['Twitter']['twitter_usedm'] = int(TWITTER_USEDM)
 
     new_config['Boxcar'] = {}
     new_config['Boxcar']['use_boxcar'] = int(USE_BOXCAR)
@@ -1986,6 +1998,7 @@ def save_config():
     new_config['Pushover']['pushover_userkey'] = PUSHOVER_USERKEY
     new_config['Pushover']['pushover_apikey'] = PUSHOVER_APIKEY
     new_config['Pushover']['pushover_device'] = PUSHOVER_DEVICE
+    new_config['Pushover']['pushover_sound]'] = PUSHOVER_SOUND
 
     new_config['Libnotify'] = {}
     new_config['Libnotify']['use_libnotify'] = int(USE_LIBNOTIFY)
@@ -2037,6 +2050,9 @@ def save_config():
     new_config['Trakt']['trakt_rolling_add_paused'] = int(TRAKT_ROLLING_ADD_PAUSED)
     new_config['Trakt']['trakt_rolling_frequency'] = int(TRAKT_ROLLING_FREQUENCY)
 
+    new_config['IMDB'] = {}
+    new_config['IMDB']['use_imdb_popular'] = int(USE_IMDB_POPULAR)
+
     new_config['pyTivo'] = {}
     new_config['pyTivo']['use_pytivo'] = int(USE_PYTIVO)
     new_config['pyTivo']['pytivo_notify_onsnatch'] = int(PYTIVO_NOTIFY_ONSNATCH)
@@ -2108,7 +2124,7 @@ def save_config():
     new_config['GUI']['poster_sortby'] = POSTER_SORTBY
     new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR
     new_config['GUI']['filter_row'] = int(FILTER_ROW)
-   
+
     new_config['Subtitles'] = {}
     new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES)
     new_config['Subtitles']['subtitles_languages'] = ','.join(SUBTITLES_LANGUAGES)
diff --git a/sickbeard/clients/__init__.py b/sickbeard/clients/__init__.py
index 38c67c71e15cc933bdb010d8be82259d9423052a..ce1b7bbddb4c1c7e7e8535b0090855af103f594b 100644
--- a/sickbeard/clients/__init__.py
+++ b/sickbeard/clients/__init__.py
@@ -19,6 +19,7 @@
 __all__ = ['utorrent',
            'transmission',
            'deluge',
+           'deluged',
            'download_station',
            'rtorrent',
            'qbittorrent'
@@ -101,6 +102,7 @@ http_error_code = {
 default_host = {'utorrent': 'http://localhost:8000',
                 'transmission': 'http://localhost:9091',
                 'deluge': 'http://localhost:8112',
+                'deluged': 'scgi://localhost:58846',
                 'download_station': 'http://localhost:5000',
                 'rtorrent': 'scgi://localhost:5000',
                 'qbittorrent': 'http://localhost:8080'
diff --git a/sickbeard/clients/deluged_client.py b/sickbeard/clients/deluged_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..800a2786a619c96a754a01cec8e1ab5290c8243d
--- /dev/null
+++ b/sickbeard/clients/deluged_client.py
@@ -0,0 +1,216 @@
+# Author: Paul Wollaston
+#
+# This client script allows connection to Deluge Daemon directly, completely
+# circumventing the requirement to use the WebUI.
+
+import json
+from base64 import b64encode
+
+import sickbeard
+from sickbeard import logger
+from .generic import GenericClient
+from synchronousdeluge import DelugeClient
+
+class DelugeDAPI(GenericClient):
+
+    drpc = None
+
+    def __init__(self, host=None, username=None, password=None):
+        super(DelugeDAPI, self).__init__('DelugeD', host, username, password)
+
+    def _get_auth(self):
+        if not self.connect():
+            return None
+
+        return True
+
+    def connect(self, reconnect = False):
+        hostname = self.host.replace("/", "").split(':')
+
+        if not self.drpc or reconnect:
+            self.drpc = DelugeRPC(hostname[1], port = hostname[2], username = self.username, password = self.password)
+
+        return self.drpc
+
+    def _add_torrent_uri(self, result):
+        label = sickbeard.TORRENT_LABEL
+        if result.show.is_anime:
+            label = sickbeard.TORRENT_LABEL_ANIME
+
+        options = {
+            'add_paused': sickbeard.TORRENT_PAUSED
+        }
+
+        remote_torrent = self.drpc.add_torrent_magnet(result.url, options)
+
+        if not remote_torrent:
+            return None
+
+        result.hash = remote_torrent
+
+        return remote_torrent
+
+    def _add_torrent_file(self, result):
+        label = sickbeard.TORRENT_LABEL
+        if result.show.is_anime:
+            label = sickbeard.TORRENT_LABEL_ANIME
+
+        if not result.content: result.content = {}
+
+        if not result.content:
+            return None
+
+        options = {
+            'add_paused': sickbeard.TORRENT_PAUSED
+        }
+
+        remote_torrent = self.drpc.add_torrent_file(result.name + '.torrent', result.content, options)
+
+        if not remote_torrent:
+            return None
+
+        result.hash = remote_torrent
+
+        return remote_torrent
+
+    def _set_torrent_label(self, result):
+
+        label = sickbeard.TORRENT_LABEL
+        if result.show.is_anime:
+            label = sickbeard.TORRENT_LABEL_ANIME
+        if ' ' in label:
+            logger.log(self.name + u': Invalid label. Label must not contain a space', logger.ERROR)
+            return False
+
+        if label:
+            if self.drpc.set_torrent_label(result.hash, label):
+                return True
+
+        return False
+
+    def _set_torrent_ratio(self, result):
+
+        return True
+
+    def _set_torrent_path(self, result):
+
+        return True
+
+    def _set_torrent_pause(self, result):
+
+        if sickbeard.TORRENT_PAUSED:
+            return self.drpc.pause_torrent(result.hash)
+        return True
+
+    def testAuthentication(self):
+        print "Test Auth"
+        if self.connect(True) and self.drpc.test():
+            return True, 'Success: Connected and Authenticated'
+        else:
+            return False, 'Error: Unable to Authenticate!  Please check your config!'
+
+class DelugeRPC(object):
+
+    host = 'localhost'
+    port = 58846
+    username = None
+    password = None
+    client = None
+
+    def __init__(self, host = 'localhost', port = 58846, username = None, password = None):
+        super(DelugeRPC, self).__init__()
+
+        self.host = host
+        self.port = port
+        self.username = username
+        self.password = password
+
+    def connect(self):
+        self.client = DelugeClient()
+        self.client.connect(self.host, int(self.port), self.username, self.password)
+
+    def test(self):
+        try:
+            self.connect()
+        except:
+            return False
+        return True
+
+    def add_torrent_magnet(self, torrent, options):
+        torrent_id = False
+        try:
+            self.connect()
+            torrent_id = self.client.core.add_torrent_magnet(torrent, options).get()
+            if not torrent_id:
+                torrent_id = self._check_torrent(True, torrent)
+        except Exception as err:
+            print "ERRERR: %s" % err
+            return False
+        finally:
+            if self.client:
+                self.disconnect()
+
+        return torrent_id
+
+    def add_torrent_file(self, filename, torrent, options):
+        torrent_id = False
+        try:
+            self.connect()
+            torrent_id = self.client.core.add_torrent_file(filename, b64encode(torrent), options).get()
+            if not torrent_id:
+                torrent_id = self._check_torrent(False, torrent)
+        except Exception as err:
+            return False
+        finally:
+            if self.client:
+                self.disconnect()
+
+        return torrent_id
+
+    def set_torrent_label(self, torrent_id, label):
+        try:
+            self.connect()
+            self.client.label.set_torrent(torrent_id, label).get()
+        except Exception as err:
+            logger.log('DelugeD: Failed to set label for torrent: ' + err + ' ' + traceback.format_exc(), logger.ERROR)
+            return False
+        finally:
+            if self.client:
+                self.disconnect()
+        return True
+
+    def pause_torrent(self, torrent_ids):
+        try:
+            self.connect()
+            self.client.core.pause_torrent(torrent_ids).get()
+        except Exception as err:
+            logger.log('DelugeD: Failed to pause torrent: ' + err + ' ' + traceback.format_exc(), logger.ERROR)
+            return False
+        finally:
+            if self.client:
+                self.disconnect()
+        return True
+
+    def disconnect(self):
+        self.client.disconnect()
+
+    def _check_torrent(self, magnet, torrent):
+        # Torrent not added, check if it already existed.
+        if magnet:
+            torrent_hash = re.findall('urn:btih:([\w]{32,40})', torrent)[0]
+        else:
+            info = bdecode(torrent)["info"]
+            torrent_hash = sha1(benc(info)).hexdigest()
+
+        # Convert base 32 to hex
+        if len(torrent_hash) == 32:
+            torrent_hash = b16encode(b32decode(torrent_hash))
+
+        torrent_hash = torrent_hash.lower()
+        torrent_check = self.client.core.get_torrent_status(torrent_hash, {}).get()
+        if torrent_check['hash']:
+            return torrent_hash
+
+        return False
+
+api = DelugeDAPI()
diff --git a/sickbeard/common.py b/sickbeard/common.py
index 8946ad6b6ea3e1d045d45f6ac9a1b05c65ed0b19..4544a7df05206c50d92491ca3016c81e7b0753f3 100644
--- a/sickbeard/common.py
+++ b/sickbeard/common.py
@@ -115,7 +115,7 @@ class Quality:
     RAWHDTV = 1 << 3  # 8  -- 720p/1080i mpeg2 (trollhd releases)
     FULLHDTV = 1 << 4  # 16 -- 1080p HDTV (QCF releases)
     HDWEBDL = 1 << 5  # 32
-    FULLHDWEBDL = 1 << 6  # 64 -- 1080p web-dl                        
+    FULLHDWEBDL = 1 << 6  # 64 -- 1080p web-dl
     HDBLURAY = 1 << 7  # 128
     FULLHDBLURAY = 1 << 8  # 256
 
@@ -199,7 +199,7 @@ class Quality:
     @staticmethod
     def sceneQuality(name, anime=False):
         """
-        Return The quality from the scene episode File 
+        Return The quality from the scene episode File
         """
         if not name:
             return Quality.UNKNOWN
@@ -259,6 +259,10 @@ class Quality:
 
     @staticmethod
     def assumeQuality(name):
+        quality = Quality.qualityFromFileMeta(name)
+        if quality != Quality.UNKNOWN:
+            return quality
+
         if name.lower().endswith((".avi", ".mp4")):
             return Quality.SDTV
         elif name.lower().endswith(".ts"):
@@ -266,6 +270,46 @@ class Quality:
         else:
             return Quality.UNKNOWN
 
+    @staticmethod
+    def qualityFromFileMeta(filename):
+        from hachoir_parser import createParser
+        from hachoir_metadata import extractMetadata
+
+        parser = createParser(filename)
+        if not parser:
+            return Quality.UNKNOWN
+
+        try:
+            metadata = extractMetadata(parser)
+        except Exception:
+            metadata = None
+            pass
+
+        if not metadata:
+            return Quality.UNKNOWN
+
+        height = 0
+        if metadata.has('height'):
+            height = int(metadata.get('height') or 0)
+        else:
+            test = getattr(metadata, "iterGroups", None)
+            if callable(test):
+                for metagroup in metadata.iterGroups():
+                    if metagroup.has('height'):
+                        height = int(metagroup.get('height') or 0)
+
+        if not height:
+            return Quality.UNKNOWN
+
+        if height > 1040:
+            return Quality.FULLHDTV
+        elif height > 680 and height < 760:
+            return Quality.HDTV
+        elif height < 680:
+            return Quality.SDTV
+
+        return Quality.UNKNOWN
+
     @staticmethod
     def compositeStatus(status, quality):
         return status + 100 * quality
@@ -316,7 +360,7 @@ ANY = Quality.combineQualities(
     [Quality.SDTV, Quality.SDDVD, Quality.HDTV, Quality.FULLHDTV, Quality.HDWEBDL, Quality.FULLHDWEBDL,
      Quality.HDBLURAY, Quality.FULLHDBLURAY, Quality.UNKNOWN], [])  # SD + HD
 
-# legacy template, cant remove due to reference in mainDB upgrade?                                                                                                                                        
+# legacy template, cant remove due to reference in mainDB upgrade?
 BEST = Quality.combineQualities([Quality.SDTV, Quality.HDTV, Quality.HDWEBDL], [Quality.HDTV])
 
 qualityPresets = (SD, HD, HD720p, HD1080p, ANY)
@@ -384,4 +428,3 @@ countryList = {'Australia': 'AU',
                'Canada': 'CA',
                'USA': 'US'
 }
-
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index e496bbdc12a519cedd10cf1c80366877c6fc589f..8d2b0b159cb6e99009987c57d7dcf8fbd82cb1cf 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -39,6 +39,8 @@ import datetime
 import errno
 import ast
 import operator
+import platform
+import sys
 from contextlib import closing
 
 import sickbeard
@@ -1628,3 +1630,20 @@ def isFileLocked(file, writeLockCheck=False):
             return True
            
     return False
+
+def getDiskSpaceUsage(diskPath=None):
+    '''
+    returns the free space in MB for a given path or False if no path given
+    @param diskPath: the filesystem path being checked
+    '''
+
+    if diskPath:
+        if platform.system() == 'Windows':
+            free_bytes = ctypes.c_ulonglong(0)
+            ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(diskPath), None, None, ctypes.pointer(free_bytes))
+            return free_bytes.value / 1024 / 1024
+        else:
+            st = os.statvfs(diskPath)
+            return st.f_bavail * st.f_frsize / 1024 / 1024
+    else:
+        return False
diff --git a/sickbeard/imdbPopular.py b/sickbeard/imdbPopular.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b6396675c5f3f617662c8ecd685698e998fa9fe
--- /dev/null
+++ b/sickbeard/imdbPopular.py
@@ -0,0 +1,54 @@
+import requests
+from bs4 import BeautifulSoup
+import re
+from datetime import date
+
+url = "http://www.imdb.com/search/title?at=0&sort=moviemeter&title_type=tv_series&year=%s,%s" % \
+      (date.today().year - 1, date.today().year + 1)
+
+def fetch_popular_shows():
+    popular_shows = []
+
+    r = requests.get(url)
+    if r.status_code == 200:
+        soup = BeautifulSoup(r.text)
+        results = soup.find("table", {"class": "results"})
+        rows = results.find_all("tr");
+
+        for row in rows:
+            show = {}
+            image_td = row.find("td", {"class": "image"})
+
+            if image_td:
+                image = image_td.find("img")
+                show['image_url'] =  image['src']
+                show['image_url_large'] = show['image_url'].replace(
+                    "V1._SX54_CR0,0,54,74_.jpg","V1._SX108_CR0,0,108,148_.jpg")
+
+            td = row.find("td", {"class": "title"})
+            if td:
+
+                show['name'] = td.find("a").contents[0]
+                show['imdb_url'] = "http://www.imdb.com" + td.find("a")["href"]
+                show['year'] = td.find("span", {"class": "year_type"}).contents[0].split(" ")[0][1:]
+
+                rating_all = td.find("div", {"class": "user_rating"})
+                if rating_all:
+                    rating_string = rating_all.find("div", {"class": "rating rating-list"})
+                    if rating_string:
+                        rating_string = rating_string['title']
+
+                        matches = re.search(".* (.*)\/10.*\((.*)\).*", rating_string).groups()
+                        show['rating'] = matches[0]
+                        show['votes'] = matches[1]
+
+                else:
+                    show['rating'] = None
+                    show['votes'] = None
+
+                show['outline'] = td.find("span", {"class": "outline"}).contents[0]
+                popular_shows.append(show)
+
+        return popular_shows
+    else:
+        return None
diff --git a/sickbeard/metadata/generic.py b/sickbeard/metadata/generic.py
index 204855b213d2b5e251c70ddd2d82b8530072c322..672faa8264bd876e71f6e7833b7b94ba597ca951 100644
--- a/sickbeard/metadata/generic.py
+++ b/sickbeard/metadata/generic.py
@@ -709,9 +709,9 @@ class GenericMetadata():
             return False
 
         image_dir = ek.ek(os.path.dirname, image_path)
-        
+
         if not image_data:
-            logger.log(u"Unable to retrieve image to %s to save in %s, skipping" % ( ek.ss(obj.prettyName()), ek.ss(image_dir) ), logger.WARNING)
+            logger.log(u"Unable to retrieve image to %s to save in %s, skipping" % ( ek.ss(obj.prettyName() if obj else "(obj is None)"), ek.ss(image_path) ), logger.WARNING)
             return False
 
         try:
@@ -763,7 +763,7 @@ class GenericMetadata():
         except (sickbeard.indexer_error, IOError), e:
             logger.log(u"Unable to look up show on " + sickbeard.indexerApi(
                 show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING)
-            logger.log(u"Indexer " + sickbeard.indexerApi(show_obj.indexer).name + "maybe experiencing some problems. Try again later", logger.DEBUG)                
+            logger.log(u"Indexer " + sickbeard.indexerApi(show_obj.indexer).name + "maybe experiencing some problems. Try again later", logger.DEBUG)
             return None
 
         if image_type not in ('fanart', 'poster', 'banner', 'poster_thumb', 'banner_thumb'):
diff --git a/sickbeard/notifiers/emailnotify.py b/sickbeard/notifiers/emailnotify.py
index 8733edf1562c8b6f98444b1d95e97845a9d9341f..7e95b4d3348735673b8ad4c451abb6ccdd4bd16c 100644
--- a/sickbeard/notifiers/emailnotify.py
+++ b/sickbeard/notifiers/emailnotify.py
@@ -58,9 +58,9 @@ class EmailNotifier:
 
         if sickbeard.EMAIL_NOTIFY_ONSNATCH:
             show = self._parseEp(ep_name)
-            to = self._generate_recepients(show)
+            to = self._generate_recipients(show)
             if len(to) == 0:
-                logger.log('Skipping email notify because there are no configured recepients', logger.WARNING)
+                logger.log('Skipping email notify because there are no configured recipients', logger.WARNING)
             else:
                 try:
                     msg = MIMEMultipart('alternative')
@@ -97,9 +97,9 @@ class EmailNotifier:
 
         if sickbeard.EMAIL_NOTIFY_ONDOWNLOAD:
             show = self._parseEp(ep_name)
-            to = self._generate_recepients(show)
+            to = self._generate_recipients(show)
             if len(to) == 0:
-                logger.log('Skipping email notify because there are no configured recepients', logger.WARNING)
+                logger.log('Skipping email notify because there are no configured recipients', logger.WARNING)
             else:
                 try:
                     msg = MIMEMultipart('alternative')
@@ -136,9 +136,9 @@ class EmailNotifier:
 
         if sickbeard.EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD:
             show = self._parseEp(ep_name)
-            to = self._generate_recepients(show)
+            to = self._generate_recipients(show)
             if len(to) == 0:
-                logger.log('Skipping email notify because there are no configured recepients', logger.WARNING)
+                logger.log('Skipping email notify because there are no configured recipients', logger.WARNING)
             else:
                 try:
                     msg = MIMEMultipart('alternative')
@@ -167,7 +167,7 @@ class EmailNotifier:
     def notify_git_update(self, new_version="??"):
         pass
 
-    def _generate_recepients(self, show):
+    def _generate_recipients(self, show):
         addrs = []
 
         # Grab the global recipients
@@ -185,7 +185,7 @@ class EmailNotifier:
                             addrs.append(addr)
 
         addrs = set(addrs)
-        logger.log('Notification recepients: %s' % addrs, logger.DEBUG)
+        logger.log('Notification recipients: %s' % addrs, logger.DEBUG)
         return addrs
 
     def _sendmail(self, host, port, smtp_from, use_tls, user, pwd, to, msg, smtpDebug=False):
diff --git a/sickbeard/notifiers/pushover.py b/sickbeard/notifiers/pushover.py
index 31fd191fcd70f9ee3461245ca6902c3257340b38..2eab3191a1938471b9264f30b1790e4233ceb453 100644
--- a/sickbeard/notifiers/pushover.py
+++ b/sickbeard/notifiers/pushover.py
@@ -33,13 +33,14 @@ class PushoverNotifier:
     def test_notify(self, userKey=None, apiKey=None):
         return self._notifyPushover("This is a test notification from SickRage", 'Test', userKey, apiKey, force=True)
 
-    def _sendPushover(self, msg, title, userKey=None, apiKey=None):
+    def _sendPushover(self, msg, title, sound=None, userKey=None, apiKey=None):
         """
         Sends a pushover notification to the address provided
         
         msg: The message to send (unicode)
         title: The title of the message
         userKey: The pushover user id to send the message to (or to subscribe with)
+        sound: The notification sound to use
         
         returns: True if the message succeeded, False otherwise
         """
@@ -50,6 +51,9 @@ class PushoverNotifier:
         if apiKey == None:
             apiKey = sickbeard.PUSHOVER_APIKEY
 
+        if sound == None:
+            sound = sickbeard.PUSHOVER_SOUND
+
         logger.log("Pushover API KEY in use: " + apiKey, logger.DEBUG)
 
         # build up the URL and parameters
@@ -65,6 +69,7 @@ class PushoverNotifier:
                     "timestamp": int(time.time()),
                     "retry": 60,
                     "expire": 3600,
+                    "sound": sound,
                    }
 
             if sickbeard.PUSHOVER_DEVICE:
@@ -93,10 +98,10 @@ class PushoverNotifier:
                 #HTTP status 401 if the user doesn't have the service added
                 subscribeNote = self._sendPushover(msg, title, userKey, apiKey)
                 if subscribeNote:
-                    logger.log("Subscription send", logger.DEBUG)
+                    logger.log("Subscription sent", logger.DEBUG)
                     return True
                 else:
-                    logger.log("Subscription could not be send", logger.ERROR)
+                    logger.log("Subscription could not be sent", logger.ERROR)
                     return False
 
             # If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
@@ -131,13 +136,14 @@ class PushoverNotifier:
             title=notifyStrings[NOTIFY_GIT_UPDATE]
             self._notifyPushover(title, update_text + new_version) 
 
-    def _notifyPushover(self, title, message, userKey=None, apiKey=None, force=False):
+    def _notifyPushover(self, title, message, sound=None, userKey=None, apiKey=None, force=False):
         """
         Sends a pushover notification based on the provided info or SB config
 
         title: The title of the notification to send
         message: The message string to send
         userKey: The userKey to send the notification to 
+        sound: The notification sound to use
         force: Enforce sending, for instance for testing
         """
 
diff --git a/sickbeard/notifiers/tweet.py b/sickbeard/notifiers/tweet.py
index f5cbc7c78ea3d6675cd918c1952676890b68c374..befee7936d9181519d0d5584c02dcb3c155ac938 100644
--- a/sickbeard/notifiers/tweet.py
+++ b/sickbeard/notifiers/tweet.py
@@ -135,13 +135,35 @@ class TwitterNotifier:
 
         return True
 
+    def _send_dm(self, message=None):
+
+        username = self.consumer_key
+        password = self.consumer_secret
+        dmdest = sickbeard.TWITTER_DMTO
+        access_token_key = sickbeard.TWITTER_USERNAME
+        access_token_secret = sickbeard.TWITTER_PASSWORD
+
+        logger.log(u"Sending DM: " + dmdest + " " + message, logger.DEBUG)
+
+        api = twitter.Api(username, password, access_token_key, access_token_secret)
+
+        try:
+            api.PostDirectMessage(dmdest, message.encode('utf8'))
+        except Exception, e:
+            logger.log(u"Error Sending Tweet (DM): " + ex(e), logger.ERROR)
+            return False
+
+        return True
+
     def _notifyTwitter(self, message='', force=False):
         prefix = sickbeard.TWITTER_PREFIX
 
         if not sickbeard.USE_TWITTER and not force:
             return False
 
-        return self._send_tweet(prefix + ": " + message)
-
+        if sickbeard.TWITTER_USEDM and sickbeard.TWITTER_DMTO:
+            return self._send_dm(self, sickbeard.TWITTER_DMTO, prefix + ": " + message)
+        else:
+            return self._send_tweet(prefix + ": " + message)
 
 notifier = TwitterNotifier
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index 36e16e6254fca8996ff12c89a5e6785dad386492..480a40a9f045a863f18cfce4c1a15715de06d327 100644
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -94,7 +94,7 @@ class PostProcessor(object):
         self.is_priority = is_priority
 
         self.log = ''
-        
+
         self.version = None
 
     def _log(self, message, level=logger.INFO):
@@ -182,21 +182,21 @@ class PostProcessor(object):
 
         # don't confuse glob with chars we didn't mean to use
         base_name = re.sub(r'[\[\]\*\?]', r'[\g<0>]', base_name)
-        
+
         if subfolders: # subfolders are only checked in show folder, so names will always be exactly alike
             filelist = ek.ek(recursive_glob, ek.ek(os.path.dirname, file_path), base_name + '*') # just create the list of all files starting with the basename
         else: # this is called when PP, so we need to do the filename check case-insensitive
             filelist = []
-                
+
             checklist = ek.ek(glob.glob, helpers.fixGlob(ek.ek(os.path.join, ek.ek(os.path.dirname, file_path), '*'))) # get a list of all the files in the folder
             for filefound in checklist: # loop through all the files in the folder, and check if they are the same name even when the cases don't match
                 file_name = filefound.rpartition('.')[0]
                 if not base_name_only:
                     file_name = file_name + '.'
                 if file_name.lower() == base_name.lower(): # if there's no difference in the filename add it to the filelist
-                    filelist.append(filefound) 
-             
-                             
+                    filelist.append(filefound)
+
+
         for associated_file_path in filelist:
             # only add associated to list
             if associated_file_path == file_path:
@@ -211,12 +211,12 @@ class PostProcessor(object):
 
             if ek.ek(os.path.isfile, associated_file_path):
                 file_path_list.append(associated_file_path)
-        
+
         if file_path_list:
             self._log(u"Found the following associated files: " + str(file_path_list), logger.DEBUG)
         else:
             self._log(u"No associated files were during this pass", logger.DEBUG)
-            
+
         return file_path_list
 
     def _delete(self, file_path, associated_files=False):
@@ -730,7 +730,7 @@ class PostProcessor(object):
                 return ep_quality
 
         # Try guessing quality from the file name
-        ep_quality = common.Quality.assumeQuality(self.file_name)
+        ep_quality = common.Quality.assumeQuality(self.file_path)
         self._log(
             u"Guessing quality for name " + self.file_name + u", got " + common.Quality.qualityStrings[ep_quality],
             logger.DEBUG)
@@ -802,7 +802,7 @@ class PostProcessor(object):
                 self._log(u"SB snatched this episode and it is a proper of equal or higher quality so I'm marking it as priority", logger.DEBUG)
                 return True
             return False
-            
+
         # if the user downloaded it manually and it's higher quality than the existing episode then it's priority
         if new_ep_quality > old_ep_quality and new_ep_quality != common.Quality.UNKNOWN:
             self._log(
@@ -909,7 +909,7 @@ class PostProcessor(object):
             self._log(
                 u"This download is marked a priority download so I'm going to replace an existing file if I find one",
                 logger.DEBUG)
-        
+
         # try to find out if we have enough space to perform the copy or move action.
         if not helpers.isFileLocked(self.file_path, False):
             if not verify_freespace(self.file_path, ep_obj.show._location, [ep_obj] + ep_obj.relatedEps):
@@ -917,7 +917,7 @@ class PostProcessor(object):
                 return False
         else:
             self._log("Unable to determine needed filespace as the source file is locked for access")
-        
+
 
         # delete the existing file (and company)
         for cur_ep in [ep_obj] + ep_obj.relatedEps:
@@ -1020,7 +1020,7 @@ class PostProcessor(object):
         # add to anidb
         if ep_obj.show.is_anime and sickbeard.ANIDB_USE_MYLIST:
             self._add_to_anidb_mylist(self.file_path)
-        
+
         try:
             # move the episode and associated files to the show dir
             if self.process_method == "copy":
@@ -1046,7 +1046,7 @@ class PostProcessor(object):
                 raise exceptions.PostProcessingFailed("Unable to move the files to their new home")
         except (OSError, IOError):
             raise exceptions.PostProcessingFailed("Unable to move the files to their new home")
-                
+
         # download subtitles
         if sickbeard.USE_SUBTITLES and ep_obj.show.subtitles:
             for cur_ep in [ep_obj] + ep_obj.relatedEps:
diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py
index 42db2237ab9acb170561830da52f4ec1788477fd..01bacf5557a26dc76c8416a45c94462bf5d106ef 100644
--- a/sickbeard/providers/__init__.py
+++ b/sickbeard/providers/__init__.py
@@ -33,7 +33,6 @@ __all__ = ['womble',
            'nyaatorrents',
            'animenzb',
            'torrentbytes',
-           'animezb',
            'frenchtorrentdb',
            'freshontv',
            'titansoftv',
diff --git a/sickbeard/providers/animezb.py b/sickbeard/providers/animezb.py
deleted file mode 100644
index 480eba9d3b5ce99e964a8d04ec627c75ef583bf7..0000000000000000000000000000000000000000
--- a/sickbeard/providers/animezb.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# Author: Nic Wolfe <nic@wolfeden.ca>
-# URL: http://code.google.com/p/sickbeard/
-#
-# This file is part of Sick Beard.
-#
-# Sick Beard is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Sick Beard is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Sick Beard.  If not, see <http://www.gnu.org/licenses/>.
-
-import urllib
-import datetime
-
-import sickbeard
-import generic
-
-from sickbeard import classes, show_name_helpers, helpers
-
-from sickbeard import exceptions, logger
-from sickbeard.common import *
-from sickbeard import tvcache
-
-class Animezb(generic.NZBProvider):
-
-    def __init__(self):
-        generic.NZBProvider.__init__(self, "Animezb")
-
-        self.urls = {'base_url': 'https://animezb.com/'}
-        self.url = self.urls['base_url']
-
-        self.supportsBacklog = False
-        self.supportsAbsoluteNumbering = True
-        self.anime_only = True
-
-        self.enabled = False
-
-        self.cache = AnimezbCache(self)
-
-    def isEnabled(self):
-        return self.enabled
-
-    def imageName(self):
-        return 'animezb.png'
-
-    def _get_season_search_strings(self, ep_obj):
-        return [x for x in show_name_helpers.makeSceneSeasonSearchString(self.show, ep_obj)]
-
-    def _get_episode_search_strings(self, ep_obj, add_string=''):
-        search_string = []
-        for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
-            ep_string = '+'.join(
-                [helpers.sanitizeSceneName(show_name).replace('.', '+'), str(ep_obj.scene_absolute_number).zfill(2)])
-            search_string.append(ep_string)
-        return search_string
-
-    def _doSearch(self, search_string, epcount=0, age=0, epObj=None):
-        if self.show and not self.show.is_anime:
-            logger.log(u"" + str(self.show.name) + " is not an anime skiping ...")
-            return []
-
-        params = {
-            "cat": "anime",
-            "q": search_string.encode('utf-8'),
-            "max": "100"
-        }
-
-        search_url = self.url + "rss?" + urllib.urlencode(params)
-
-        logger.log(u"Search url: " + search_url, logger.DEBUG)
-
-        results = []
-        for curItem in self.cache.getRSSFeed(search_url, items=['entries'])['entries'] or []:
-            (title, url) = self._get_title_and_url(curItem)
-
-            if title and url:
-                results.append(curItem)
-            else:
-                logger.log(
-                    u"The data returned from the " + self.name + " is incomplete, this result is unusable",
-                    logger.DEBUG)
-
-        return results
-
-    def findPropers(self, date=None):
-
-        results = []
-
-        for item in self._doSearch("v2 OR v3 OR v4 OR v5"):
-
-            (title, url) = self._get_title_and_url(item)
-
-            if item.has_key('published_parsed') and item['published_parsed']:
-                result_date = item.published_parsed
-                if result_date:
-                    result_date = datetime.datetime(*result_date[0:6])
-            else:
-                logger.log(u"Unable to figure out the date for entry " + title + ", skipping it")
-                continue
-
-            if not date or result_date > date:
-                search_result = classes.Proper(title, url, result_date, self.show)
-                results.append(search_result)
-
-        return results
-
-
-class AnimezbCache(tvcache.TVCache):
-
-    def __init__(self, provider):
-
-        tvcache.TVCache.__init__(self, provider)
-
-        # only poll Animezb every 20 minutes max
-        self.minTime = 20
-
-    def _getRSSData(self):
-
-        params = {
-            "cat": "anime".encode('utf-8'),
-            "max": "100".encode('utf-8')
-        }
-
-        rss_url = self.provider.url + 'rss?' + urllib.urlencode(params)
-
-        logger.log(self.provider.name + u" cache update URL: " + rss_url, logger.DEBUG)
-
-        return self.getRSSFeed(rss_url)
-
-provider = Animezb()
diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py
index 4e6d2f93b6f93b79f3db3b2a9b30a9adef47cbf3..65200573eecfc14aa49a35898ac6728ccf9f72a2 100644
--- a/sickbeard/providers/kat.py
+++ b/sickbeard/providers/kat.py
@@ -137,7 +137,7 @@ class KATProvider(generic.TorrentProvider):
                     if quality != Quality.UNKNOWN: break
 
                 if fileName is not None and quality == Quality.UNKNOWN:
-                    quality = Quality.assumeQuality(os.path.basename(fileName))
+                    quality = Quality.assumeQuality(fileName)
 
                 if quality == Quality.UNKNOWN:
                     logger.log(u"Unable to obtain a Season Quality for " + title, logger.DEBUG)
diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py
index d2e6d38ddf4ba48443d996a8d4b7939bf633cc20..2e8a4ebd6a3f8ec1eb9bb9ece6886988e4f18614 100644
--- a/sickbeard/providers/nyaatorrents.py
+++ b/sickbeard/providers/nyaatorrents.py
@@ -61,10 +61,10 @@ class NyaaProvider(generic.TorrentProvider):
         return generic.TorrentProvider.findSearchResults(self, show, episodes, search_mode, manualSearch, downCurQuality)
 
     def _get_season_search_strings(self, ep_obj):
-        return show_name_helpers.makeSceneShowSearchStrings(self.show, anime=True)
+        return [x for x in show_name_helpers.makeSceneSeasonSearchString(self.show, ep_obj)]
 
     def _get_episode_search_strings(self, ep_obj, add_string=''):
-        return self._get_season_search_strings(ep_obj)
+        return [x for x in show_name_helpers.makeSceneSearchString(self.show, ep_obj)]
 
     def _doSearch(self, search_string, search_mode='eponly', epcount=0, age=0, epObj=None):
         if self.show and not self.show.is_anime:
diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py
index 2e229ff9badb51ad87c29beb4cf45d4709ab09aa..23df14de24015ab1be375338f26a18a74f32147a 100644
--- a/sickbeard/providers/thepiratebay.py
+++ b/sickbeard/providers/thepiratebay.py
@@ -145,7 +145,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
             if quality != Quality.UNKNOWN: break
 
         if fileName is not None and quality == Quality.UNKNOWN:
-            quality = Quality.assumeQuality(os.path.basename(fileName))
+            quality = Quality.assumeQuality(fileName)
 
         if quality == Quality.UNKNOWN:
             logger.log(u"Unable to obtain a Season Quality for " + title, logger.DEBUG)
@@ -258,7 +258,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
                             'title') + " but that doesn't seem like a trusted result so I'm ignoring it", logger.DEBUG)
                         continue
 
-                    #Check number video files = episode in season and find the real Quality for full season torrent analyzing files in torrent 
+                    #Check number video files = episode in season and find the real Quality for full season torrent analyzing files in torrent
                     if mode == 'Season' and search_mode == 'sponly':
                         ep_number = int(epcount / len(set(allPossibleShowNames(self.show))))
                         title = self._find_season_quality(title, id, ep_number)
diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py
index 247de71c2663ba031aff53587ed5ae5d7f7b6b25..d206cf406153e5d767547fef04a9aac8aa608e28 100644
--- a/sickbeard/show_queue.py
+++ b/sickbeard/show_queue.py
@@ -319,7 +319,7 @@ class QueueItemAdd(ShowQueueItem):
                     self.show.release_groups.set_black_keywords(self.blacklist)
                 if self.whitelist:
                     self.show.release_groups.set_white_keywords(self.whitelist)
-                    
+
             # be smartish about this
             #if self.show.genre and "talk show" in self.show.genre.lower():
             #    self.show.air_by_date = 1
@@ -393,6 +393,7 @@ class QueueItemAdd(ShowQueueItem):
         sickbeard.traktRollingScheduler.action.updateWantedList(self.show.indexerid)
 
         # if they set default ep status to WANTED then run the backlog to search for episodes
+        # FIXME: This needs to be a backlog queue item!!!
         if self.show.default_ep_status == WANTED:
             logger.log(u"Launching backlog for this show since its episodes are WANTED")
             sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])
@@ -548,6 +549,7 @@ class QueueItemUpdate(ShowQueueItem):
             logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)
             logger.log(traceback.format_exc(), logger.DEBUG)
 
+        # have to save show before reading episodes from db
         try:
             self.show.saveToDB()
         except Exception, e:
@@ -567,7 +569,6 @@ class QueueItemUpdate(ShowQueueItem):
                 self.show.indexer).name + ", the show info will not be refreshed: " + ex(e), logger.ERROR)
             IndexerEpList = None
 
-        foundMissingEps = False
         if IndexerEpList is None:
             logger.log(u"No data returned from " + sickbeard.indexerApi(
                 self.show.indexer).name + ", unable to update this show", logger.ERROR)
@@ -575,15 +576,13 @@ class QueueItemUpdate(ShowQueueItem):
             # for each ep we found on the Indexer delete it from the DB list
             for curSeason in IndexerEpList:
                 for curEpisode in IndexerEpList[curSeason]:
-                    logger.log(u"Removing " + str(curSeason) + "x" + str(curEpisode) + " from the DB list",
-                               logger.DEBUG)
+                    curEp = self.show.getEpisode(curSeason, curEpisode)
+                    curEp.saveToDB()
+
                     if curSeason in DBEpList and curEpisode in DBEpList[curSeason]:
                         del DBEpList[curSeason][curEpisode]
-                    else:
-                        # found missing episodes
-                        foundMissingEps = True
 
-            # for the remaining episodes in the DB list just delete them from the DB
+            # remaining episodes in the DB list are not on the indexer, just delete them from the DB
             for curSeason in DBEpList:
                 for curEpisode in DBEpList[curSeason]:
                     logger.log(u"Permanently deleting episode " + str(curSeason) + "x" + str(
@@ -594,10 +593,12 @@ class QueueItemUpdate(ShowQueueItem):
                     except exceptions.EpisodeDeletedException:
                         pass
 
-        # if they set default ep status to WANTED then run the backlog
-        if foundMissingEps and self.show.default_ep_status == WANTED:
-            logger.log(u"Launching backlog for this show since we found missing episodes")
-            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])
+        # save show again, in case episodes have changed
+        try:
+            self.show.saveToDB()
+        except Exception, e:
+            logger.log(u"Error saving show info to the database: " + ex(e), logger.ERROR)
+            logger.log(traceback.format_exc(), logger.DEBUG)
 
         logger.log(u"Finished update of " + self.show.name, logger.DEBUG)
 
diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py
index d4cd64bbfd24bcbf98b52f706f2c02b8ef4173cb..757ca924afdb100961b46e38a57b2b1356a6c25b 100644
--- a/sickbeard/versionChecker.py
+++ b/sickbeard/versionChecker.py
@@ -148,7 +148,7 @@ class CheckVersion():
 
         def db_safe(self):
             try:
-                result = self.getDBcompare(sickbeard.BRANCH)
+                result = self.getDBcompare()
                 if result == 'equal':
                     logger.log(u"We can proceed with the update. New update has same DB version", logger.DEBUG)
                     return True
@@ -192,9 +192,9 @@ class CheckVersion():
             logger.log(u"Auto update aborted", logger.DEBUG)
             return False
 
-    def getDBcompare(self, branchDest):
+    def getDBcompare(self):
         try:
-            response = requests.get("https://raw.githubusercontent.com/SICKRAGETV/SickRage/" + str(branchDest) +"/sickbeard/databases/mainDB.py")
+            response = requests.get("http://cdn.rawgit.com/SICKRAGETV/SickRage/" + str(self._newest_commit_hash) +"/sickbeard/databases/mainDB.py")
             response.raise_for_status()
             match = re.search(r"MAX_DB_VERSION\s=\s(?P<version>\d{2,3})",response.text)
             branchDestDBversion = int(match.group('version'))
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 72cca7266b54c9695f114f7fc3210d00837dbc39..e00908b4a893b1c346cfb7610b83b3a82d12d6bb 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -16,8 +16,6 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import with_statement
-
 import traceback
 import os
 import time
@@ -36,7 +34,6 @@ from sickbeard import encodingKludge as ek
 from sickbeard import search_queue
 from sickbeard import image_cache
 from sickbeard import naming
-from sickbeard import scene_exceptions
 from sickbeard import subtitles
 from sickbeard import network_timezones
 from sickbeard import sbdatetime
@@ -46,12 +43,13 @@ from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILE
 from sickbeard.common import SD, HD720p, HD1080p
 from sickbeard.exceptions import ex
 from sickbeard.blackandwhitelist import BlackAndWhiteList, short_group_names
-from sickbeard.scene_exceptions import get_scene_exceptions
 from sickbeard.browser import foldersAtPath
 from sickbeard.scene_numbering import get_scene_numbering, set_scene_numbering, get_scene_numbering_for_show, \
     get_xem_numbering_for_show, get_scene_absolute_numbering_for_show, get_xem_absolute_numbering_for_show, \
     get_scene_absolute_numbering
 
+import imdbPopular
+
 from dateutil import tz, parser as dateutil_parser
 from unrar2 import RarFile
 import adba, subliminal
@@ -72,8 +70,8 @@ try:
 except ImportError:
     import xml.etree.ElementTree as etree
 
-from Cheetah.Template import Template as CheetahTemplate
-from Cheetah.Filters import Filter as CheetahFilter
+from mako.template import Template as MakoTemplate
+from mako.lookup import TemplateLookup
 
 from tornado.routes import route
 from tornado.web import RequestHandler, HTTPError, authenticated, asynchronous
@@ -83,74 +81,61 @@ from tornado.concurrent import run_on_executor
 from concurrent.futures import ThreadPoolExecutor
 
 route_locks = {}
-import chardet
-
-class html_entities(CheetahFilter):
-    def filter(self, val, **dummy_kw):
-        if isinstance(val, unicode):
-            filtered = val.encode('ascii', 'xmlcharrefreplace')
-        elif val is None:
-            filtered = ''
-        elif isinstance(val, str):
-            try:
-                filtered = unicode(val)
-            except Exception:
-                try:
-                    filtered = unicode(val, 'utf-8')
-                except Exception:
-                    try:
-                        filtered = unicode(val, 'latin-1')
-                    except Exception:
-                        try:
-                            filtered = unicode(val, sickbeard.SYS_ENCODING)
-                        except Exception:
-                            logger.log(u'Unable to decode using %s, utf-8, or latin-1. Falling back to chardet!' %
-                                    sickbeard.SYS_ENCODING, logger.ERROR)
-                            filtered = unicode(val, chardet.detect(val).get('encoding'))
-            try:
-                filtered = filtered.encode('ascii', 'xmlcharrefreplace')
-            except Exception:
-                logger.log(u'Unable to encode to ascii using xmlcharrefreplace.', logger.ERROR)
-        else:
-            filtered = self.filter(str(val))
 
-        return filtered
+mako_path = mako_cache = mako_lookup = None
 
+class _setupLookup():
+    def __init__(self, *args, **kwargs):
+        global mako_path
+        global mako_cache
+        global mako_lookup
+
+        if not mako_path:
+            mako_path = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/")
+        if not mako_cache:
+            mako_cache = os.path.join(sickbeard.CACHE_DIR, 'mako')
+        if not mako_lookup:
+            mako_lookup = TemplateLookup(directories=[mako_path], module_directory=mako_cache, format_exceptions=True)
+
+class PageTemplate(MakoTemplate):
+    arguments = {}
+    def __init__(self, rh, file, *args, **kwargs):
+        _setupLookup()
+        kwargs['filename'] = os.path.join(mako_path, file)
+        kwargs['module_directory'] = mako_cache
+        kwargs['lookup'] = mako_lookup
+        kwargs['format_exceptions'] = True
 
-class PageTemplate(CheetahTemplate):
-    def __init__(self, rh, *args, **kwargs):
-        kwargs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/", kwargs['file'])
-        kwargs['filter'] = html_entities
         super(PageTemplate, self).__init__(*args, **kwargs)
 
-        self.sbRoot = sickbeard.WEB_ROOT
-        self.sbHttpPort = sickbeard.WEB_PORT
-        self.sbHttpsPort = sickbeard.WEB_PORT
-        self.sbHttpsEnabled = sickbeard.ENABLE_HTTPS
-        self.sbHandleReverseProxy = sickbeard.HANDLE_REVERSE_PROXY
-        self.sbThemeName = sickbeard.THEME_NAME
-        self.sbDefaultPage = sickbeard.DEFAULT_PAGE
-        self.sbLogin = rh.get_current_user()
+        self.arguments['sbRoot'] = sickbeard.WEB_ROOT
+        self.arguments['sbHttpPort'] = sickbeard.WEB_PORT
+        self.arguments['sbHttpsPort'] = sickbeard.WEB_PORT
+        self.arguments['sbHttpsEnabled'] = sickbeard.ENABLE_HTTPS
+        self.arguments['sbHandleReverseProxy'] = sickbeard.HANDLE_REVERSE_PROXY
+        self.arguments['sbThemeName'] = sickbeard.THEME_NAME
+        self.arguments['sbDefaultPage'] = sickbeard.DEFAULT_PAGE
+        self.arguments['sbLogin'] = rh.get_current_user()
 
         if rh.request.headers['Host'][0] == '[':
-            self.sbHost = re.match("^\[.*\]", rh.request.headers['Host'], re.X | re.M | re.S).group(0)
+            self.arguments['sbHost'] = re.match("^\[.*\]", rh.request.headers['Host'], re.X | re.M | re.S).group(0)
         else:
-            self.sbHost = re.match("^[^:]+", rh.request.headers['Host'], re.X | re.M | re.S).group(0)
+            self.arguments['sbHost'] = re.match("^[^:]+", rh.request.headers['Host'], re.X | re.M | re.S).group(0)
 
         if "X-Forwarded-Host" in rh.request.headers:
-            self.sbHost = rh.request.headers['X-Forwarded-Host']
+            self.arguments['sbHost'] = rh.request.headers['X-Forwarded-Host']
         if "X-Forwarded-Port" in rh.request.headers:
             sbHttpPort = rh.request.headers['X-Forwarded-Port']
-            self.sbHttpsPort = sbHttpPort
+            self.arguments['sbHttpsPort'] = sbHttpPort
         if "X-Forwarded-Proto" in rh.request.headers:
-            self.sbHttpsEnabled = True if rh.request.headers['X-Forwarded-Proto'] == 'https' else False
+            self.arguments['sbHttpsEnabled'] = True if rh.request.headers['X-Forwarded-Proto'] == 'https' else False
 
         logPageTitle = 'Logs &amp; Errors'
         if len(classes.ErrorViewer.errors):
             logPageTitle += ' (' + str(len(classes.ErrorViewer.errors)) + ')'
-        self.logPageTitle = logPageTitle
-        self.sbPID = str(sickbeard.PID)
-        self.menu = [
+        self.arguments['logPageTitle'] = logPageTitle
+        self.arguments['sbPID'] = str(sickbeard.PID)
+        self.arguments['menu'] = [
             {'title': 'Home', 'key': 'home'},
             {'title': 'Coming Episodes', 'key': 'comingEpisodes'},
             {'title': 'History', 'key': 'history'},
@@ -159,14 +144,16 @@ class PageTemplate(CheetahTemplate):
             {'title': logPageTitle, 'key': 'errorlogs'},
         ]
 
-    def compile(self, *args, **kwargs):
-        if not os.path.exists(os.path.join(sickbeard.CACHE_DIR, 'cheetah')):
-            os.mkdir(os.path.join(sickbeard.CACHE_DIR, 'cheetah'))
+        self.arguments['title'] = "FixME"
+        self.arguments['header'] = "FixME"
+        self.arguments['topmenu'] = "FixME"
 
-        kwargs['cacheModuleFilesForTracebacks'] = True
-        kwargs['cacheDirForModuleFiles'] = os.path.join(sickbeard.CACHE_DIR, 'cheetah')
-        return super(PageTemplate, self).compile(*args, **kwargs)
+    def render(self, *args, **kwargs):
+        for key in self.arguments:
+            if key not in kwargs:
+                kwargs[key] = self.arguments[key]
 
+        return super(PageTemplate, self).render(*args, **kwargs)
 
 class BaseHandler(RequestHandler):
     def __init__(self, *args, **kwargs):
@@ -286,8 +273,8 @@ class LoginHandler(BaseHandler):
         if self.get_current_user():
             self.redirect('/' + sickbeard.DEFAULT_PAGE +'/')
         else:
-            t = PageTemplate(rh=self, file="login.tmpl")
-            self.finish(t.respond())
+            t = PageTemplate(rh=self, file="login.mako")
+            self.finish(t.render(title="Login", header="Login", topmenu="login"))
 
     def post(self, *args, **kwargs):
 
@@ -305,7 +292,7 @@ class LoginHandler(BaseHandler):
             self.set_secure_cookie('sickrage_user', api_key, expires_days=30 if remember_me > 0 else None)
             logger.log('User logged into the SickRage web interface', logger.INFO)
         else:
-            logger.log('User attempted a failed login to the SickRage web interface from IP: ' + self.request.remote_ip, logger.WARNING)    
+            logger.log('User attempted a failed login to the SickRage web interface from IP: ' + self.request.remote_ip, logger.WARNING)
 
         self.redirect('/' + sickbeard.DEFAULT_PAGE +'/')
 
@@ -350,36 +337,36 @@ class WebRoot(WebHandler):
         return "User-agent: *\nDisallow: /"
 
     def apibuilder(self):
-        t = PageTemplate(rh=self, file="apiBuilder.tmpl")
+        t = PageTemplate(rh=self, file="apiBuilder.mako")
 
         def titler(x):
             return (helpers.remove_article(x), x)[not x or sickbeard.SORT_ARTICLE]
 
-        t.sortedShowList = sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))
+        sortedShowList = sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))
 
         myDB = db.DBConnection(row_type="dict")
 
         seasonSQLResults = {}
         episodeSQLResults = {}
 
-        for curShow in t.sortedShowList:
+        for curShow in sortedShowList:
             seasonSQLResults[curShow.indexerid] = myDB.select(
                 "SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season DESC", [curShow.indexerid])
 
-        for curShow in t.sortedShowList:
+        for curShow in sortedShowList:
             episodeSQLResults[curShow.indexerid] = myDB.select(
                 "SELECT DISTINCT season,episode FROM tv_episodes WHERE showid = ? ORDER BY season DESC, episode DESC",
                 [curShow.indexerid])
 
-        t.seasonSQLResults = seasonSQLResults
-        t.episodeSQLResults = episodeSQLResults
+        seasonSQLResults = seasonSQLResults
+        episodeSQLResults = episodeSQLResults
 
         if len(sickbeard.API_KEY) == 32:
-            t.apikey = sickbeard.API_KEY
+            apikey = sickbeard.API_KEY
         else:
-            t.apikey = "api key not generated"
+            apikey = "api key not generated"
 
-        return t.respond()
+        return t.render(title="Api Builder", header="Api Builder", sortedShowList=sortedShowList, seasonSQLResults=seasonSQLResults, episodeSQLResults=episodeSQLResults, apikey=apikey)
 
     def showPoster(self, show=None, which=None):
         # Redirect initial poster/banner thumb to default images
@@ -408,7 +395,7 @@ class WebRoot(WebHandler):
                 if not cache_obj.has_fanart(show):
                     cache_obj.fill_cache(sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)))
                 image_file_name = cache_obj.fanart_path(show)
-                
+
             if ek.ek(os.path.isfile, image_file_name):
                 static_image_path = os.path.normpath(image_file_name.replace(sickbeard.CACHE_DIR, '/cache'))
 
@@ -552,13 +539,13 @@ class WebRoot(WebHandler):
 
         sql_results.sort(sorts[sickbeard.COMING_EPS_SORT])
 
-        t = PageTemplate(rh=self, file="comingEpisodes.tmpl")
+        t = PageTemplate(rh=self, file="comingEpisodes.mako")
         # paused_item = { 'title': '', 'path': 'toggleComingEpsDisplayPaused' }
         # paused_item['title'] = 'Hide Paused' if sickbeard.COMING_EPS_DISPLAY_PAUSED else 'Show Paused'
         paused_item = {'title': 'View Paused:', 'path': {'': ''}}
         paused_item['path'] = {'Hide': 'toggleComingEpsDisplayPaused'} if sickbeard.COMING_EPS_DISPLAY_PAUSED else {
             'Show': 'toggleComingEpsDisplayPaused'}
-        t.submenu = [
+        submenu = [
             {'title': 'Sort by:', 'path': {'Date': 'setComingEpsSort/?sort=date',
                                            'Show': 'setComingEpsSort/?sort=show',
                                            'Network': 'setComingEpsSort/?sort=network',
@@ -572,17 +559,18 @@ class WebRoot(WebHandler):
             paused_item,
         ]
 
-        t.next_week = datetime.datetime.combine(next_week1, datetime.time(tzinfo=network_timezones.sb_timezone))
-        t.today = datetime.datetime.now().replace(tzinfo=network_timezones.sb_timezone)
-        t.sql_results = sql_results
+        next_week = datetime.datetime.combine(next_week1, datetime.time(tzinfo=network_timezones.sb_timezone))
+        today = datetime.datetime.now().replace(tzinfo=network_timezones.sb_timezone)
+        sql_results = sql_results
 
         # Allow local overriding of layout parameter
         if layout and layout in ('poster', 'banner', 'list', 'calendar'):
-            t.layout = layout
+            layout = layout
         else:
-            t.layout = sickbeard.COMING_EPS_LAYOUT
+            layout = sickbeard.COMING_EPS_LAYOUT
 
-        return t.respond()
+        return t.render(submenu=submenu, next_week=next_week, today=today, sql_results=sql_results, layout=layout,
+                title='Coming Episodes', header='Coming Episodes', topmenu='comingEpisodes')
 
 
 class CalendarHandler(BaseHandler):
@@ -666,7 +654,7 @@ class CalendarHandler(BaseHandler):
         ical += 'END:VCALENDAR'
 
         return ical
-        
+
 @route('/ui(/?.*)')
 class UI(WebRoot):
     def __init__(self, *args, **kwargs):
@@ -716,20 +704,17 @@ class Home(WebRoot):
         menu = [
             {'title': 'Add Shows', 'path': 'home/addShows/', },
             {'title': 'Manual Post-Processing', 'path': 'home/postprocess/'},
-            {'title': 'Update KODI', 'path': 'home/updateKODI/', 'requires': self.haveKODI},
-            {'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX},
-            {'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY},
-            {'title': 'Manage Torrents', 'path': 'manage/manageTorrents/', 'requires': self.haveTORRENT},
+            {'title': 'Update KODI', 'path': 'home/updateKODI/', 'requires': self.haveKODI()},
+            {'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX()},
+            {'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY()},
+            {'title': 'Manage Torrents', 'path': 'manage/manageTorrents/', 'requires': self.haveTORRENT()},
         ]
 
         return menu
 
     def _genericMessage(self, subject, message):
-        t = PageTemplate(rh=self, file="genericMessage.tmpl")
-        t.submenu = self.HomeMenu()
-        t.subject = subject
-        t.message = message
-        return t.respond()
+        t = PageTemplate(rh=self, file="genericMessage.mako")
+        return t.render(message=message, subject=subject, submenu=self.HomeMenu(), topmenu="home", title="")
 
     def _getEpisode(self, show, season=None, episode=None, absolute=None):
         if show is None:
@@ -753,7 +738,7 @@ class Home(WebRoot):
         return epObj
 
     def index(self):
-        t = PageTemplate(rh=self, file="home.tmpl")
+        t = PageTemplate(rh=self, file="home.mako")
         if sickbeard.ANIME_SPLIT_HOME:
             shows = []
             anime = []
@@ -762,14 +747,11 @@ class Home(WebRoot):
                     anime.append(show)
                 else:
                     shows.append(show)
-            t.showlists = [["Shows", shows],
-                           ["Anime", anime]]
+            showlists = [["Shows", shows],["Anime", anime]]
         else:
-            t.showlists = [["Shows", sickbeard.showList]]
-
-        t.submenu = self.HomeMenu()
+            showlists = [["Shows", sickbeard.showList]]
 
-        return t.respond()
+        return t.render(title="Home", header="Show List", topmenu="home", showlists=showlists, submenu=self.HomeMenu())
 
     def is_alive(self, *args, **kwargs):
         if 'callback' in kwargs and '_' in kwargs:
@@ -977,7 +959,7 @@ class Home(WebRoot):
         ui.notifications.message('Tested Plex Media Server host(s): ', urllib.unquote_plus(host.replace(',', ', ')))
 
         return finalResult
-        
+
     def testLibnotify(self):
         # self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
 
@@ -1043,15 +1025,15 @@ class Home(WebRoot):
         else:
             return '{"message": "Unable to find NMJ Database at location: %(dbloc)s. Is the right location selected and PCH running?", "database": ""}' % {
                 "dbloc": dbloc}
-   
+
     def getTraktToken(self, trakt_pin=None):
-        
+
         trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)
         response = trakt_api.traktToken(trakt_pin)
         if response:
             return "Trakt Authorized"
         return "Trakt Not Authorized!"
- 
+
     def testTrakt(self, username=None, password=None, disable_ssl=None, blacklist_name=None):
         # self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
         if disable_ssl == 'true':
@@ -1136,8 +1118,22 @@ class Home(WebRoot):
             return "Error sending Pushbullet notification"
 
     def status(self):
-        t = PageTemplate(rh=self, file='status.tmpl')
-        return t.respond()
+        tvdirFree = helpers.getDiskSpaceUsage(sickbeard.TV_DOWNLOAD_DIR)
+        rootDir = {}
+        if sickbeard.ROOT_DIRS:
+            backend_pieces = sickbeard.ROOT_DIRS.split('|')
+            backend_default = 'rd-' + backend_pieces[0]
+            backend_dirs = backend_pieces[1:]
+        else:
+            backend_default = ''
+            backend_dirs = []
+
+        if len(backend_dirs):
+            for subject in backend_dirs:
+                rootDir[subject] = helpers.getDiskSpaceUsage(subject)
+
+        t = PageTemplate(rh=self, file="status.mako")
+        return t.render(title='Status', header='Status', topmenu='home', submenu=self.HomeMenu(), tvdirFree=tvdirFree, rootDir=rootDir)
 
     def shutdown(self, pid=None):
         if str(pid) != str(sickbeard.PID):
@@ -1154,13 +1150,12 @@ class Home(WebRoot):
         if str(pid) != str(sickbeard.PID):
             return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/')
 
-        t = PageTemplate(rh=self, file="restart.tmpl")
-        t.submenu = self.HomeMenu()
+        t = PageTemplate(rh=self, file="restart.mako")
 
         # restart
         sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
 
-        return t.respond()
+        return t.render(title="Home", header="Restarting SickRage", topmenu="home", submenu=self.HomeMenu())
 
     def updateCheck(self, pid=None):
         if str(pid) != str(sickbeard.PID):
@@ -1171,10 +1166,10 @@ class Home(WebRoot):
         return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/')
 
     def update(self, pid=None):
-        
+
         if str(pid) != str(sickbeard.PID):
             return self.redirect('/home/')
-            
+
         checkversion = CheckVersion()
         backup = checkversion._runbackup()
 
@@ -1183,9 +1178,9 @@ class Home(WebRoot):
             if sickbeard.versionCheckScheduler.action.update():
                 # do a hard restart
                 sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
-            
-                t = PageTemplate(rh=self, file="restart.tmpl")
-                return t.respond()
+
+                t = PageTemplate(rh=self, file="restart.mako")
+                return t.render(title="Home", header="Restarting SickRage", topmenu="home", submenu=self.HomeMenu())
             else:
                 return self._genericMessage("Update Failed",
                                             "Update wasn't successful, not restarting. Check your log for more information.")
@@ -1240,13 +1235,13 @@ class Home(WebRoot):
             [showObj.indexerid]
         )
 
-        t = PageTemplate(rh=self, file="displayShow.tmpl")
-        t.submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
+        t = PageTemplate(rh=self, file="displayShow.mako")
+        submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
 
         try:
-            t.showLoc = (showObj.location, True)
+            showLoc = (showObj.location, True)
         except sickbeard.exceptions.ShowDirNotFoundException:
-            t.showLoc = (showObj._location, False)
+            showLoc = (showObj._location, False)
 
         show_message = ''
 
@@ -1274,30 +1269,28 @@ class Home(WebRoot):
         if not sickbeard.showQueueScheduler.action.isBeingAdded(showObj):
             if not sickbeard.showQueueScheduler.action.isBeingUpdated(showObj):
                 if showObj.paused:
-                    t.submenu.append({'title': 'Resume', 'path': 'home/togglePause?show=%d' % showObj.indexerid})
+                    submenu.append({'title': 'Resume', 'path': 'home/togglePause?show=%d' % showObj.indexerid})
                 else:
-                    t.submenu.append({'title': 'Pause', 'path': 'home/togglePause?show=%d' % showObj.indexerid})
-                    
-                t.submenu.append(
+                    submenu.append({'title': 'Pause', 'path': 'home/togglePause?show=%d' % showObj.indexerid})
+
+                submenu.append(
                     {'title': 'Remove', 'path': 'home/deleteShow?show=%d' % showObj.indexerid, 'confirm': True})
-                t.submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid})
-                t.submenu.append(
+                submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid})
+                submenu.append(
                     {'title': 'Force Full Update', 'path': 'home/updateShow?show=%d&amp;force=1' % showObj.indexerid})
-                t.submenu.append({'title': 'Update show in KODI',
-                                  'path': 'home/updateKODI?show=%d' % showObj.indexerid, 'requires': self.haveKODI})
-                t.submenu.append({'title': 'Update show in Emby',
-                                  'path': 'home/updateEMBY?show=%d' % showObj.indexerid, 'requires': self.haveEMBY})
-                t.submenu.append({'title': 'Preview Rename', 'path': 'home/testRename?show=%d' % showObj.indexerid})
+                submenu.append({'title': 'Update show in KODI',
+                                  'path': 'home/updateKODI?show=%d' % showObj.indexerid, 'requires': self.haveKODI()})
+
+                submenu.append({'title': 'Update show in Emby',
+                                  'path': 'home/updateEMBY?show=%d' % showObj.indexerid, 'requires': self.haveEMBY()})
+
+                submenu.append({'title': 'Preview Rename', 'path': 'home/testRename?show=%d' % showObj.indexerid})
+
                 if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled(
                         showObj) and showObj.subtitles:
-                    t.submenu.append(
+                    submenu.append(
                         {'title': 'Download Subtitles', 'path': 'home/subtitleShow?show=%d' % showObj.indexerid})
 
-        t.show = showObj
-        t.sqlResults = sqlResults
-        t.seasonResults = seasonResults
-        t.show_message = show_message
-
         epCounts = {}
         epCats = {}
         epCounts[Overview.SKIPPED] = 0
@@ -1324,30 +1317,31 @@ class Home(WebRoot):
                     anime.append(show)
                 else:
                     shows.append(show)
-            t.sortedShowLists = [["Shows", sorted(shows, lambda x, y: cmp(titler(x.name), titler(y.name)))],
+            sortedShowLists = [["Shows", sorted(shows, lambda x, y: cmp(titler(x.name), titler(y.name)))],
                                  ["Anime", sorted(anime, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
         else:
-            t.sortedShowLists = [
+            sortedShowLists = [
                 ["Shows", sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
 
-        t.bwl = None
+        bwl = None
         if showObj.is_anime:
-            t.bwl = showObj.release_groups
-
-        t.epCounts = epCounts
-        t.epCats = epCats
+            bwl = showObj.release_groups
 
-        showObj.exceptions = scene_exceptions.get_scene_exceptions(showObj.indexerid)
+        showObj.exceptions = sickbeard.scene_exceptions.get_scene_exceptions(showObj.indexerid)
 
         indexerid = int(showObj.indexerid)
         indexer = int(showObj.indexer)
-        t.all_scene_exceptions = showObj.exceptions
-        t.scene_numbering = get_scene_numbering_for_show(indexerid, indexer)
-        t.xem_numbering = get_xem_numbering_for_show(indexerid, indexer)
-        t.scene_absolute_numbering = get_scene_absolute_numbering_for_show(indexerid, indexer)
-        t.xem_absolute_numbering = get_xem_absolute_numbering_for_show(indexerid, indexer)
 
-        return t.respond()
+        return t.render(submenu=submenu, showLoc=showLoc, show_message=show_message,
+                show=showObj, sqlResults=sqlResults, seasonResults=seasonResults,
+                sortedShowLists=sortedShowLists, bwl=bwl, epCounts=epCounts,
+                epCats=epCats, all_scene_exceptions=showObj.exceptions,
+                scene_numbering=get_scene_numbering_for_show(indexerid, indexer),
+                xem_numbering=get_xem_numbering_for_show(indexerid, indexer),
+                scene_absolute_numbering=get_scene_absolute_numbering_for_show(indexerid, indexer),
+                xem_absolute_numbering=get_xem_absolute_numbering_for_show(indexerid, indexer),
+                title=showObj.name
+        )
 
 
     def plotDetails(self, show, season, episode):
@@ -1394,31 +1388,35 @@ class Home(WebRoot):
             else:
                 return self._genericMessage("Error", errString)
 
-        showObj.exceptions = scene_exceptions.get_scene_exceptions(showObj.indexerid)
+        showObj.exceptions = sickbeard.scene_exceptions.get_scene_exceptions(showObj.indexerid)
 
         if not location and not anyQualities and not bestQualities and not flatten_folders:
-            t = PageTemplate(rh=self, file="editShow.tmpl")
-            t.submenu = self.HomeMenu()
+            t = PageTemplate(rh=self, file="editShow.mako")
 
             if showObj.is_anime:
-                t.whitelist = showObj.release_groups.whitelist
-                t.blacklist = showObj.release_groups.blacklist
+                whitelist = showObj.release_groups.whitelist
+                blacklist = showObj.release_groups.blacklist
 
-                t.groups = []
+                groups = []
                 if helpers.set_up_anidb_connection() and not anidb_failed:
                     try:
                         anime = adba.Anime(sickbeard.ADBA_CONNECTION, name=showObj.name)
-                        t.groups = anime.get_groups()
+                        groups = anime.get_groups()
                     except Exception as e:
                         anidb_failed = True
                         ui.notifications.error('Unable to retreive Fansub Groups from AniDB.')
                         logger.log('Unable to retreive Fansub Groups from AniDB. Error is {0}'.format(str(e)),logger.DEBUG)
 
             with showObj.lock:
-                t.show = showObj
-                t.scene_exceptions = get_scene_exceptions(showObj.indexerid)
+                show = showObj
+                scene_exceptions = sickbeard.scene_exceptions.get_scene_exceptions(showObj.indexerid)
 
-            return t.respond()
+            if showObj.is_anime:
+                return t.render(submenu=self.HomeMenu(), show=show, scene_exceptions=scene_exceptions, groups=groups, whitelist=whitelist,
+                                blacklist=blacklist, title='Edit Shows', header='Edit Shows')
+            else:
+                return t.render(submenu=self.HomeMenu(), show=show, scene_exceptions=scene_exceptions, title='Edit Shows',
+                                header='Edit Shows')
 
         flatten_folders = config.checkbox_to_value(flatten_folders)
         dvdorder = config.checkbox_to_value(dvdorder)
@@ -1471,7 +1469,7 @@ class Home(WebRoot):
 
                     if whitelist:
                         shortwhitelist = short_group_names(whitelist)
-                        showObj.release_groups.set_white_keywords(shortwhitelist) 
+                        showObj.release_groups.set_white_keywords(shortwhitelist)
                     else:
                         showObj.release_groups.set_white_keywords([])
 
@@ -1544,7 +1542,7 @@ class Home(WebRoot):
 
         if do_update_exceptions:
             try:
-                scene_exceptions.update_scene_exceptions(showObj.indexerid, exceptions_list)  # @UndefinedVdexerid)
+                sickbeard.scene_exceptions.update_scene_exceptions(showObj.indexerid, exceptions_list)  # @UndefinedVdexerid)
                 time.sleep(cpu_presets[sickbeard.CPU_PRESET])
             except exceptions.CantUpdateException, e:
                 errors.append("Unable to force an update on scene exceptions of the show.")
@@ -1575,14 +1573,14 @@ class Home(WebRoot):
     def togglePause(self, show=None):
         if show is None:
             return self._genericMessage("Error", "Invalid show ID")
-        
+
         showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
 
         if showObj is None:
             return self._genericMessage("Error", "Unable to find the specified show")
 
         if showObj.paused:
-            showObj.paused = 0           
+            showObj.paused = 0
         else:
             showObj.paused = 1
 
@@ -1596,7 +1594,7 @@ class Home(WebRoot):
 
         ui.notifications.message('%s has been %s' % (showObj.name,('resumed', 'paused')[showObj.paused]))
         return self.redirect("/home/displayShow?show=" + show)
-        
+
     def deleteShow(self, show=None, full=0):
 
         if show is None:
@@ -1623,7 +1621,7 @@ class Home(WebRoot):
 
         ui.notifications.message('%s has been %s %s' %
                                  (showObj.name,
-                                  ('deleted', 'trashed')[sickbeard.TRASH_REMOVE_SHOW],
+                                  ('deleted', 'trashed')[bool(sickbeard.TRASH_REMOVE_SHOW)],
                                   ('(media untouched)', '(with all related media)')[bool(full)]))
         #Dont redirect to default page so user can confirm show was deleted
         return self.redirect('/home/')
@@ -1690,15 +1688,15 @@ class Home(WebRoot):
         return self.redirect("/home/displayShow?show=" + str(showObj.indexerid))
 
 
-    def updateKODI(self, show=None):        
+    def updateKODI(self, show=None):
         showName=None
         showObj=None
-        
+
         if show:
             showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
             if showObj:
                 showName=urllib.quote_plus(showObj.name.encode('utf-8'))
-            
+
         # only send update to first host in the list -- workaround for kodi sql backend users
         if sickbeard.KODI_UPDATE_ONLYFIRST:
             # only send update to first host in the list -- workaround for kodi sql backend users
@@ -1710,7 +1708,7 @@ class Home(WebRoot):
             ui.notifications.message("Library update command sent to KODI host(s): " + host)
         else:
             ui.notifications.error("Unable to contact one or more KODI host(s): " + host)
-            
+
         if showObj:
             return self.redirect('/home/displayShow?show=' + str(showObj.indexerid))
         else:
@@ -1818,11 +1816,11 @@ class Home(WebRoot):
                             u"Refusing to change status of " + curEp + " to FAILED because it's not SNATCHED/DOWNLOADED",
                             logger.ERROR)
                         continue
-                    
+
                     if epObj.status in Quality.DOWNLOADED and int(status) == WANTED:
                         logger.log(u"Removing release_name for episode as you want to set a downloaded episode back to wanted, so obviously you want it replaced")
                         epObj.release_name = ""
-                        
+
                     epObj.status = int(status)
 
                     # mass add to database
@@ -1865,7 +1863,7 @@ class Home(WebRoot):
                 ui.notifications.message("Backlog started", msg)
         elif int(status) == WANTED and showObj.paused:
             logger.log(u"Some episodes were set to wanted, but " + showObj.name + " is paused. Not adding to Backlog until show is unpaused")
-            
+
         if int(status) == FAILED:
             msg = "Retrying Search was automatically started for the following season of <b>" + showObj.name + "</b>:<br />"
             msg += '<ul>'
@@ -1927,12 +1925,10 @@ class Home(WebRoot):
             # present season DESC episode DESC on screen
             ep_obj_rename_list.reverse()
 
-        t = PageTemplate(rh=self, file="testRename.tmpl")
-        t.submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
-        t.ep_obj_list = ep_obj_rename_list
-        t.show = showObj
+        t = PageTemplate(rh=self, file="testRename.mako")
+        submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
 
-        return t.respond()
+        return t.render(submenu=submenu, ep_obj_list=ep_obj_rename_list, show=showObj, title='Preview Rename', header='Preview Rename')
 
 
     def doRename(self, show=None, eps=None):
@@ -2009,11 +2005,11 @@ class Home(WebRoot):
         def getEpisodes(searchThread, searchstatus):
             results = []
             showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(searchThread.show.indexerid))
-            
+
             if not showObj:
                 logger.log('No Show Object found for show with indexerID: ' + str(searchThread.show.indexerid), logger.ERROR)
                 return results
-            
+
             if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem):
                 results.append({'show': searchThread.show.indexerid,
                                 'episode': searchThread.segment.episode,
@@ -2207,7 +2203,7 @@ class Home(WebRoot):
             logger.log(u'ReleaseGroups: %s' % groups, logger.INFO)
             return json.dumps({'result': 'success', 'groups': groups})
 
-        return json.dumps({'result': 'failure'})            
+        return json.dumps({'result': 'failure'})
 
 @route('/IRC(/?.*)')
 class HomeIRC(Home):
@@ -2216,12 +2212,8 @@ class HomeIRC(Home):
 
     def index(self):
 
-        t = PageTemplate(rh=self, file="IRC.tmpl")
-        t.submenu = self.HomeMenu()
-        t.title = "IRC"
-        t.header = "IRC"
-        t.topmenu = "IRC"
-        return t.respond()
+        t = PageTemplate(rh=self, file="IRC.mako")
+        return t.render(topmenu="IRC", header="IRC", title="IRC", submenu=self.HomeMenu())
 
 @route('/news(/?.*)')
 class HomeNews(Home):
@@ -2236,14 +2228,10 @@ class HomeNews(Home):
             logger.log(u'Could not load news from repo, giving a link!', logger.DEBUG)
             news = 'Could not load news from the repo. [Click here for news.md](http://sickragetv.github.io/sickrage-news/news.md)'
 
-        t = PageTemplate(rh=self, file="markdown.tmpl")
-        t.submenu = self.HomeMenu()
-        t.title = "News"
-        t.header = "News"
-        t.topmenu = "news"
-        t.data = markdown2.markdown(news if news else "The was a problem connecting to github, please refresh and try again")
+        t = PageTemplate(rh=self, file="markdown.mako")
+        data = markdown2.markdown(news if news else "The was a problem connecting to github, please refresh and try again")
 
-        return t.respond()
+        return t.render(title="News", header="News", topmenu="news", data=data, submenu=self.HomeMenu())
 
 @route('/changes(/?.*)')
 class HomeChangeLog(Home):
@@ -2257,14 +2245,10 @@ class HomeChangeLog(Home):
             logger.log(u'Could not load changes from repo, giving a link!', logger.DEBUG)
             changes = 'Could not load changes from the repo. [Click here for CHANGES.md](http://sickragetv.github.io/sickrage-news/CHANGES.md)'
 
-        t = PageTemplate(rh=self, file="markdown.tmpl")
-        t.submenu = self.HomeMenu()
-        t.title = "Changelog"
-        t.header = "Changelog"
-        t.topmenu = "changes"
-        t.data = markdown2.markdown(changes if changes else "The was a problem connecting to github, please refresh and try again")
+        t = PageTemplate(rh=self, file="markdown.mako")
+        data = markdown2.markdown(changes if changes else "The was a problem connecting to github, please refresh and try again")
 
-        return t.respond()
+        return t.render(title="Changelog", header="Changelog", topmenu="changes", data=data, submenu=self.HomeMenu())
 
 @route('/home/postprocess(/?.*)')
 class HomePostProcess(Home):
@@ -2272,9 +2256,8 @@ class HomePostProcess(Home):
         super(HomePostProcess, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="home_postprocess.tmpl")
-        t.submenu = self.HomeMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="home_postprocess.mako")
+        return t.render(submenu=self.HomeMenu(), title='Post Processing', header='Post Processing')
 
     def processEpisode(self, dir=None, nzbName=None, jobName=None, quiet=None, process_method=None, force=None,
                        is_priority=None, delete_on="0", failed="0", type="auto", *args, **kwargs):
@@ -2293,12 +2276,12 @@ class HomePostProcess(Home):
             is_priority = True
         else:
             is_priority = False
-            
+
         if delete_on in ["on", "1"]:
             delete_on = True
         else:
             delete_on = False
-            
+
         if not dir:
             return self.redirect("/home/postprocess/")
         else:
@@ -2317,9 +2300,8 @@ class HomeAddShows(Home):
         super(HomeAddShows, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="home_addShows.tmpl")
-        t.submenu = self.HomeMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="home_addShows.mako")
+        return t.render(submenu=self.HomeMenu(), title='Add Shows', header='Add Shows')
 
     def getIndexerLanguages(self):
         result = sickbeard.indexerApi().config['valid_languages']
@@ -2364,8 +2346,7 @@ class HomeAddShows(Home):
 
 
     def massAddTable(self, rootDir=None):
-        t = PageTemplate(rh=self, file="home_massAddTable.tmpl")
-        t.submenu = self.HomeMenu()
+        t = PageTemplate(rh=self, file="home_massAddTable.mako")
 
         if not rootDir:
             return "No folders selected."
@@ -2443,20 +2424,17 @@ class HomeAddShows(Home):
                 if indexer_id and helpers.findCertainShow(sickbeard.showList, indexer_id):
                     cur_dir['added_already'] = True
 
-        t.dirList = dir_list
 
-        return t.respond()
+        return t.render(submenu=self.HomeMenu(), dirList=dir_list)
 
 
-    def newShow(self, show_to_add=None, other_shows=None):
+    def newShow(self, show_to_add=None, other_shows=None, search_string=None):
         """
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, file="home_newShow.tmpl")
-        t.submenu = self.HomeMenu()
-        t.enable_anime_options = True
-        
+        t = PageTemplate(rh=self, file="home_newShow.mako")
+
         indexer, show_dir, indexer_id, show_name = self.split_extra_show(show_to_add)
 
         if indexer_id and indexer and show_name:
@@ -2464,17 +2442,18 @@ class HomeAddShows(Home):
         else:
             use_provided_info = False
 
-        # tell the template whether we're giving it show name & Indexer ID
-        t.use_provided_info = use_provided_info
-
         # use the given show_dir for the indexer search if available
         if not show_dir:
-            t.default_show_name = ''
+            if search_string:
+                default_show_name = search_string
+            else:
+                default_show_name = ''
+
         elif not show_name:
-            t.default_show_name = re.sub(' \(\d{4}\)', '',
+            default_show_name = re.sub(' \(\d{4}\)', '',
                                          ek.ek(os.path.basename, ek.ek(os.path.normpath, show_dir)).replace('.', ' '))
         else:
-            t.default_show_name = show_name
+            default_show_name = show_name
 
         # carry a list of other dirs if given
         if not other_shows:
@@ -2482,36 +2461,31 @@ class HomeAddShows(Home):
         elif type(other_shows) != list:
             other_shows = [other_shows]
 
-        if use_provided_info:
-            t.provided_indexer_id = int(indexer_id or 0)
-            t.provided_indexer_name = show_name
+        provided_indexer_id = int(indexer_id or 0)
+        provided_indexer_name = show_name
 
-        t.provided_show_dir = show_dir
-        t.other_shows = other_shows
-        t.provided_indexer = int(indexer or sickbeard.INDEXER_DEFAULT)
-        t.indexers = sickbeard.indexerApi().indexers
-        t.whitelist = []
-        t.blacklist = []
-        t.groups = []
-        return t.respond()
+        provided_indexer = int(indexer or sickbeard.INDEXER_DEFAULT)
+
+        return t.render(submenu=self.HomeMenu(), enable_anime_options=True,
+                use_provided_info=use_provided_info, default_show_name=default_show_name, other_shows=other_shows,
+                provided_show_dir=show_dir, provided_indexer_id=provided_indexer_id, provided_indexer_name=provided_indexer_name,
+                provided_indexer=provided_indexer, indexers=sickbeard.indexerApi().indexers, whitelist=[], blacklist=[], groups=[],
+                title='New Show', header='New Show'
+        )
 
     def recommendedShows(self):
         """
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, file="home_recommendedShows.tmpl")
-        t.submenu = self.HomeMenu()
-        t.enable_anime_options = False
-        
-        return t.respond()
+        t = PageTemplate(rh=self, file="home_recommendedShows.mako")
+        return t.render(title="Recommended Shows", header="Recommended Shows", submenu=self.HomeMenu(), enable_anime_options=False)
 
     def getRecommendedShows(self):
-        t = PageTemplate(rh=self, file="trendingShows.tmpl")
-        t.submenu = self.HomeMenu()
+        t = PageTemplate(rh=self, file="trendingShows.mako")
+
+        trending_shows = []
 
-        t.trending_shows = []
-        
         trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)
 
         try:
@@ -2524,7 +2498,7 @@ class HomeAddShows(Home):
             shows = trakt_api.traktRequest("recommendations/shows?extended=full,images") or []
 
             library_shows = trakt_api.traktRequest("sync/collection/shows?extended=full") or []
-            
+
             for show_detail in shows:
                 show = {}
                 show['show']=show_detail
@@ -2534,44 +2508,40 @@ class HomeAddShows(Home):
                     if not helpers.findCertainShow(sickbeard.showList, [tvdb_id, tvrage_id]):
                         if show['show']['ids']['tvdb'] not in (lshow['show']['ids']['tvdb'] for lshow in library_shows):
                             if not_liked_show:
-                                if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'):	
-                                    t.trending_shows += [show]
+                                if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'):
+                                    trending_shows += [show]
                             else:
-                                t.trending_shows += [show]
+                                trending_shows += [show]
 
                 except exceptions.MultipleShowObjectsException:
                     continue
 
             if sickbeard.TRAKT_BLACKLIST_NAME != '':
-                t.blacklist = True
+                blacklist = True
             else:
-                t.blacklist = False
+                blacklist = False
 
         except traktException as e:
             logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
 
-        return t.respond()
+        return t.render(title="Trending Shows", header="Trending Shows", trending_shows=trending_shows, blacklist=blacklist, submenu=self.HomeMenu())
 
     def trendingShows(self):
         """
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, file="home_trendingShows.tmpl")
-        t.submenu = self.HomeMenu()
-        t.enable_anime_options = False
-
-        return t.respond()
+        t = PageTemplate(rh=self, file="home_trendingShows.mako")
+        return t.render(submenu=self.HomeMenu(), enable_anime_options=False)
 
     def getTrendingShows(self):
         """
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, file="trendingShows.tmpl")
-        t.submenu = self.HomeMenu()
+        t = PageTemplate(rh=self, file="trendingShows.mako")
 
-        t.trending_shows = []
+        trending_shows = []
 
         trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)
 
@@ -2595,22 +2565,37 @@ class HomeAddShows(Home):
                         if show['show']['ids']['tvdb'] not in (lshow['show']['ids']['tvdb'] for lshow in library_shows):
                             if not_liked_show:
                                 if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'):
-                                    t.trending_shows += [show]
+                                    trending_shows += [show]
                             else:
-                                t.trending_shows += [show]
+                                trending_shows += [show]
 
                 except exceptions.MultipleShowObjectsException:
                     continue
 
             if sickbeard.TRAKT_BLACKLIST_NAME != '':
-                t.blacklist = True
+                blacklist = True
             else:
-                t.blacklist = False
+                blacklist = False
 
         except traktException as e:
             logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
 
-        return t.respond()
+        return t.render(submenu = self.HomeMenu(), blacklist=blacklist, trending_shows=trending_shows)
+
+
+    def popularShows(self):
+        """
+        Fetches data from IMDB to show a list of popular shows.
+        """
+        t = PageTemplate(rh=self, file="home_popularShows.mako")
+
+        try:
+            popular_shows = imdbPopular.fetch_popular_shows()
+        except:
+            popular_shows = None
+
+        return t.render(submenu = self.HomeMenu(), popular_shows=popular_shows)
+
 
     def addShowToBlacklist(self, indexer_id):
 
@@ -2635,11 +2620,8 @@ class HomeAddShows(Home):
         """
         Prints out the page to add existing shows from a root dir
         """
-        t = PageTemplate(rh=self, file="home_addExistingShow.tmpl")
-        t.submenu = self.HomeMenu()
-        t.enable_anime_options = False
-        
-        return t.respond()
+        t = PageTemplate(rh=self, file="home_addExistingShow.mako")
+        return t.render(submenu=self.HomeMenu(), enable_anime_options=False, title='Existing Show', header='Existing Show', topmenu="home")
 
     def addTraktShow(self, indexer_id, showName):
         if helpers.findCertainShow(sickbeard.showList, int(indexer_id)):
@@ -2773,7 +2755,7 @@ class HomeAddShows(Home):
             whitelist = short_group_names(whitelist)
         if blacklist:
             blacklist = short_group_names(blacklist)
-        
+
         if not anyQualities:
             anyQualities = []
         if not bestQualities:
@@ -2901,9 +2883,8 @@ class Manage(Home, WebRoot):
         return menu
 
     def index(self):
-        t = PageTemplate(rh=self, file="manage.tmpl")
-        t.submenu = self.ManageMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="manage.mako")
+        return t.render(submenu=self.ManageMenu(), title='Mass Update', header='Mass Update')
 
 
     def showEpisodeStatuses(self, indexer_id, whichStatus):
@@ -2931,20 +2912,18 @@ class Manage(Home, WebRoot):
 
     def episodeStatuses(self, whichStatus=None):
         if whichStatus:
-            whichStatus = int(whichStatus)
-            status_list = [whichStatus]
+            status_list = [int(whichStatus)]
             if status_list[0] == SNATCHED:
-                status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER
+                status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST
         else:
             status_list = []
 
-        t = PageTemplate(rh=self, file="manage_episodeStatuses.tmpl")
-        t.submenu = self.ManageMenu()
-        t.whichStatus = whichStatus
+        t = PageTemplate(rh=self, file="manage_episodeStatuses.mako")
 
         # if we have no status then this is as far as we need to go
         if not status_list:
-            return t.respond()
+            return t.render(title="Episode Overview",  header="Episode Overview",
+                    topmenu="manage", submenu=self.ManageMenu(), whichStatus=whichStatus)
 
         myDB = db.DBConnection()
         status_results = myDB.select(
@@ -2967,10 +2946,9 @@ class Manage(Home, WebRoot):
             if cur_indexer_id not in sorted_show_ids:
                 sorted_show_ids.append(cur_indexer_id)
 
-        t.show_names = show_names
-        t.ep_counts = ep_counts
-        t.sorted_show_ids = sorted_show_ids
-        return t.respond()
+        return t.render(title="Episode Overview",  header="Episode Overview",
+                    topmenu="manage", submenu=self.ManageMenu(), whichStatus=whichStatus,
+                    show_names=show_names, ep_counts=ep_counts, sorted_show_ids=sorted_show_ids)
 
 
     def changeEpisodeStatuses(self, oldStatus, newStatus, *args, **kwargs):
@@ -3043,12 +3021,10 @@ class Manage(Home, WebRoot):
 
     def subtitleMissed(self, whichSubs=None):
 
-        t = PageTemplate(rh=self, file="manage_subtitleMissed.tmpl")
-        t.submenu = self.ManageMenu()
-        t.whichSubs = whichSubs
+        t = PageTemplate(rh=self, file="manage_subtitleMissed.mako")
 
         if not whichSubs:
-            return t.respond()
+            return t.render(submenu=self.ManageMenu(), whichSubs=whichSubs, title='Episode Overview', header='Episode Overview')
 
         myDB = db.DBConnection()
         status_results = myDB.select(
@@ -3077,10 +3053,8 @@ class Manage(Home, WebRoot):
             if cur_indexer_id not in sorted_show_ids:
                 sorted_show_ids.append(cur_indexer_id)
 
-        t.show_names = show_names
-        t.ep_counts = ep_counts
-        t.sorted_show_ids = sorted_show_ids
-        return t.respond()
+        return t.render(submenu=self.ManageMenu(), whichSubs=whichSubs, show_names=show_names, ep_counts=ep_counts, sorted_show_ids=sorted_show_ids,
+                        title='Episode Overview', header='Episode Overview')
 
 
     def downloadSubtitleMissed(self, *args, **kwargs):
@@ -3130,8 +3104,7 @@ class Manage(Home, WebRoot):
 
     def backlogOverview(self):
 
-        t = PageTemplate(rh=self, file="manage_backlogOverview.tmpl")
-        t.submenu = self.ManageMenu()
+        t = PageTemplate(rh=self, file="manage_backlogOverview.mako")
 
         showCounts = {}
         showCats = {}
@@ -3163,17 +3136,13 @@ class Manage(Home, WebRoot):
             showCats[curShow.indexerid] = epCats
             showSQLResults[curShow.indexerid] = sqlResults
 
-        t.showCounts = showCounts
-        t.showCats = showCats
-        t.showSQLResults = showSQLResults
-
-        return t.respond()
+        return t.render(submenu=self.ManageMenu(), showCounts=showCounts, showCats=showCats, showSQLResults=showSQLResults,
+                        title='Backlog Overview', header='Backlog Overview')
 
 
     def massEdit(self, toEdit=None):
 
-        t = PageTemplate(rh=self, file="manage_massEdit.tmpl")
-        t.submenu = self.ManageMenu()
+        t = PageTemplate(rh=self, file="manage_massEdit.mako")
 
         if not toEdit:
             return self.redirect("/manage/")
@@ -3288,20 +3257,22 @@ class Manage(Home, WebRoot):
                 else:
                     last_air_by_date = curShow.air_by_date
 
-        t.showList = toEdit
-        t.archive_firstmatch_value = last_archive_firstmatch if archive_firstmatch_all_same else None
-        t.default_ep_status_value = last_default_ep_status if default_ep_status_all_same else None
-        t.paused_value = last_paused if paused_all_same else None
-        t.anime_value = last_anime if anime_all_same else None
-        t.flatten_folders_value = last_flatten_folders if flatten_folders_all_same else None
-        t.quality_value = last_quality if quality_all_same else None
-        t.subtitles_value = last_subtitles if subtitles_all_same else None
-        t.scene_value = last_scene if scene_all_same else None
-        t.sports_value = last_sports if sports_all_same else None
-        t.air_by_date_value = last_air_by_date if air_by_date_all_same else None
-        t.root_dir_list = root_dir_list
+        archive_firstmatch_value = last_archive_firstmatch if archive_firstmatch_all_same else None
+        default_ep_status_value = last_default_ep_status if default_ep_status_all_same else None
+        paused_value = last_paused if paused_all_same else None
+        anime_value = last_anime if anime_all_same else None
+        flatten_folders_value = last_flatten_folders if flatten_folders_all_same else None
+        quality_value = last_quality if quality_all_same else None
+        subtitles_value = last_subtitles if subtitles_all_same else None
+        scene_value = last_scene if scene_all_same else None
+        sports_value = last_sports if sports_all_same else None
+        air_by_date_value = last_air_by_date if air_by_date_all_same else None
+        root_dir_list = root_dir_list
 
-        return t.respond()
+        return t.render(submenu=self.ManageMenu(), showList=toEdit, archive_firstmatch_value=archive_firstmatch_value, default_ep_status_value=default_ep_status_value,
+                        paused_value=paused_value, anime_value=anime_value, flatten_folders_value=flatten_folders_value,
+                        quality_value=quality_value, subtitles_value=subtitles_value, scene_value=scene_value, sports_value=sports_value,
+                        air_by_date_value=air_by_date_value, root_dir_list=root_dir_list, title='Mass Edit', header='Mass Edit')
 
 
     def massEditSubmit(self, archive_firstmatch=None, paused=None, default_ep_status=None,
@@ -3537,31 +3508,31 @@ class Manage(Home, WebRoot):
 
     def manageTorrents(self):
 
-        t = PageTemplate(rh=self, file="manage_torrents.tmpl")
-        t.info_download_station = ''
-        t.submenu = self.ManageMenu()
+        t = PageTemplate(rh=self, file="manage_torrents.mako")
+        info_download_station = ''
 
         if re.search('localhost', sickbeard.TORRENT_HOST):
 
             if sickbeard.LOCALHOST_IP == '':
-                t.webui_url = re.sub('localhost', helpers.get_lan_ip(), sickbeard.TORRENT_HOST)
+                webui_url = re.sub('localhost', helpers.get_lan_ip(), sickbeard.TORRENT_HOST)
             else:
-                t.webui_url = re.sub('localhost', sickbeard.LOCALHOST_IP, sickbeard.TORRENT_HOST)
+                webui_url = re.sub('localhost', sickbeard.LOCALHOST_IP, sickbeard.TORRENT_HOST)
         else:
-            t.webui_url = sickbeard.TORRENT_HOST
+            webui_url = sickbeard.TORRENT_HOST
 
         if sickbeard.TORRENT_METHOD == 'utorrent':
-            t.webui_url = '/'.join(s.strip('/') for s in (t.webui_url, 'gui/'))
+            webui_url = '/'.join(s.strip('/') for s in (webui_url, 'gui/'))
         if sickbeard.TORRENT_METHOD == 'download_station':
-            if helpers.check_url(t.webui_url + 'download/'):
-                t.webui_url = t.webui_url + 'download/'
+            if helpers.check_url(webui_url + 'download/'):
+                webui_url = webui_url + 'download/'
             else:
-                t.info_download_station = '<p>To have a better experience please set the Download Station alias as <code>download</code>, you can check this setting in the Synology DSM <b>Control Panel</b> > <b>Application Portal</b>. Make sure you allow DSM to be embedded with iFrames too in <b>Control Panel</b> > <b>DSM Settings</b> > <b>Security</b>.</p><br/><p>There is more information about this available <a href="https://github.com/midgetspy/Sick-Beard/pull/338">here</a>.</p><br/>'
+                info_download_station = '<p>To have a better experience please set the Download Station alias as <code>download</code>, you can check this setting in the Synology DSM <b>Control Panel</b> > <b>Application Portal</b>. Make sure you allow DSM to be embedded with iFrames too in <b>Control Panel</b> > <b>DSM Settings</b> > <b>Security</b>.</p><br/><p>There is more information about this available <a href="https://github.com/midgetspy/Sick-Beard/pull/338">here</a>.</p><br/>'
 
         if not sickbeard.TORRENT_PASSWORD == "" and not sickbeard.TORRENT_USERNAME == "":
-            t.webui_url = re.sub('://', '://' + str(sickbeard.TORRENT_USERNAME) + ':' + str(sickbeard.TORRENT_PASSWORD) + '@' ,t.webui_url)
+            webui_url = re.sub('://', '://' + str(sickbeard.TORRENT_USERNAME) + ':' + str(sickbeard.TORRENT_PASSWORD) + '@' ,webui_url)
 
-        return t.respond()
+        return t.render(submenu=self.ManageMenu(), webui_url=webui_url, info_download_station=info_download_station,
+                        title='Manage Torrents', header='Manage Torrents')
 
 
     def failedDownloads(self, limit=100, toRemove=None):
@@ -3581,12 +3552,9 @@ class Manage(Home, WebRoot):
         if toRemove:
             return self.redirect('/manage/failedDownloads/')
 
-        t = PageTemplate(rh=self, file="manage_failedDownloads.tmpl")
-        t.failedResults = sqlResults
-        t.limit = limit
-        t.submenu = self.ManageMenu()
+        t = PageTemplate(rh=self, file="manage_failedDownloads.mako")
 
-        return t.respond()
+        return t.render(submenu=self.ManageMenu(), limit=limit, failedResults=sqlResults, title='Failed Downloads', header='Failed Downloads')
 
 
 @route('/manage/manageSearches(/?.*)')
@@ -3595,17 +3563,13 @@ class ManageSearches(Manage):
         super(ManageSearches, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="manage_manageSearches.tmpl")
+        t = PageTemplate(rh=self, file="manage_manageSearches.mako")
         # t.backlogPI = sickbeard.backlogSearchScheduler.action.getProgressIndicator()
-        t.backlogPaused = sickbeard.searchQueueScheduler.action.is_backlog_paused()
-        t.backlogRunning = sickbeard.searchQueueScheduler.action.is_backlog_in_progress()
-        t.dailySearchStatus = sickbeard.dailySearchScheduler.action.amActive
-        t.findPropersStatus = sickbeard.properFinderScheduler.action.amActive
-        t.queueLength = sickbeard.searchQueueScheduler.action.queue_length()
 
-        t.submenu = self.ManageMenu()
-
-        return t.respond()
+        return t.render(submenu=self.ManageMenu(), backlogPaused=sickbeard.searchQueueScheduler.action.is_backlog_paused(),
+                        backlogRunning=sickbeard.searchQueueScheduler.action.is_backlog_in_progress(), dailySearchStatus=sickbeard.dailySearchScheduler.action.amActive,
+                        findPropersStatus=sickbeard.properFinderScheduler.action.amActive, queueLength=sickbeard.searchQueueScheduler.action.queue_length(),
+                        title='Manage Searches', header='Manage Searches')
 
     def forceBacklog(self):
         # force it to run the next time it looks
@@ -3711,16 +3675,13 @@ class History(WebRoot):
                 history['actions'].append(action)
                 history['actions'].sort(key=lambda x: x['time'], reverse=True)
 
-        t = PageTemplate(rh=self, file="history.tmpl")
-        t.historyResults = sqlResults
-        t.compactResults = compact
-        t.limit = limit
-        t.submenu = [
+        t = PageTemplate(rh=self, file="history.mako")
+        submenu = [
             {'title': 'Clear History', 'path': 'history/clearHistory'},
             {'title': 'Trim History', 'path': 'history/trimHistory'},
         ]
 
-        return t.respond()
+        return t.render(historyResults=sqlResults, compactResults=compact, limit=limit, submenu=submenu, title='History', header='History')
 
 
     def clearHistory(self):
@@ -3762,10 +3723,9 @@ class Config(WebRoot):
         return menu
 
     def index(self):
-        t = PageTemplate(rh=self, file="config.tmpl")
-        t.submenu = self.ConfigMenu()
+        t = PageTemplate(rh=self, file="config.mako")
 
-        return t.respond()
+        return t.render(submenu=self.ConfigMenu(), title='Configuration', header='Configuration', topmenu="config")
 
 
 @route('/config/general(/?.*)')
@@ -3774,10 +3734,9 @@ class ConfigGeneral(Config):
         super(ConfigGeneral, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_general.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_general.mako")
 
+        return t.render(title='Config - General', header='General Configuration', topmenu='config', submenu=self.ConfigMenu())
 
     def generateApiKey(self):
         return helpers.generateApiKey()
@@ -3853,7 +3812,7 @@ class ConfigGeneral(Config):
         sickbeard.GIT_PASSWORD = git_password
         #sickbeard.GIT_RESET = config.checkbox_to_value(git_reset)
         #Force GIT_RESET
-        sickbeard.GIT_RESET = 1        
+        sickbeard.GIT_RESET = 1
         sickbeard.GIT_AUTOISSUES = config.checkbox_to_value(git_autoissues)
         sickbeard.GIT_PATH = git_path
         sickbeard.GIT_REMOTE = git_remote
@@ -3864,7 +3823,7 @@ class ConfigGeneral(Config):
         # sickbeard.LOG_DIR is set in config.change_LOG_DIR()
         sickbeard.COMING_EPS_MISSED_RANGE = config.to_int(coming_eps_missed_range,default=7)
         sickbeard.DISPLAY_ALL_SEASONS = config.checkbox_to_value(display_all_seasons)
-        
+
         sickbeard.WEB_PORT = config.to_int(web_port)
         sickbeard.WEB_IPV6 = config.checkbox_to_value(web_ipv6)
         # sickbeard.WEB_LOG is set in config.change_LOG_DIR()
@@ -3913,7 +3872,7 @@ class ConfigGeneral(Config):
         sickbeard.HANDLE_REVERSE_PROXY = config.checkbox_to_value(handle_reverse_proxy)
 
         sickbeard.THEME_NAME = theme_name
-        
+
         sickbeard.DEFAULT_PAGE = default_page
 
         sickbeard.save_config()
@@ -3935,9 +3894,9 @@ class ConfigBackupRestore(Config):
         super(ConfigBackupRestore, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_backuprestore.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_backuprestore.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Backup/Restore', header='Backup/Restore', topmenu='comingEpisodes')
 
     def backup(self, backupDir=None):
 
@@ -3995,9 +3954,9 @@ class ConfigSearch(Config):
         super(ConfigSearch, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_search.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_search.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Episode Search', header='Search Settings')
 
 
     def saveSearch(self, use_nzbs=None, use_torrents=None, nzb_dir=None, sab_username=None, sab_password=None,
@@ -4068,7 +4027,7 @@ class ConfigSearch(Config):
         sickbeard.TORRENT_LABEL = torrent_label
         sickbeard.TORRENT_LABEL_ANIME = torrent_label_anime
         sickbeard.TORRENT_VERIFY_CERT = config.checkbox_to_value(torrent_verify_cert)
-        sickbeard.TORRENT_PATH = torrent_path
+        sickbeard.TORRENT_PATH = torrent_path.rstrip('/\\')
         sickbeard.TORRENT_SEED_TIME = torrent_seed_time
         sickbeard.TORRENT_PAUSED = config.checkbox_to_value(torrent_paused)
         sickbeard.TORRENT_HIGH_BANDWIDTH = config.checkbox_to_value(torrent_high_bandwidth)
@@ -4095,9 +4054,9 @@ class ConfigPostProcessing(Config):
         super(ConfigPostProcessing, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_postProcessing.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_postProcessing.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Post Processing', header='Post Processing')
 
 
     def savePostProcessing(self, naming_pattern=None, naming_multi_ep=None,
@@ -4285,9 +4244,9 @@ class ConfigProviders(Config):
         super(ConfigProviders, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_providers.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_providers.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Providers', header='Search Providers')
 
 
     def canAddNewznabProvider(self, name):
@@ -4762,9 +4721,9 @@ class ConfigNotifications(Config):
         super(ConfigNotifications, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_notifications.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_notifications.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Notifications', header='Notifications')
 
 
     def saveNotifications(self, use_kodi=None, kodi_always_on=None, kodi_notify_onsnatch=None,
@@ -4784,13 +4743,13 @@ class ConfigNotifications(Config):
                           use_prowl=None, prowl_notify_onsnatch=None, prowl_notify_ondownload=None,
                           prowl_notify_onsubtitledownload=None, prowl_api=None, prowl_priority=0,
                           use_twitter=None, twitter_notify_onsnatch=None, twitter_notify_ondownload=None,
-                          twitter_notify_onsubtitledownload=None,
+                          twitter_notify_onsubtitledownload=None, twitter_usedm=None, twitter_dmto=None,
                           use_boxcar=None, boxcar_notify_onsnatch=None, boxcar_notify_ondownload=None,
                           boxcar_notify_onsubtitledownload=None, boxcar_username=None,
                           use_boxcar2=None, boxcar2_notify_onsnatch=None, boxcar2_notify_ondownload=None,
                           boxcar2_notify_onsubtitledownload=None, boxcar2_accesstoken=None,
                           use_pushover=None, pushover_notify_onsnatch=None, pushover_notify_ondownload=None,
-                          pushover_notify_onsubtitledownload=None, pushover_userkey=None, pushover_apikey=None, pushover_device=None,
+                          pushover_notify_onsubtitledownload=None, pushover_userkey=None, pushover_apikey=None, pushover_device=None, pushover_sound=None,
                           use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None,
                           libnotify_notify_onsubtitledownload=None,
                           use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None, use_synoindex=None,
@@ -4874,6 +4833,8 @@ class ConfigNotifications(Config):
         sickbeard.TWITTER_NOTIFY_ONSNATCH = config.checkbox_to_value(twitter_notify_onsnatch)
         sickbeard.TWITTER_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(twitter_notify_ondownload)
         sickbeard.TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(twitter_notify_onsubtitledownload)
+        sickbeard.TWITTER_USEDM = config.checkbox_to_value(twitter_usedm)
+        sickbeard.TWITTER_DMTO = twitter_dmto
 
         sickbeard.USE_BOXCAR = config.checkbox_to_value(use_boxcar)
         sickbeard.BOXCAR_NOTIFY_ONSNATCH = config.checkbox_to_value(boxcar_notify_onsnatch)
@@ -4894,6 +4855,7 @@ class ConfigNotifications(Config):
         sickbeard.PUSHOVER_USERKEY = pushover_userkey
         sickbeard.PUSHOVER_APIKEY = pushover_apikey
         sickbeard.PUSHOVER_DEVICE = pushover_device
+        sickbeard.PUSHOVER_SOUND = pushover_sound
 
         sickbeard.USE_LIBNOTIFY = config.checkbox_to_value(use_libnotify)
         sickbeard.LIBNOTIFY_NOTIFY_ONSNATCH = config.checkbox_to_value(libnotify_notify_onsnatch)
@@ -4937,6 +4899,8 @@ class ConfigNotifications(Config):
         sickbeard.TRAKT_ROLLING_ADD_PAUSED = config.checkbox_to_value(trakt_rolling_add_paused)
         sickbeard.TRAKT_ROLLING_FREQUENCY = int(trakt_rolling_frequency)
 
+        sickbeard.USE_IMDB_POPULAR = config.checkbox_to_value(use_imdb_popular)
+
         sickbeard.USE_EMAIL = config.checkbox_to_value(use_email)
         sickbeard.EMAIL_NOTIFY_ONSNATCH = config.checkbox_to_value(email_notify_onsnatch)
         sickbeard.EMAIL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(email_notify_ondownload)
@@ -4997,9 +4961,9 @@ class ConfigSubtitles(Config):
         super(ConfigSubtitles, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, file="config_subtitles.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_subtitles.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Subtitles', header='Subtitles')
 
 
     def saveSubtitles(self, use_subtitles=None, subtitles_plugins=None, subtitles_languages=None, subtitles_dir=None,
@@ -5050,9 +5014,9 @@ class ConfigAnime(Config):
 
     def index(self):
 
-        t = PageTemplate(rh=self, file="config_anime.tmpl")
-        t.submenu = self.ConfigMenu()
-        return t.respond()
+        t = PageTemplate(rh=self, file="config_anime.mako")
+
+        return t.render(submenu=self.ConfigMenu(), title='Config - Anime', header='Anime')
 
 
     def saveAnime(self, use_anidb=None, anidb_username=None, anidb_password=None, anidb_use_mylist=None,
@@ -5087,17 +5051,15 @@ class ErrorLogs(WebRoot):
     def ErrorLogsMenu(self):
         menu = [
             {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'},
-            {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveErrors, 'confirm': True},
+            {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveErrors(), 'confirm': True},
         ]
 
         return menu
 
     def index(self):
 
-        t = PageTemplate(rh=self, file="errorlogs.tmpl")
-        t.submenu = self.ErrorLogsMenu()
-
-        return t.respond()
+        t = PageTemplate(rh=self, file="errorlogs.mako")
+        return t.render(header="Logs &amp; Errors", title="Logs &amp; Errors", topmenu="errorlogs", submenu=self.ErrorLogsMenu())
 
     def haveErrors(self):
         if len(classes.ErrorViewer.errors) > 0:
@@ -5108,15 +5070,15 @@ class ErrorLogs(WebRoot):
         return self.redirect("/errorlogs/")
 
     def viewlog(self, minLevel=logger.INFO, logFilter="<NONE>",logSearch=None, maxLines=500):
-        
+
         def Get_Data(Levelmin, data_in, lines_in, regex, Filter, Search, mlines):
-            
+
             lastLine = False
             numLines = lines_in
             numToShow = min(maxLines, numLines + len(data_in))
-            
-            finalData = [] 
-            
+
+            finalData = []
+
             for x in reversed(data_in):
 
                 x = ek.ss(x)
@@ -5147,15 +5109,14 @@ class ErrorLogs(WebRoot):
                     finalData.append("AA" + x)
                     numLines += 1
 
-                
+
 
                 if numLines >= numToShow:
                     return finalData
-                
+
             return finalData
-            
-        t = PageTemplate(rh=self, file="viewlogs.tmpl")
-        t.submenu = self.ErrorLogsMenu()
+
+        t = PageTemplate(rh=self, file="viewlogs.mako")
 
         minLevel = int(minLevel)
 
@@ -5182,27 +5143,21 @@ class ErrorLogs(WebRoot):
             logFilter = '<NONE>'
 
         regex = "^(\d\d\d\d)\-(\d\d)\-(\d\d)\s*(\d\d)\:(\d\d):(\d\d)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$"
-        
+
         data = []
-        
+
         if os.path.isfile(logger.logFile):
             with ek.ek(codecs.open, *[logger.logFile, 'r', 'utf-8']) as f:
                 data = Get_Data(minLevel, f.readlines(), 0, regex, logFilter, logSearch, maxLines)
-                
+
         for i in range (1 , int(sickbeard.LOG_NR)):
             if os.path.isfile(logger.logFile + "." + str(i)) and (len(data) <= maxLines):
                 with ek.ek(codecs.open, *[logger.logFile + "." + str(i), 'r', 'utf-8']) as f:
                         data += Get_Data(minLevel, f.readlines(), len(data), regex, logFilter, logSearch, maxLines)
 
-        result = "".join(data)
-
-        t.logLines = result
-        t.minLevel = minLevel
-        t.logNameFilters = logNameFilters
-        t.logFilter = logFilter
-        t.logSearch = logSearch
-
-        return t.respond()
+        return t.render(header="Log File", title="Logs", topmenu="errorlogs", submenu=self.ErrorLogsMenu(),
+                logLines="".join(data), minLevel=minLevel, logNameFilters=logNameFilters,
+                logFilter=logFilter, logSearch=logSearch)
 
     def submit_errors(self):
         if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD):
@@ -5212,7 +5167,7 @@ class ErrorLogs(WebRoot):
             issue_id = logger.submit_errors()
             if issue_id == 'RUNNING':
                 ui.notifications.message('Issue submitter is running, please wait for it to complete')
-            elif issue_id: 
+            elif issue_id:
                 ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue_id)
 
         return self.redirect("/errorlogs/")