1. Martin Tournoij
  2. Frederick

Commits

Martin Tournoij  committed a61649d

Add CherryPy

  • Participants
  • Parent commits d57c6f3
  • Branches default

Comments (0)

Files changed (37)

File README.md

View file
 
 Installation & running
 ======================
-Frederick requires [Python][python], at the moment only Python 2 will work since
-feedparser doesn’t work with Python 3 yet.  
-You will need the [feedparser][feedparser] and [Jinja2][jinja2] modules. You’ll also need the
-sqlite3 module, which is included in Python, but sometimes distributed as a
-separate module.
 
-To start the server, run `serve.py`, this will start the built-in HTTP server
-port 8000. At the moment, this the only supported way to run Frederick. Point
-your browser to `http://(ip|hostname):8000/cgi-bin/index.py`.
+Dependencies
+------------
+- Some server (Only UNIX/Linux was tested, Windows may or may not work)
+- [Python 2](http://python.org/) (Python 3 will not work)
+- [Jinja2](http://jinja.pocoo.org/docs/)
+- py-sqlite3 (Included in Python, sometimes a separate package)
+- [CherryPy](http://www.cherrypy.org/)
+- [FeedParser](https://code.google.com/p/feedparser/)
 
-Note that there’s no authentication, you almost certainly don’t want
-to run this on a publicly accessible server.
+Running
+-------
+To start the server, run `serve.py`, this will start a HTTP server port 8000.
+You can optionally add a server & port to listen on as `address:port`
+
+Note that there’s no authentication, you almost certainly don’t want to run this
+on a publicly accessible server.
 
 Frederick should work on any size device, but will probably have problems in
 older browsers (such as IE8 and earlier).
 
 Updating feeds
 --------------
-Feeds automatically are updated on pageview if the last update was more than 30 minutes ago.
+Feeds automatically are updated on pageview if the last update was more than 30
+minutes ago.
 
 You can also run `update.py` to update Frederick, you probably want to run this
 from cron to prevent ‘gaps’ in your item list.
 ChangeLog
 =========
 
+Version 1.2, To be released
+---------------------------
+- Replace the Python's builtin HTTP server with CheryPy
+
 Version 1.1, 20130725
 --------------------
 - Many minor changes
 - Initial release
 
 
-[python]: http://python.org
-[feedparser]: https://code.google.com/p/feedparser/
-[jinja2]: http://jinja.pocoo.org/docs/
+Credits
+=======
+Copyright © 2013 Martin Tournoij <martin@arp242.net>  
+MIT license applies
+
+Frederick includes (in whole, or code based on):
+
+- [Bootstrap](http://getbootstrap.com/)
+- [Font awesome](http://fortawesome.github.io/Font-Awesome/)
+- [jQuery](http://jquery.com/)

File TODO

View file
+- Remove feeds
+- Truncate feed entries
+- Some sort of authorisation/password
+- 

File cgi-bin/index.py

-#!/usr/bin/env python2
-# encoding:utf-8
-#
-# http://code.arp242.net/frederick
-#
-# Copyright © 2013 Martin Tournoij <martin@arp242.net>
-# See below for full copyright
-# 
-
-
-# Feedparser won't work with Python 3, but we want to be as forward-compatible
-# as we can
-from __future__ import nested_scopes, division, absolute_import, with_statement, print_function, unicode_literals
-
-import cgi, codecs, datetime, hashlib, json, os, pickle, re, sys, time, traceback, urllib
-
-import feedparser, sqlite3
-from jinja2 import Environment, FileSystemLoader
-
-
-_root = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
-_db = None
-
-
-def JSONDefault(obj):
-	if obj.__class__.__name__ == 'datetime':
-		return obj.strftime('%Y-%m-%d %H:%M')
-
-
-def OpenDb():
-	global _db
-	_db = sqlite3.connect('%s/db/db.sqlite3' % _root,
-		detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
-
-	def dict_factory(cursor, row):
-		d = {}
-		for idx, col in enumerate(cursor.description):
-			d[col[0]] = row[idx]
-		return d
-	_db.row_factory = dict_factory
-
-
-def GetFeeds():
-	feeds = _db.execute('''
-		select feeds.*,
-			(select count(id) from entries where feed = feeds.id and read = 0) as numentries
-		from feeds
-	''').fetchall()
-
-	for f in feeds:
-		if not f.get('icon'): f['icon'] = '/tpl/img/favicon.png'
-			
-		exp = f['updated'] + datetime.timedelta(minutes=30)
-		if datetime.datetime.today() > exp:
-			AddEntries(f['id'], LoadFeed(f['url'])['entries'])
-
-	return feeds
-
-
-def Template(f, v):
-	env = Environment(loader=FileSystemLoader('%s/tpl' % _root))
-	env.filters['urlencode'] = lambda s: urllib.quote_plus(s, '')
-
-	return re.sub(r'>\s*<', '><', env.get_template(f).render(**v)).encode('utf-8')
-
-
-def MarkRead(eid):
-	c = _db.cursor()
-	c.execute('UPDATE entries set read = 1 where id = ?',
-		(eid,))
-	_db.commit()
-
-
-def CreateDb():
-	c = _db.cursor()
-
-	c.execute('''create table feeds (
-		id integer primary key autoincrement,
-		url text not null,
-		updated timestamp not null,
-		title text not null,
-		icon text
-	)''')
-
-	c.execute('''create table entries (
-		id integer primary key autoincrement,
-		hash text,
-		feed integer not null,
-		read integer not null default 0,
-		title text not null,
-		summary text not null,
-		link text not null,
-		date timestamp,
-		foreign key(feed) references feeds(id) on delete cascade
-	)''')
-
-	c.execute('create unique index uniquehash on entries (feed, hash)')
-
-	_db.commit()
-
-
-def LoadFeed(url):
-	feed = feedparser.parse(url)
-
-	icon = None
-	if feed.feed.get('icon'):
-		icon = feed.feed['icon']
-	elif feed.feed.get('image') and feed.feed.get('image').get('href'):
-		icon = feed.feed['image']['href']
-
-	r = {
-		'title': feed.feed.title.encode('utf-8'),
-		'icon': icon,
-		'entries': [],
-	}
-
-	for e in feed.entries:
-		if e.get('updated_parsed'):
-			date = e['updated_parsed']
-		elif e.get('published_parsed'):
-			date = e['published_parsed']
-
-		if e.get('id'):
-			id = e['id']
-		else:
-			id = e.link
-
-		r['entries'].append({
-			'hash': hashlib.sha256(id).hexdigest(),
-			'title': e.title,
-			'summary': e.summary,
-			'link': e.link,
-			'date': datetime.datetime(year=date.tm_year, month=date.tm_mon,
-				day=date.tm_mday, hour=date.tm_hour, minute=date.tm_min)
-		})
-
-	return r
-
-
-def AddFeed(url):
-	feed = LoadFeed(url)
-	c = _db.cursor()
-	c.execute('insert into feeds (url, title, icon, updated) values (?, ?, ?, ?)', (
-		url, feed['title'], feed['icon'], '1970-01-01 13:37:00'))
-	_db.commit()
-
-	AddEntries(c.lastrowid, feed['entries'])
-
-
-def AddEntries(fid, entries):
-	c = _db.cursor()
-	for e in entries:
-		try:
-			c.execute('insert into entries (hash, feed, title, summary, link, date) values (?, ?, ?, ?, ?, ?)', (
-				e['hash'], fid, e['title'], e['summary'], e['link'], e['date']))
-		except sqlite3.IntegrityError:
-			continue
-
-	c.execute('update feeds set updated = ? where id = ?',
-		(datetime.datetime.today(), fid))
-	_db.commit()
-
-
-def GetFeedById(fid):
-	c = _db.cursor()
-	c.execute('select * from feeds where id = ?', (fid,))
-	return c.fetchone()
-
-def GetEntriesByFeedId(fid):
-	c = _db.cursor()
-	c.execute('''select entries.* from entries
-		inner join feeds on feeds.id = entries.feed
-		where feeds.id = ?
-		and entries.read = 0
-		order by date desc
-	''', (fid,))
-
-	return c.fetchall()
-
-
-def GetEntriesByFeedname(name):
-	c = _db.cursor()
-	c.execute('''select entries.* from entries
-		inner join feeds on feeds.id = entries.feed
-		where feeds.title = ?
-		and entries.read = 0
-		order by date desc
-	''', (name,))
-
-	return c.fetchall()
-
-
-def Main():
-	OpenDb()
-	if len(_db.cursor().execute('select * from sqlite_master where type="table"').fetchall()) == 0:
-		CreateDb()
-
-	path = os.environ['PATH_INFO'].strip()
-	data = cgi.FieldStorage()
-	if path.startswith('/add-feed'):
-		AddFeed(data['url'].value)
-		return ''
-	elif path.startswith('/get-feeds'):
-		return json.dumps(GetFeeds(), default=JSONDefault)
-	elif path.startswith('/get-feed'):
-		return json.dumps({
-			'feeds': GetFeeds(),
-			'html': Template('entries.html', {
-				'entries': GetEntriesByFeedId(path.split('/')[2]),
-			}),
-		}, default=JSONDefault)
-	elif path.startswith('/mark-read'):
-		MarkRead(data['eid'].value)
-		return ''
-	elif path.startswith('/reload-feed'):
-		f = GetFeedById(data['feed'].value)
-		AddEntries(f['id'], LoadFeed(f['url'])['entries'])
-		return ''
-	else:
-		feeds = GetFeeds()
-		feed = None
-		entries = None
-		if path not in ['', '/']:
-			name = urllib.unquote_plus(path.lstrip('/'))
-			entries = GetEntriesByFeedname(name)
-			feed = [ f for f in feeds if f['title'] == name ][0]
-
-		return Template('main.html', {
-			'feeds': feeds,
-			'feed': feed,
-			'entries': entries,
-		})
-
-
-if __name__ == '__main__':
-	if sys.version_info[0] > 2:
-		print('HTTP/1.0 500 Error', end='\r\n')
-		print('Content-Type: text/html; charset=utf-8', end='\r\n')
-		print('')
-		print('index.py only works for Python 2 for now ...')
-		sys.exit(1)
-
-	try:
-		d = Main()
-		print('HTTP/1.0 200 Ok', end='\r\n')
-		print('Content-Type: text/html; charset=utf-8', end='\r\n')
-		print('')
-		print(d)
-	except:
-		print('HTTP/1.0 500 Error', end='\r\n')
-		print('Content-Type: text/plain; charset=utf-8', end='\r\n')
-		print('')
-		print(sys.exc_info()[0], sys.exc_info()[1])
-		print('')
-		traceback.print_tb(sys.exc_info()[2], file=sys.stdout)
-
-
-
-# The MIT License (MIT)
-#
-# Copyright © 2013 Martin Tournoij
-#
-# 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.

File frederick/__init__.py

View file
+#!/usr/bin/env python
+# encoding:utf-8
+#
+# http://code.arp242.net/frederick
+#
+# Copyright © 2013 Martin Tournoij <martin@arp242.net>
+# See below for full copyright
+# 
+
+
+# Feedparser won't work with Python 3, but we want to be as forward-compatible
+# as we can
+from __future__ import nested_scopes, division, absolute_import, with_statement, print_function, unicode_literals
+import datetime, hashlib, os, re, sys, urllib, sqlite3
+
+import feedparser
+from jinja2 import Environment, FileSystemLoader
+
+
+_root = os.path.dirname(os.path.realpath(sys.argv[0]))
+_db = None
+
+
+def opendb():
+	global _db
+	_db = sqlite3.connect('%s/db/db.sqlite3' % _root,
+		detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
+
+	def dict_factory(cursor, row):
+		d = {}
+		for idx, col in enumerate(cursor.description):
+			d[col[0]] = row[idx]
+		return d
+	_db.row_factory = dict_factory
+
+
+def getfeeds():
+	feeds = _db.execute('''
+		select feeds.*,
+			(select count(id) from entries where feed = feeds.id and read = 0) as numentries
+		from feeds
+	''').fetchall()
+
+	for f in feeds:
+		if not f.get('icon'): f['icon'] = '/tpl/img/favicon.png'
+			
+		exp = f['updated'] + datetime.timedelta(minutes=30)
+		if datetime.datetime.today() > exp:
+			addentries(f['id'], loadfeed(f['url'])['entries'])
+
+	return feeds
+
+
+def template(f, v):
+	env = Environment(loader=FileSystemLoader('%s/tpl' % _root))
+	env.filters['urlencode'] = lambda s: urllib.quote_plus(s, '')
+
+	return re.sub(r'>\s*<', '><', env.get_template(f).render(**v)).encode('utf-8')
+
+
+def markread(eid):
+	c = _db.cursor()
+	c.execute('update entries set read = 1 where id = ?',
+		(eid,))
+	_db.commit()
+
+
+def createdb():
+	c = _db.cursor()
+
+	c.execute('''create table feeds (
+		id integer primary key autoincrement,
+		url text not null,
+		updated timestamp not null,
+		title text not null,
+		icon text
+	)''')
+
+	c.execute('''create table entries (
+		id integer primary key autoincrement,
+		hash text,
+		feed integer not null,
+		read integer not null default 0,
+		title text not null,
+		summary text not null,
+		link text not null,
+		date timestamp,
+		foreign key(feed) references feeds(id) on delete cascade
+	)''')
+
+	c.execute('create unique index uniquehash on entries (feed, hash)')
+
+	_db.commit()
+
+
+def loadfeed(url):
+	feed = feedparser.parse(url)
+
+	icon = None
+	if feed.feed.get('icon'):
+		icon = feed.feed['icon']
+	elif feed.feed.get('image') and feed.feed.get('image').get('href'):
+		icon = feed.feed['image']['href']
+
+	r = {
+		'title': feed.feed.title.encode('utf-8'),
+		'icon': icon,
+		'entries': [],
+	}
+
+	for e in feed.entries:
+		if e.get('updated_parsed'):
+			date = e['updated_parsed']
+		elif e.get('published_parsed'):
+			date = e['published_parsed']
+
+		if e.get('id'):
+			id = e['id']
+		else:
+			id = e.link
+
+		r['entries'].append({
+			'hash': hashlib.sha256(id).hexdigest(),
+			'title': e.title,
+			'summary': e.summary,
+			'link': e.link,
+			'date': datetime.datetime(year=date.tm_year, month=date.tm_mon,
+				day=date.tm_mday, hour=date.tm_hour, minute=date.tm_min)
+		})
+
+	return r
+
+
+def addfeed(url):
+	feed = loadfeed(url)
+	c = _db.cursor()
+	c.execute('insert into feeds (url, title, icon, updated) values (?, ?, ?, ?)', (
+		url, feed['title'], feed['icon'], '1970-01-01 13:37:00'))
+	_db.commit()
+
+	addentries(c.lastrowid, feed['entries'])
+
+
+def addentries(fid, entries):
+	c = _db.cursor()
+	for e in entries:
+		try:
+			c.execute('insert into entries (hash, feed, title, summary, link, date) values (?, ?, ?, ?, ?, ?)', (
+				e['hash'], fid, e['title'], e['summary'], e['link'], e['date']))
+		except sqlite3.IntegrityError:
+			continue
+
+	c.execute('update feeds set updated = ? where id = ?',
+		(datetime.datetime.today(), fid))
+	_db.commit()
+
+
+def getfeedbyid(fid):
+	c = _db.cursor()
+	c.execute('select * from feeds where id = ?', (fid,))
+	return c.fetchone()
+
+
+def getentriesbyfeedid(fid):
+	c = _db.cursor()
+	c.execute('''select entries.* from entries
+		inner join feeds on feeds.id = entries.feed
+		where feeds.id = ?
+		and entries.read = 0
+		order by date desc
+	''', (fid,))
+
+	return c.fetchall()
+
+
+def getentriesbyfeedname(name):
+	c = _db.cursor()
+	c.execute('''select entries.* from entries
+		inner join feeds on feeds.id = entries.feed
+		where feeds.title = ?
+		and entries.read = 0
+		order by date desc
+	''', (name,))
+
+	return c.fetchall()
+
+
+def start():
+	opendb()
+	if len(_db.cursor().execute('select * from sqlite_master where type="table"').fetchall()) == 0:
+		createdb()
+
+
+# The MIT License (MIT)
+#
+# Copyright © 2013 Martin Tournoij
+#
+# 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.

File serve.py

View file
-#!/usr/bin/env python3
+#!/usr/bin/env python
+# encoding:utf-8
+#
+# http://code.arp242.net/frederick
+#
+# Copyright © 2013 Martin Tournoij <martin@arp242.net>
+# See below for full copyright
+#
+
 
 from __future__ import print_function
+import sys, os, json, urllib
 
-import sys
-if sys.version_info[0] < 3:
-	print('serve.py only works for Python 3 for now ...')
-	sys.exit(1)
+import cherrypy
 
-try:
-	from http.server import HTTPServer, CGIHTTPRequestHandler
-except ImportError:
-	from BaseHTTPServer import HTTPServer
-	from CGIHTTPServer import CGIHTTPRequestHandler
+import frederick
 
-HTTPServer(
-	('', 8000),
-	CGIHTTPRequestHandler
-).serve_forever()
+def JSONDefault(obj):
+	if obj.__class__.__name__ == 'datetime':
+		return obj.strftime('%Y-%m-%d %H:%M')
+
+
+class AgentCooper:
+	@cherrypy.expose
+	def index(self, feedname=None):
+		frederick.start()
+		feeds = frederick.getfeeds()
+		feed = None
+		entries = None
+
+		if feedname is not None:
+			name = urllib.unquote_plus(feedname.lstrip('/'))
+			entries = frederick.getentriesbyfeedname(name)
+			feed = [ f for f in feeds if f['title'] == name ][0]
+
+		return frederick.template('main.html', {
+			'feeds': feeds,
+			'feed': feed,
+			'entries': entries,
+		})
+
+
+	@cherrypy.expose
+	def default(self, feedname):
+		return self.index(feedname)
+
+
+	@cherrypy.expose
+	def get_feeds(self):
+		frederick.start()
+		return json.dumps(frederick.getfeeds(), default=JSONDefault)
+
+
+	@cherrypy.expose
+	def get_feed(self, id):
+		frederick.start()
+		return json.dumps({
+			'feeds': frederick.getfeeds(),
+			'html': frederick.template('entries.html', {
+				'entries': frederick.getentriesbyfeedid(id),
+			}),
+		}, default=JSONDefault)
+
+
+	@cherrypy.expose
+	def reload_feed(self, feed=None):
+		frederick.start()
+		f = frederick.getfeedbyid(feed)
+		return frederick.addentries(f['id'], frederick.loadfeed(f['url'])['entries'])
+
+
+	@cherrypy.expose
+	def mark_read(self, eid=None):
+		frederick.start()
+		return frederick.markread(eid)
+
+
+	@cherrypy.expose
+	def add_feed(self, url=None):
+		frederick.start()
+		return frederick.addfeed(url)
+
+
+server = '0.0.0.0'
+port = 8000
+
+if len(sys.argv) > 1:
+	listen = sys.argv[1].split(':')
+	server = listen[0]
+	if len(listen) > 1:
+		port = listen[1]
+
+cherrypy.config.update({
+	'server.socket_host': server,
+	'server.socket_port': port,
+})
+cherrypy.quickstart(AgentCooper(), config={
+	'/': {
+		'tools.staticdir.root': os.path.dirname(os.path.realpath(sys.argv[0])),
+	},
+	'/tpl': {
+		'tools.staticdir.on': 'True',
+		'tools.staticdir.dir': 'tpl',
+	},
+})
+
+
+# The MIT License (MIT)
+#
+# Copyright © 2013 Martin Tournoij
+#
+# 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.

File tpl/bootstrap/accordion.less

-//
-// Accordion
-// --------------------------------------------------
-
-
-// Parent container
-.accordion {
-  margin-bottom: @baseLineHeight;
-}
-
-// Group == heading + body
-.accordion-group {
-  margin-bottom: 2px;
-  border: 1px solid #e5e5e5;
-  .border-radius(@baseBorderRadius);
-}
-.accordion-heading {
-  border-bottom: 0;
-}
-.accordion-heading .accordion-toggle {
-  display: block;
-  padding: 8px 15px;
-}
-
-// General toggle styles
-.accordion-toggle {
-  cursor: pointer;
-}
-
-// Inner needs the styles because you can't animate properly with any styles on the element
-.accordion-inner {
-  padding: 9px 15px;
-  border-top: 1px solid #e5e5e5;
-}

File tpl/bootstrap/alerts.less

-//
-// Alerts
-// --------------------------------------------------
-
-
-// Base styles
-// -------------------------
-
-.alert {
-  padding: 8px 35px 8px 14px;
-  margin-bottom: @baseLineHeight;
-  text-shadow: 0 1px 0 rgba(255,255,255,.5);
-  background-color: @warningBackground;
-  border: 1px solid @warningBorder;
-  .border-radius(@baseBorderRadius);
-}
-.alert,
-.alert h4 {
-  // Specified for the h4 to prevent conflicts of changing @headingsColor
-  color: @warningText;
-}
-.alert h4 {
-  margin: 0;
-}
-
-// Adjust close link position
-.alert .close {
-  position: relative;
-  top: -2px;
-  right: -21px;
-  line-height: @baseLineHeight;
-}
-
-
-// Alternate styles
-// -------------------------
-
-.alert-success {
-  background-color: @successBackground;
-  border-color: @successBorder;
-  color: @successText;
-}
-.alert-success h4 {
-  color: @successText;
-}
-.alert-danger,
-.alert-error {
-  background-color: @errorBackground;
-  border-color: @errorBorder;
-  color: @errorText;
-}
-.alert-danger h4,
-.alert-error h4 {
-  color: @errorText;
-}
-.alert-info {
-  background-color: @infoBackground;
-  border-color: @infoBorder;
-  color: @infoText;
-}
-.alert-info h4 {
-  color: @infoText;
-}
-
-
-// Block alerts
-// -------------------------
-
-.alert-block {
-  padding-top: 14px;
-  padding-bottom: 14px;
-}
-.alert-block > p,
-.alert-block > ul {
-  margin-bottom: 0;
-}
-.alert-block p + p {
-  margin-top: 5px;
-}

File tpl/bootstrap/bootstrap.less

View file
  */
 
 // Core variables and mixins
-@import "variables.less"; // Modify this for custom colors, font-sizes, etc
+@import "variables.less";
 @import "mixins.less";
 
-// CSS Reset
+
 @import "reset.less";
-
-// Grid system and page structure
 @import "scaffolding.less";
 @import "grid.less";
-//@import "layouts.less";
+@import "type.less";
+@import "forms.less";
+@import "font-awesome/font-awesome.less";
+@import "buttons.less";
+@import "navs.less";
 
-// Base CSS
-@import "type.less";
-//@import "code.less";
-@import "forms.less";
-//@import "tables.less";
-
-// Components: common
-//@import "sprites.less";
-@import "font-awesome/font-awesome.less";
-//@import "dropdowns.less";
-//@import "wells.less";
-//@import "component-animations.less";
-//@import "close.less";
-
-// Components: Buttons & Alerts
-@import "buttons.less";
-//@import "button-groups.less";
-//@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
-
-// Components: Nav
-@import "navs.less";
-//@import "navbar.less";
-//@import "breadcrumbs.less";
-//@import "pagination.less";
-//@import "pager.less";
-
-// Components: Popovers
-//@import "modals.less";
-//@import "tooltip.less";
-//@import "popovers.less";
-
-// Components: Misc
-//@import "thumbnails.less";
-//@import "media.less";
-//@import "labels-badges.less";
-//@import "progress-bars.less";
-//@import "accordion.less";
-//@import "carousel.less";
-//@import "hero-unit.less";
-
-// Utility classes
-@import "utilities.less"; // Has to be last to override when necessary
-
+@import "utilities.less";
 @import "responsive.less";

File tpl/bootstrap/breadcrumbs.less

-//
-// Breadcrumbs
-// --------------------------------------------------
-
-
-.breadcrumb {
-  padding: 8px 15px;
-  margin: 0 0 @baseLineHeight;
-  list-style: none;
-  background-color: #f5f5f5;
-  .border-radius(@baseBorderRadius);
-  > li {
-    display: inline-block;
-    .ie7-inline-block();
-    text-shadow: 0 1px 0 @white;
-    > .divider {
-      padding: 0 5px;
-      color: #ccc;
-    }
-  }
-  > .active {
-    color: @grayLight;
-  }
-}

File tpl/bootstrap/button-groups.less

-//
-// Button groups
-// --------------------------------------------------
-
-
-// Make the div behave like a button
-.btn-group {
-  position: relative;
-  display: inline-block;
-  .ie7-inline-block();
-  font-size: 0; // remove as part 1 of font-size inline-block hack
-  vertical-align: middle; // match .btn alignment given font-size hack above
-  white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page)
-  .ie7-restore-left-whitespace();
-}
-
-// Space out series of button groups
-.btn-group + .btn-group {
-  margin-left: 5px;
-}
-
-// Optional: Group multiple button groups together for a toolbar
-.btn-toolbar {
-  font-size: 0; // Hack to remove whitespace that results from using inline-block
-  margin-top: @baseLineHeight / 2;
-  margin-bottom: @baseLineHeight / 2;
-  > .btn + .btn,
-  > .btn-group + .btn,
-  > .btn + .btn-group {
-    margin-left: 5px;
-  }
-}
-
-// Float them, remove border radius, then re-add to first and last elements
-.btn-group > .btn {
-  position: relative;
-  .border-radius(0);
-}
-.btn-group > .btn + .btn {
-  margin-left: -1px;
-}
-.btn-group > .btn,
-.btn-group > .dropdown-menu,
-.btn-group > .popover {
-  font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack
-}
-
-// Reset fonts for other sizes
-.btn-group > .btn-mini {
-  font-size: @fontSizeMini;
-}
-.btn-group > .btn-small {
-  font-size: @fontSizeSmall;
-}
-.btn-group > .btn-large {
-  font-size: @fontSizeLarge;
-}
-
-// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match
-.btn-group > .btn:first-child {
-  margin-left: 0;
-  .border-top-left-radius(@baseBorderRadius);
-  .border-bottom-left-radius(@baseBorderRadius);
-}
-// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
-.btn-group > .btn:last-child,
-.btn-group > .dropdown-toggle {
-  .border-top-right-radius(@baseBorderRadius);
-  .border-bottom-right-radius(@baseBorderRadius);
-}
-// Reset corners for large buttons
-.btn-group > .btn.large:first-child {
-  margin-left: 0;
-  .border-top-left-radius(@borderRadiusLarge);
-  .border-bottom-left-radius(@borderRadiusLarge);
-}
-.btn-group > .btn.large:last-child,
-.btn-group > .large.dropdown-toggle {
-  .border-top-right-radius(@borderRadiusLarge);
-  .border-bottom-right-radius(@borderRadiusLarge);
-}
-
-// On hover/focus/active, bring the proper btn to front
-.btn-group > .btn:hover,
-.btn-group > .btn:focus,
-.btn-group > .btn:active,
-.btn-group > .btn.active {
-  z-index: 2;
-}
-
-// On active and open, don't show outline
-.btn-group .dropdown-toggle:active,
-.btn-group.open .dropdown-toggle {
-  outline: 0;
-}
-
-
-
-// Split button dropdowns
-// ----------------------
-
-// Give the line between buttons some depth
-.btn-group > .btn + .dropdown-toggle {
-  padding-left: 8px;
-  padding-right: 8px;
-  .box-shadow(~"inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)");
-  *padding-top: 5px;
-  *padding-bottom: 5px;
-}
-.btn-group > .btn-mini + .dropdown-toggle {
-  padding-left: 5px;
-  padding-right: 5px;
-  *padding-top: 2px;
-  *padding-bottom: 2px;
-}
-.btn-group > .btn-small + .dropdown-toggle {
-  *padding-top: 5px;
-  *padding-bottom: 4px;
-}
-.btn-group > .btn-large + .dropdown-toggle {
-  padding-left: 12px;
-  padding-right: 12px;
-  *padding-top: 7px;
-  *padding-bottom: 7px;
-}
-
-.btn-group.open {
-
-  // The clickable button for toggling the menu
-  // Remove the gradient and set the same inset shadow as the :active state
-  .dropdown-toggle {
-    background-image: none;
-    .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)");
-  }
-
-  // Keep the hover's background when dropdown is open
-  .btn.dropdown-toggle {
-    background-color: @btnBackgroundHighlight;
-  }
-  .btn-primary.dropdown-toggle {
-    background-color: @btnPrimaryBackgroundHighlight;
-  }
-  .btn-warning.dropdown-toggle {
-    background-color: @btnWarningBackgroundHighlight;
-  }
-  .btn-danger.dropdown-toggle {
-    background-color: @btnDangerBackgroundHighlight;
-  }
-  .btn-success.dropdown-toggle {
-    background-color: @btnSuccessBackgroundHighlight;
-  }
-  .btn-info.dropdown-toggle {
-    background-color: @btnInfoBackgroundHighlight;
-  }
-  .btn-inverse.dropdown-toggle {
-    background-color: @btnInverseBackgroundHighlight;
-  }
-}
-
-
-// Reposition the caret
-.btn .caret {
-  margin-top: 8px;
-  margin-left: 0;
-}
-// Carets in other button sizes
-.btn-large .caret {
-  margin-top: 6px;
-}
-.btn-large .caret {
-  border-left-width:  5px;
-  border-right-width: 5px;
-  border-top-width:   5px;
-}
-.btn-mini .caret,
-.btn-small .caret {
-  margin-top: 8px;
-}
-// Upside down carets for .dropup
-.dropup .btn-large .caret {
-  border-bottom-width: 5px;
-}
-
-
-
-// Account for other colors
-.btn-primary,
-.btn-warning,
-.btn-danger,
-.btn-info,
-.btn-success,
-.btn-inverse {
-  .caret {
-    border-top-color: @white;
-    border-bottom-color: @white;
-  }
-}
-
-
-
-// Vertical button groups
-// ----------------------
-
-.btn-group-vertical {
-  display: inline-block; // makes buttons only take up the width they need
-  .ie7-inline-block();
-}
-.btn-group-vertical > .btn {
-  display: block;
-  float: none;
-  max-width: 100%;
-  .border-radius(0);
-}
-.btn-group-vertical > .btn + .btn {
-  margin-left: 0;
-  margin-top: -1px;
-}
-.btn-group-vertical > .btn:first-child {
-  .border-radius(@baseBorderRadius @baseBorderRadius 0 0);
-}
-.btn-group-vertical > .btn:last-child {
-  .border-radius(0 0 @baseBorderRadius @baseBorderRadius);
-}
-.btn-group-vertical > .btn-large:first-child {
-  .border-radius(@borderRadiusLarge @borderRadiusLarge 0 0);
-}
-.btn-group-vertical > .btn-large:last-child {
-  .border-radius(0 0 @borderRadiusLarge @borderRadiusLarge);
-}

File tpl/bootstrap/carousel.less

-//
-// Carousel
-// --------------------------------------------------
-
-
-.carousel {
-  position: relative;
-  margin-bottom: @baseLineHeight;
-  line-height: 1;
-}
-
-.carousel-inner {
-  overflow: hidden;
-  width: 100%;
-  position: relative;
-}
-
-.carousel-inner {
-
-  > .item {
-    display: none;
-    position: relative;
-    .transition(.6s ease-in-out left);
-
-    // Account for jankitude on images
-    > img,
-    > a > img {
-      display: block;
-      line-height: 1;
-    }
-  }
-
-  > .active,
-  > .next,
-  > .prev { display: block; }
-
-  > .active {
-    left: 0;
-  }
-
-  > .next,
-  > .prev {
-    position: absolute;
-    top: 0;
-    width: 100%;
-  }
-
-  > .next {
-    left: 100%;
-  }
-  > .prev {
-    left: -100%;
-  }
-  > .next.left,
-  > .prev.right {
-    left: 0;
-  }
-
-  > .active.left {
-    left: -100%;
-  }
-  > .active.right {
-    left: 100%;
-  }
-
-}
-
-// Left/right controls for nav
-// ---------------------------
-
-.carousel-control {
-  position: absolute;
-  top: 40%;
-  left: 15px;
-  width: 40px;
-  height: 40px;
-  margin-top: -20px;
-  font-size: 60px;
-  font-weight: 100;
-  line-height: 30px;
-  color: @white;
-  text-align: center;
-  background: @grayDarker;
-  border: 3px solid @white;
-  .border-radius(23px);
-  .opacity(50);
-
-  // we can't have this transition here
-  // because webkit cancels the carousel
-  // animation if you trip this while
-  // in the middle of another animation
-  // ;_;
-  // .transition(opacity .2s linear);
-
-  // Reposition the right one
-  &.right {
-    left: auto;
-    right: 15px;
-  }
-
-  // Hover/focus state
-  &:hover,
-  &:focus {
-    color: @white;
-    text-decoration: none;
-    .opacity(90);
-  }
-}
-
-// Carousel indicator pips
-// -----------------------------
-.carousel-indicators {
-  position: absolute;
-  top: 15px;
-  right: 15px;
-  z-index: 5;
-  margin: 0;
-  list-style: none;
-
-  li {
-    display: block;
-    float: left;
-    width: 10px;
-    height: 10px;
-    margin-left: 5px;
-    text-indent: -999px;
-    background-color: #ccc;
-    background-color: rgba(255,255,255,.25);
-    border-radius: 5px;
-  }
-  .active {
-    background-color: #fff;
-  }
-}
-
-// Caption for text below images
-// -----------------------------
-
-.carousel-caption {
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  padding: 15px;
-  background: @grayDark;
-  background: rgba(0,0,0,.75);
-}
-.carousel-caption h4,
-.carousel-caption p {
-  color: @white;
-  line-height: @baseLineHeight;
-}
-.carousel-caption h4 {
-  margin: 0 0 5px;
-}
-.carousel-caption p {
-  margin-bottom: 0;
-}

File tpl/bootstrap/close.less

-//
-// Close icons
-// --------------------------------------------------
-
-
-.close {
-  float: right;
-  font-size: 20px;
-  font-weight: bold;
-  line-height: @baseLineHeight;
-  color: @black;
-  text-shadow: 0 1px 0 rgba(255,255,255,1);
-  .opacity(20);
-  &:hover,
-  &:focus {
-    color: @black;
-    text-decoration: none;
-    cursor: pointer;
-    .opacity(40);
-  }
-}
-
-// Additional properties for button version
-// iOS requires the button element instead of an anchor tag.
-// If you want the anchor version, it requires `href="#"`.
-button.close {
-  padding: 0;
-  cursor: pointer;
-  background: transparent;
-  border: 0;
-  -webkit-appearance: none;
-}

File tpl/bootstrap/code.less

-//
-// Code (inline and blocK)
-// --------------------------------------------------
-
-
-// Inline and block code styles
-code,
-pre {
-  padding: 0 3px 2px;
-  #font > #family > .monospace;
-  font-size: @baseFontSize - 2;
-  color: @grayDark;
-  .border-radius(3px);
-}
-
-// Inline code
-code {
-  padding: 2px 4px;
-  color: #d14;
-  background-color: #f7f7f9;
-  border: 1px solid #e1e1e8;
-  white-space: nowrap;
-}
-
-// Blocks of code
-pre {
-  display: block;
-  padding: (@baseLineHeight - 1) / 2;
-  margin: 0 0 @baseLineHeight / 2;
-  font-size: @baseFontSize - 1; // 14px to 13px
-  line-height: @baseLineHeight;
-  word-break: break-all;
-  word-wrap: break-word;
-  white-space: pre;
-  white-space: pre-wrap;
-  background-color: #f5f5f5;
-  border: 1px solid #ccc; // fallback for IE7-8
-  border: 1px solid rgba(0,0,0,.15);
-  .border-radius(@baseBorderRadius);
-
-  // Make prettyprint styles more spaced out for readability
-  &.prettyprint {
-    margin-bottom: @baseLineHeight;
-  }
-
-  // Account for some code outputs that place code tags in pre tags
-  code {
-    padding: 0;
-    color: inherit;
-    white-space: pre;
-    white-space: pre-wrap;
-    background-color: transparent;
-    border: 0;
-  }
-}
-
-// Enable scrollable blocks of code
-.pre-scrollable {
-  max-height: 340px;
-  overflow-y: scroll;
-}

File tpl/bootstrap/component-animations.less

-//
-// Component animations
-// --------------------------------------------------
-
-
-.fade {
-  opacity: 0;
-  .transition(opacity .15s linear);
-  &.in {
-    opacity: 1;
-  }
-}
-
-.collapse {
-  position: relative;
-  height: 0;
-  overflow: hidden;
-  .transition(height .35s ease);
-  &.in {
-    height: auto;
-  }
-}

File tpl/bootstrap/dropdowns.less

-//
-// Dropdown menus
-// --------------------------------------------------
-
-
-// Use the .menu class on any <li> element within the topbar or ul.tabs and you'll get some superfancy dropdowns
-.dropup,
-.dropdown {
-  position: relative;
-}
-.dropdown-toggle {
-  // The caret makes the toggle a bit too tall in IE7
-  *margin-bottom: -3px;
-}
-.dropdown-toggle:active,
-.open .dropdown-toggle {
-  outline: 0;
-}
-
-// Dropdown arrow/caret
-// --------------------
-.caret {
-  display: inline-block;
-  width: 0;
-  height: 0;
-  vertical-align: top;
-  border-top:   4px solid @black;
-  border-right: 4px solid transparent;
-  border-left:  4px solid transparent;
-  content: "";
-}
-
-// Place the caret
-.dropdown .caret {
-  margin-top: 8px;
-  margin-left: 2px;
-}
-
-// The dropdown menu (ul)
-// ----------------------
-.dropdown-menu {
-  position: absolute;
-  top: 100%;
-  left: 0;
-  z-index: @zindexDropdown;
-  display: none; // none by default, but block on "open" of the menu
-  float: left;
-  min-width: 160px;
-  padding: 5px 0;
-  margin: 2px 0 0; // override default ul
-  list-style: none;
-  background-color: @dropdownBackground;
-  border: 1px solid #ccc; // Fallback for IE7-8
-  border: 1px solid @dropdownBorder;
-  *border-right-width: 2px;
-  *border-bottom-width: 2px;
-  .border-radius(6px);
-  .box-shadow(0 5px 10px rgba(0,0,0,.2));
-  -webkit-background-clip: padding-box;
-     -moz-background-clip: padding;
-          background-clip: padding-box;
-
-  // Aligns the dropdown menu to right
-  &.pull-right {
-    right: 0;
-    left: auto;
-  }
-
-  // Dividers (basically an hr) within the dropdown
-  .divider {
-    .nav-divider(@dropdownDividerTop, @dropdownDividerBottom);
-  }
-
-  // Links within the dropdown menu
-  > li > a {
-    display: block;
-    padding: 3px 20px;
-    clear: both;
-    font-weight: normal;
-    line-height: @baseLineHeight;
-    color: @dropdownLinkColor;
-    white-space: nowrap;
-  }
-}
-
-// Hover/Focus state
-// -----------
-.dropdown-menu > li > a:hover,
-.dropdown-menu > li > a:focus,
-.dropdown-submenu:hover > a,
-.dropdown-submenu:focus > a {
-  text-decoration: none;
-  color: @dropdownLinkColorHover;
-  #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%));
-}
-
-// Active state
-// ------------
-.dropdown-menu > .active > a,
-.dropdown-menu > .active > a:hover,
-.dropdown-menu > .active > a:focus {
-  color: @dropdownLinkColorActive;
-  text-decoration: none;
-  outline: 0;
-  #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%));
-}
-
-// Disabled state
-// --------------
-// Gray out text and ensure the hover/focus state remains gray
-.dropdown-menu > .disabled > a,
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
-  color: @grayLight;
-}
-// Nuke hover/focus effects
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
-  text-decoration: none;
-  background-color: transparent;
-  background-image: none; // Remove CSS gradient
-  .reset-filter();
-  cursor: default;
-}
-
-// Open state for the dropdown
-// ---------------------------
-.open {
-  // IE7's z-index only goes to the nearest positioned ancestor, which would
-  // make the menu appear below buttons that appeared later on the page
-  *z-index: @zindexDropdown;
-
-  & > .dropdown-menu {
-    display: block;
-  }
-}
-
-// Backdrop to catch body clicks on mobile, etc.
-// ---------------------------
-.dropdown-backdrop {
-  position: fixed;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  top: 0;
-  z-index: @zindexDropdown - 10;
-}
-
-// Right aligned dropdowns
-// ---------------------------
-.pull-right > .dropdown-menu {
-  right: 0;
-  left: auto;
-}
-
-// Allow for dropdowns to go bottom up (aka, dropup-menu)
-// ------------------------------------------------------
-// Just add .dropup after the standard .dropdown class and you're set, bro.
-// TODO: abstract this so that the navbar fixed styles are not placed here?
-.dropup,
-.navbar-fixed-bottom .dropdown {
-  // Reverse the caret
-  .caret {
-    border-top: 0;
-    border-bottom: 4px solid @black;
-    content: "";
-  }
-  // Different positioning for bottom up menu
-  .dropdown-menu {
-    top: auto;
-    bottom: 100%;
-    margin-bottom: 1px;
-  }
-}
-
-// Sub menus
-// ---------------------------
-.dropdown-submenu {
-  position: relative;
-}
-// Default dropdowns
-.dropdown-submenu > .dropdown-menu {
-  top: 0;
-  left: 100%;
-  margin-top: -6px;
-  margin-left: -1px;
-  .border-radius(0 6px 6px 6px);
-}
-.dropdown-submenu:hover > .dropdown-menu {
-  display: block;
-}
-
-// Dropups
-.dropup .dropdown-submenu > .dropdown-menu {
-  top: auto;
-  bottom: 0;
-  margin-top: 0;
-  margin-bottom: -2px;
-  .border-radius(5px 5px 5px 0);
-}
-
-// Caret to indicate there is a submenu
-.dropdown-submenu > a:after {
-  display: block;
-  content: " ";
-  float: right;
-  width: 0;
-  height: 0;
-  border-color: transparent;
-  border-style: solid;
-  border-width: 5px 0 5px 5px;
-  border-left-color: darken(@dropdownBackground, 20%);
-  margin-top: 5px;
-  margin-right: -10px;
-}
-.dropdown-submenu:hover > a:after {
-  border-left-color: @dropdownLinkColorHover;
-}
-
-// Left aligned submenus
-.dropdown-submenu.pull-left {
-  // Undo the float
-  // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere.
-  float: none;
-
-  // Positioning the submenu
-  > .dropdown-menu {
-    left: -100%;
-    margin-left: 10px;
-    .border-radius(6px 0 6px 6px);
-  }
-}
-
-// Tweak nav headers
-// -----------------
-// Increase padding from 15px to 20px on sides
-.dropdown .dropdown-menu .nav-header {
-  padding-left: 20px;
-  padding-right: 20px;
-}
-
-// Typeahead
-// ---------
-.typeahead {
-  z-index: 1051;
-  margin-top: 2px; // give it some space to breathe
-  .border-radius(@baseBorderRadius);
-}

File tpl/bootstrap/hero-unit.less

-//
-// Hero unit
-// --------------------------------------------------
-
-
-.hero-unit {
-  padding: 60px;
-  margin-bottom: 30px;
-  font-size: 18px;
-  font-weight: 200;
-  line-height: @baseLineHeight * 1.5;
-  color: @heroUnitLeadColor;
-  background-color: @heroUnitBackground;
-  .border-radius(6px);
-  h1 {
-    margin-bottom: 0;
-    font-size: 60px;
-    line-height: 1;
-    color: @heroUnitHeadingColor;
-    letter-spacing: -1px;
-  }
-  li {
-    line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less
-  }
-}

File tpl/bootstrap/labels-badges.less

-//
-// Labels and badges
-// --------------------------------------------------
-
-
-// Base classes
-.label,
-.badge {
-  display: inline-block;
-  padding: 2px 4px;
-  font-size: @baseFontSize * .846;
-  font-weight: bold;
-  line-height: 14px; // ensure proper line-height if floated
-  color: @white;
-  vertical-align: baseline;
-  white-space: nowrap;
-  text-shadow: 0 -1px 0 rgba(0,0,0,.25);
-  background-color: @grayLight;
-}
-// Set unique padding and border-radii
-.label {
-  .border-radius(3px);
-}
-.badge {
-  padding-left: 9px;
-  padding-right: 9px;
-  .border-radius(9px);
-}
-
-// Empty labels/badges collapse
-.label,
-.badge {
-  &:empty {
-    display: none;
-  }
-}
-
-// Hover/focus state, but only for links
-a {
-  &.label:hover,
-  &.label:focus,
-  &.badge:hover,
-  &.badge:focus {
-    color: @white;
-    text-decoration: none;
-    cursor: pointer;
-  }
-}
-
-// Colors
-// Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute)
-.label,
-.badge {
-  // Important (red)
-  &-important         { background-color: @errorText; }
-  &-important[href]   { background-color: darken(@errorText, 10%); }
-  // Warnings (orange)
-  &-warning           { background-color: @orange; }
-  &-warning[href]     { background-color: darken(@orange, 10%); }
-  // Success (green)
-  &-success           { background-color: @successText; }
-  &-success[href]     { background-color: darken(@successText, 10%); }
-  // Info (turquoise)
-  &-info              { background-color: @infoText; }
-  &-info[href]        { background-color: darken(@infoText, 10%); }
-  // Inverse (black)
-  &-inverse           { background-color: @grayDark; }
-  &-inverse[href]     { background-color: darken(@grayDark, 10%); }
-}
-
-// Quick fix for labels/badges in buttons
-.btn {
-  .label,
-  .badge {
-    position: relative;
-    top: -1px;
-  }
-}
-.btn-mini {
-  .label,
-  .badge {
-    top: 0;
-  }
-}

File tpl/bootstrap/layouts.less

-//
-// Layouts
-// --------------------------------------------------
-
-
-// Container (centered, fixed-width layouts)
-.container {
-  .container-fixed();
-}
-
-// Fluid layouts (left aligned, with sidebar, min- & max-width content)
-.container-fluid {
-  padding-right: @gridGutterWidth;
-  padding-left: @gridGutterWidth;
-  .clearfix();
-}

File tpl/bootstrap/media.less

-// Media objects
-// Source: http://stubbornella.org/content/?p=497
-// --------------------------------------------------
-
-
-// Common styles
-// -------------------------
-
-// Clear the floats
-.media,
-.media-body {
-  overflow: hidden;
-  *overflow: visible;
-  zoom: 1;
-}
-
-// Proper spacing between instances of .media
-.media,
-.media .media {
-  margin-top: 15px;
-}
-.media:first-child {
-  margin-top: 0;
-}
-
-// For images and videos, set to block
-.media-object {
-  display: block;
-}
-
-// Reset margins on headings for tighter default spacing
-.media-heading {
-  margin: 0 0 5px;
-}
-
-
-// Media image alignment
-// -------------------------
-
-.media > .pull-left {
-  margin-right: 10px;
-}
-.media > .pull-right {
-  margin-left: 10px;
-}
-
-
-// Media list variation
-// -------------------------
-
-// Undo default ul/ol styles
-.media-list {
-  margin-left: 0;
-  list-style: none;
-}

File tpl/bootstrap/modals.less

-//
-// Modals
-// --------------------------------------------------
-
-// Background
-.modal-backdrop {
-  position: fixed;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  z-index: @zindexModalBackdrop;
-  background-color: @black;
-  // Fade for backdrop
-  &.fade { opacity: 0; }
-}
-
-.modal-backdrop,
-.modal-backdrop.fade.in {
-  .opacity(80);
-}
-
-// Base modal
-.modal {
-  position: fixed;
-  top: 10%;
-  left: 50%;
-  z-index: @zindexModal;
-  width: 560px;
-  margin-left: -280px;
-  background-color: @white;
-  border: 1px solid #999;
-  border: 1px solid rgba(0,0,0,.3);
-  *border: 1px solid #999; /* IE6-7 */
-  .border-radius(6px);
-  .box-shadow(0 3px 7px rgba(0,0,0,0.3));
-  .background-clip(padding-box);
-  // Remove focus outline from opened modal
-  outline: none;
-
-  &.fade {
-    .transition(e('opacity .3s linear, top .3s ease-out'));
-    top: -25%;
-  }
-  &.fade.in { top: 10%; }
-}
-.modal-header {
-  padding: 9px 15px;
-  border-bottom: 1px solid #eee;
-  // Close icon
-  .close { margin-top: 2px; }
-  // Heading
-  h3 {
-    margin: 0;
-    line-height: 30px;
-  }
-}
-
-// Body (where all modal content resides)
-.modal-body {
-  position: relative;
-  overflow-y: auto;
-  max-height: 400px;
-  padding: 15px;
-}
-// Remove bottom margin if need be
-.modal-form {
-  margin-bottom: 0;
-}
-
-// Footer (for actions)
-.modal-footer {
-  padding: 14px 15px 15px;
-  margin-bottom: 0;
-  text-align: right; // right align buttons
-  background-color: #f5f5f5;
-  border-top: 1px solid #ddd;
-  .border-radius(0 0 6px 6px);
-  .box-shadow(inset 0 1px 0 @white);
-  .clearfix(); // clear it in case folks use .pull-* classes on buttons
-
-  // Properly space out buttons
-  .btn + .btn {
-    margin-left: 5px;
-    margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs
-  }
-  // but override that for button groups
-  .btn-group .btn + .btn {
-    margin-left: -1px;
-  }
-  // and override it for block buttons as well
-  .btn-block + .btn-block {
-    margin-left: 0;
-  }
-}

File tpl/bootstrap/navbar.less

-//
-// Navbars (Redux)
-// --------------------------------------------------
-
-
-// COMMON STYLES
-// -------------
-
-// Base class and wrapper
-.navbar {
-  overflow: visible;
-  margin-bottom: @baseLineHeight;
-
-  // Fix for IE7's bad z-indexing so dropdowns don't appear below content that follows the navbar
-  *position: relative;
-  *z-index: 2;
-}
-
-// Inner for background effects
-// Gradient is applied to its own element because overflow visible is not honored by IE when filter is present
-.navbar-inner {
-  min-height: @navbarHeight;
-  padding-left:  20px;
-  padding-right: 20px;
-  #gradient > .vertical(@navbarBackgroundHighlight, @navbarBackground);
-  border: 1px solid @navbarBorder;
-  .border-radius(@baseBorderRadius);
-  .box-shadow(0 1px 4px rgba(0,0,0,.065));
-
-  // Prevent floats from breaking the navbar
-  .clearfix();
-}
-
-// Set width to auto for default container
-// We then reset it for fixed navbars in the #gridSystem mixin
-.navbar .container {
-  width: auto;
-}
-
-// Override the default collapsed state
-.nav-collapse.collapse {
-  height: auto;
-  overflow: visible;
-}
-
-
-// Brand: website or project name
-// -------------------------
-.navbar .brand {
-  float: left;
-  display: block;
-  // Vertically center the text given @navbarHeight
-  padding: ((@navbarHeight - @baseLineHeight) / 2) 20px ((@navbarHeight - @baseLineHeight) / 2);
-  margin-left: -20px; // negative indent to left-align the text down the page
-  font-size: 20px;
-  font-weight: 200;
-  color: @navbarBrandColor;
-  text-shadow: 0 1px 0 @navbarBackgroundHighlight;
-  &:hover,
-  &:focus {
-    text-decoration: none;
-  }
-}
-
-// Plain text in topbar
-// -------------------------
-.navbar-text {
-  margin-bottom: 0;
-  line-height: @navbarHeight;
-  color: @navbarText;
-}
-
-// Janky solution for now to account for links outside the .nav
-// -------------------------
-.navbar-link {
-  color: @navbarLinkColor;
-  &:hover,
-  &:focus {
-    color: @navbarLinkColorHover;
-  }
-}
-
-// Dividers in navbar
-// -------------------------
-.navbar .divider-vertical {
-  height: @navbarHeight;
-  margin: 0 9px;
-  border-left: 1px solid @navbarBackground;
-  border-right: 1px solid @navbarBackgroundHighlight;
-}
-
-// Buttons in navbar
-// -------------------------
-.navbar .btn,
-.navbar .btn-group {
-  .navbarVerticalAlign(30px); // Vertically center in navbar
-}
-.navbar .btn-group .btn,
-.navbar .input-prepend .btn,
-.navbar .input-append .btn,
-.navbar .input-prepend .btn-group,
-.navbar .input-append .btn-group {
-  margin-top: 0; // then undo the margin here so we don't accidentally double it
-}
-
-// Navbar forms
-// -------------------------
-.navbar-form {
-  margin-bottom: 0; // remove default bottom margin
-  .clearfix();
-  input,
-  select,
-  .radio,
-  .checkbox {
-    .navbarVerticalAlign(30px); // Vertically center in navbar
-  }
-  input,
-  select,
-  .btn {
-    display: inline-block;
-    margin-bottom: 0;
-  }
-  input[type="image"],
-  input[type="checkbox"],
-  input[type="radio"] {
-    margin-top: 3px;
-  }
-  .input-append,
-  .input-prepend {
-    margin-top: 5px;
-    white-space: nowrap; // preven two  items from separating within a .navbar-form that has .pull-left
-    input {
-      margin-top: 0; // remove the margin on top since it's on the parent
-    }
-  }
-}
-
-// Navbar search
-// -------------------------
-.navbar-search {
-  position: relative;
-  float: left;
-  .navbarVerticalAlign(30px); // Vertically center in navbar
-  margin-bottom: 0;
-  .search-query {
-    margin-bottom: 0;
-    padding: 4px 14px;
-    #font > .sans-serif(13px, normal, 1);
-    .border-radius(15px); // redeclare because of specificity of the type attribute
-  }
-}
-