Browse Source

Initial commit

Ralfp 12 years ago
parent
commit
7509e0735a
375 changed files with 24965 additions and 0 deletions
  1. 8 0
      .gitignore
  2. 0 0
      CHANGELOG.txt
  3. 280 0
      LICENSE.txt
  4. 10 0
      manage.py
  5. 23 0
      misago/__init__.py
  6. 0 0
      misago/acl/__init__.py
  7. 26 0
      misago/acl/admin/__init__.py
  8. 4 0
      misago/acl/admin/views.py
  9. 265 0
      misago/admin/__init__.py
  10. 4 0
      misago/admin/context_processors.py
  11. 41 0
      misago/admin/fixtures.py
  12. 4 0
      misago/admin/views.py
  13. 474 0
      misago/admin/widgets.py
  14. 0 0
      misago/auth/__init__.py
  15. 116 0
      misago/auth/fixtures.py
  16. 109 0
      misago/auth/forms.py
  17. 9 0
      misago/auth/urls.py
  18. 210 0
      misago/auth/views.py
  19. 0 0
      misago/banning/__init__.py
  20. 6 0
      misago/banning/context_processors.py
  21. 13 0
      misago/banning/decorators.py
  22. 4 0
      misago/banning/fixtures.py
  23. 17 0
      misago/banning/middleware.py
  24. 69 0
      misago/banning/models.py
  25. 11 0
      misago/banning/views.py
  26. 15 0
      misago/context_processors.py
  27. 0 0
      misago/cookie_jar/__init__.py
  28. 38 0
      misago/cookie_jar/cookie_jar.py
  29. 12 0
      misago/cookie_jar/middleware.py
  30. 0 0
      misago/crawlers/__init__.py
  31. 23 0
      misago/crawlers/crawler.py
  32. 16 0
      misago/crawlers/database.py
  33. 14 0
      misago/crawlers/middleware.py
  34. 149 0
      misago/forms/__init__.py
  35. 297 0
      misago/forms/layouts.py
  36. 0 0
      misago/forums/__init__.py
  37. 37 0
      misago/forums/admin/__init__.py
  38. 4 0
      misago/forums/admin/views.py
  39. 10 0
      misago/forums/fixtures.py
  40. 167 0
      misago/forums/models.py
  41. 1 0
      misago/forums/views.py
  42. 37 0
      misago/messages/__init__.py
  43. 4 0
      misago/messages/context_processors.py
  44. 21 0
      misago/messages/messages.py
  45. 5 0
      misago/messages/middleware.py
  46. 0 0
      misago/monitor/__init__.py
  47. 4 0
      misago/monitor/context_processors.py
  48. 11 0
      misago/monitor/fixtures.py
  49. 5 0
      misago/monitor/middleware.py
  50. 6 0
      misago/monitor/models.py
  51. 69 0
      misago/monitor/monitor.py
  52. 0 0
      misago/overview/__init__.py
  53. 59 0
      misago/overview/admin/__init__.py
  54. 28 0
      misago/overview/admin/forms.py
  55. 195 0
      misago/overview/admin/views.py
  56. 85 0
      misago/search/__init__.py
  57. 4 0
      misago/security/__init__.py
  58. 51 0
      misago/security/admin/__init__.py
  59. 0 0
      misago/security/admin/qatest/__init__.py
  60. 26 0
      misago/security/admin/qatest/forms.py
  61. 0 0
      misago/security/admin/qatest/helpers.py
  62. 124 0
      misago/security/admin/qatest/views.py
  63. 119 0
      misago/security/auth.py
  64. 30 0
      misago/security/captcha.py
  65. 8 0
      misago/security/context_processors.py
  66. 9 0
      misago/security/csrf.py
  67. 38 0
      misago/security/decorators.py
  68. 35 0
      misago/security/firewalls.py
  69. 57 0
      misago/security/fixtures.py
  70. 26 0
      misago/security/forms.py
  71. 0 0
      misago/security/management/__init__.py
  72. 0 0
      misago/security/management/commands/__init__.py
  73. 14 0
      misago/security/management/commands/clearattempts.py
  74. 43 0
      misago/security/middleware.py
  75. 75 0
      misago/security/models.py
  76. 13 0
      misago/security/urls.py
  77. 113 0
      misago/security/views.py
  78. 0 0
      misago/sessions/__init__.py
  79. 0 0
      misago/sessions/management/__init__.py
  80. 0 0
      misago/sessions/management/commands/__init__.py
  81. 14 0
      misago/sessions/management/commands/clearsessions.py
  82. 14 0
      misago/sessions/management/commands/cleartokens.py
  83. 19 0
      misago/sessions/middleware.py
  84. 22 0
      misago/sessions/models.py
  85. 236 0
      misago/sessions/sessions.py
  86. 0 0
      misago/settings/__init__.py
  87. 28 0
      misago/settings/admin/__init__.py
  88. 6 0
      misago/settings/admin/forms.py
  89. 116 0
      misago/settings/admin/views.py
  90. 4 0
      misago/settings/context_processors.py
  91. 106 0
      misago/settings/fixtures.py
  92. 5 0
      misago/settings/middleware.py
  93. 146 0
      misago/settings/models.py
  94. 68 0
      misago/settings/settings.py
  95. 178 0
      misago/settings_base.py
  96. 0 0
      misago/setup/__init__.py
  97. 14 0
      misago/setup/fixtures.py
  98. 0 0
      misago/setup/management/__init__.py
  99. 0 0
      misago/setup/management/commands/__init__.py
  100. 39 0
      misago/setup/management/commands/about.py
  101. 18 0
      misago/setup/management/commands/loadfixtures.py
  102. 7 0
      misago/stopwatch/__init__.py
  103. 16 0
      misago/stopwatch/middleware.py
  104. 0 0
      misago/template/__init__.py
  105. 0 0
      misago/template/templatetags/__init__.py
  106. 13 0
      misago/template/templatetags/django2jinja.py
  107. 0 0
      misago/themes/__init__.py
  108. 8 0
      misago/themes/middleware.py
  109. 55 0
      misago/themes/theme.py
  110. 87 0
      misago/timezones/__init__.py
  111. 28 0
      misago/urls.py
  112. 0 0
      misago/users/__init__.py
  113. 90 0
      misago/users/admin/__init__.py
  114. 0 0
      misago/users/admin/users/__init__.py
  115. 22 0
      misago/users/admin/users/forms.py
  116. 88 0
      misago/users/admin/users/views.py
  117. 4 0
      misago/users/context_processors.py
  118. 139 0
      misago/users/fixtures.py
  119. 0 0
      misago/users/management/__init__.py
  120. 0 0
      misago/users/management/commands/__init__.py
  121. 39 0
      misago/users/management/commands/adduser.py
  122. 12 0
      misago/users/management/commands/updateranks.py
  123. 26 0
      misago/users/middleware.py
  124. 421 0
      misago/users/models.py
  125. 12 0
      misago/users/urls.py
  126. 48 0
      misago/users/validators.py
  127. 80 0
      misago/users/views.py
  128. 28 0
      misago/utils/__init__.py
  129. 0 0
      misago/utils/templatetags/__init__.py
  130. 14 0
      misago/utils/templatetags/misago_filters.py
  131. 28 0
      misago/views.py
  132. 929 0
      static/admin/css/admin.css
  133. 92 0
      static/admin/css/admin.less
  134. 62 0
      static/admin/css/admin/alerts.less
  135. 45 0
      static/admin/css/admin/avatars.less
  136. 103 0
      static/admin/css/admin/buttons.less
  137. 95 0
      static/admin/css/admin/forms.less
  138. 28 0
      static/admin/css/admin/graphs.less
  139. 172 0
      static/admin/css/admin/navbar.less
  140. 85 0
      static/admin/css/admin/navs.less
  141. 32 0
      static/admin/css/admin/popovers.less
  142. 94 0
      static/admin/css/admin/scaffolding.less
  143. 134 0
      static/admin/css/admin/tables.less
  144. 21 0
      static/admin/css/admin/users-lists.less
  145. 16 0
      static/admin/css/admin/utilities.less
  146. 15 0
      static/admin/css/admin/wells.less
  147. 148 0
      static/admin/css/bootstrap-toggle-buttons.css
  148. 34 0
      static/admin/css/bootstrap/accordion.less
  149. 65 0
      static/admin/css/bootstrap/alerts.less
  150. 62 0
      static/admin/css/bootstrap/bootstrap.less
  151. 24 0
      static/admin/css/bootstrap/breadcrumbs.less
  152. 245 0
      static/admin/css/bootstrap/button-groups.less
  153. 231 0
      static/admin/css/bootstrap/buttons.less
  154. 131 0
      static/admin/css/bootstrap/carousel.less
  155. 31 0
      static/admin/css/bootstrap/close.less
  156. 58 0
      static/admin/css/bootstrap/code.less
  157. 22 0
      static/admin/css/bootstrap/component-animations.less
  158. 210 0
      static/admin/css/bootstrap/dropdowns.less
  159. 650 0
      static/admin/css/bootstrap/forms.less
  160. 21 0
      static/admin/css/bootstrap/grid.less
  161. 24 0
      static/admin/css/bootstrap/hero-unit.less
  162. 72 0
      static/admin/css/bootstrap/labels-badges.less
  163. 16 0
      static/admin/css/bootstrap/layouts.less
  164. 681 0
      static/admin/css/bootstrap/mixins.less
  165. 98 0
      static/admin/css/bootstrap/modals.less
  166. 475 0
      static/admin/css/bootstrap/navbar.less
  167. 384 0
      static/admin/css/bootstrap/navs.less
  168. 40 0
      static/admin/css/bootstrap/pager.less
  169. 64 0
      static/admin/css/bootstrap/pagination.less
  170. 117 0
      static/admin/css/bootstrap/popovers.less
  171. 122 0
      static/admin/css/bootstrap/progress-bars.less
  172. 137 0
      static/admin/css/bootstrap/reset.less
  173. 28 0
      static/admin/css/bootstrap/responsive-1200px-min.less
  174. 174 0
      static/admin/css/bootstrap/responsive-767px-max.less
  175. 19 0
      static/admin/css/bootstrap/responsive-768px-979px.less
  176. 177 0
      static/admin/css/bootstrap/responsive-navbar.less
  177. 43 0
      static/admin/css/bootstrap/responsive-utilities.less
  178. 48 0
      static/admin/css/bootstrap/responsive.less
  179. 52 0
      static/admin/css/bootstrap/scaffolding.less
  180. 193 0
      static/admin/css/bootstrap/sprites.less
  181. 245 0
      static/admin/css/bootstrap/tables.less
  182. 52 0
      static/admin/css/bootstrap/thumbnails.less
  183. 70 0
      static/admin/css/bootstrap/tooltip.less
  184. 221 0
      static/admin/css/bootstrap/type.less
  185. 30 0
      static/admin/css/bootstrap/utilities.less
  186. 279 0
      static/admin/css/bootstrap/variables.less
  187. 29 0
      static/admin/css/bootstrap/wells.less
  188. 281 0
      static/admin/css/variables.less
  189. BIN
      static/admin/img/glyphicons-halflings-white.png
  190. BIN
      static/admin/img/glyphicons-halflings.png
  191. 65 0
      static/admin/js/admin.js
  192. 6 0
      static/admin/js/bootstrap.min.js
  193. 4 0
      static/admin/js/jquery-1.7.2.min.js
  194. 138 0
      static/admin/js/jquery.toggle.buttons.js
  195. BIN
      static/favicon.ico
  196. 1 0
      static/sora/css/admin.css
  197. 34 0
      static/sora/css/bootstrap/accordion.less
  198. 65 0
      static/sora/css/bootstrap/alerts.less
  199. 62 0
      static/sora/css/bootstrap/bootstrap.less
  200. 24 0
      static/sora/css/bootstrap/breadcrumbs.less
  201. 245 0
      static/sora/css/bootstrap/button-groups.less
  202. 231 0
      static/sora/css/bootstrap/buttons.less
  203. 131 0
      static/sora/css/bootstrap/carousel.less
  204. 31 0
      static/sora/css/bootstrap/close.less
  205. 58 0
      static/sora/css/bootstrap/code.less
  206. 22 0
      static/sora/css/bootstrap/component-animations.less
  207. 210 0
      static/sora/css/bootstrap/dropdowns.less
  208. 650 0
      static/sora/css/bootstrap/forms.less
  209. 21 0
      static/sora/css/bootstrap/grid.less
  210. 24 0
      static/sora/css/bootstrap/hero-unit.less
  211. 72 0
      static/sora/css/bootstrap/labels-badges.less
  212. 16 0
      static/sora/css/bootstrap/layouts.less
  213. 681 0
      static/sora/css/bootstrap/mixins.less
  214. 98 0
      static/sora/css/bootstrap/modals.less
  215. 475 0
      static/sora/css/bootstrap/navbar.less
  216. 384 0
      static/sora/css/bootstrap/navs.less
  217. 40 0
      static/sora/css/bootstrap/pager.less
  218. 64 0
      static/sora/css/bootstrap/pagination.less
  219. 117 0
      static/sora/css/bootstrap/popovers.less
  220. 122 0
      static/sora/css/bootstrap/progress-bars.less
  221. 137 0
      static/sora/css/bootstrap/reset.less
  222. 28 0
      static/sora/css/bootstrap/responsive-1200px-min.less
  223. 174 0
      static/sora/css/bootstrap/responsive-767px-max.less
  224. 19 0
      static/sora/css/bootstrap/responsive-768px-979px.less
  225. 177 0
      static/sora/css/bootstrap/responsive-navbar.less
  226. 43 0
      static/sora/css/bootstrap/responsive-utilities.less
  227. 48 0
      static/sora/css/bootstrap/responsive.less
  228. 52 0
      static/sora/css/bootstrap/scaffolding.less
  229. 193 0
      static/sora/css/bootstrap/sprites.less
  230. 245 0
      static/sora/css/bootstrap/tables.less
  231. 52 0
      static/sora/css/bootstrap/thumbnails.less
  232. 70 0
      static/sora/css/bootstrap/tooltip.less
  233. 221 0
      static/sora/css/bootstrap/type.less
  234. 30 0
      static/sora/css/bootstrap/utilities.less
  235. 279 0
      static/sora/css/bootstrap/variables.less
  236. 29 0
      static/sora/css/bootstrap/wells.less
  237. 917 0
      static/sora/css/sora.css
  238. 84 0
      static/sora/css/sora.less
  239. 62 0
      static/sora/css/sora/alerts.less
  240. 61 0
      static/sora/css/sora/avatars.less
  241. 105 0
      static/sora/css/sora/buttons.less
  242. 62 0
      static/sora/css/sora/forms.less
  243. 162 0
      static/sora/css/sora/navbar.less
  244. 50 0
      static/sora/css/sora/navs.less
  245. 37 0
      static/sora/css/sora/scaffolding.less
  246. 128 0
      static/sora/css/sora/tables.less
  247. 21 0
      static/sora/css/sora/users-lists.less
  248. 5 0
      static/sora/css/sora/utilities.less
  249. 36 0
      static/sora/css/sora/wells.less
  250. 282 0
      static/sora/css/variables.less
  251. BIN
      static/sora/img/glyphicons-halflings-white.png
  252. BIN
      static/sora/img/glyphicons-halflings.png
  253. 6 0
      static/sora/js/bootstrap.min.js
  254. 4 0
      static/sora/js/jquery-1.7.2.min.js
  255. 138 0
      static/sora/js/jquery.toggle.buttons.js
  256. 57 0
      static/sora/js/sora.js
  257. 76 0
      templates/500.html
  258. 9 0
      templates/_bootstrap/field_snippet.html
  259. 4 0
      templates/_bootstrap/fields/checkbox.html
  260. 8 0
      templates/_bootstrap/fields/recaptcha.html
  261. 6 0
      templates/_bootstrap/fields/text.html
  262. 0 0
      templates/_bootstrap/fields/textarea.html
  263. 12 0
      templates/_bootstrap/form_snippet.html
  264. 17 0
      templates/_bootstrap/row_snippet.html
  265. 17 0
      templates/_email/auth/activation_0_html.html
  266. 14 0
      templates/_email/auth/activation_0_plain.html
  267. 11 0
      templates/_email/auth/activation_1_html.html
  268. 11 0
      templates/_email/auth/activation_1_plain.html
  269. 8 0
      templates/_email/auth/activation_2_html.html
  270. 10 0
      templates/_email/auth/activation_2_plain.html
  271. 12 0
      templates/_email/auth/activation_resend_html.html
  272. 10 0
      templates/_email/auth/activation_resend_plain.html
  273. 12 0
      templates/_email/auth/reset_confirm_html.html
  274. 11 0
      templates/_email/auth/reset_confirm_plain.html
  275. 13 0
      templates/_email/auth/reset_new_html.html
  276. 11 0
      templates/_email/auth/reset_new_plain.html
  277. 26 0
      templates/_email/base_html.html
  278. 13 0
      templates/_email/base_plain.html
  279. 187 0
      templates/_forms.html
  280. 8 0
      templates/_message/auth/activated_new.html
  281. 8 0
      templates/_message/auth/activated_password.html
  282. 7 0
      templates/_message/auth/activation_admin.html
  283. 7 0
      templates/_message/auth/activation_not_required.html
  284. 7 0
      templates/_message/auth/activation_only_by_admin.html
  285. 9 0
      templates/_message/auth/activation_required.html
  286. 8 0
      templates/_message/auth/activation_resent.html
  287. 9 0
      templates/_message/auth/activation_user.html
  288. 6 0
      templates/_message/auth/invalid_confirmation_activation.html
  289. 6 0
      templates/_message/auth/invalid_confirmation_password.html
  290. 8 0
      templates/_message/auth/password_reset_confirm.html
  291. 9 0
      templates/_message/auth/password_reset_done.html
  292. 10 0
      templates/_message/auth/registered_activation_admin.html
  293. 8 0
      templates/_message/auth/registered_activation_none.html
  294. 9 0
      templates/_message/auth/registered_activation_user.html
  295. 7 0
      templates/_message/auth/registrations_off.html
  296. 6 0
      templates/_message/banning/banned_ip.html
  297. 14 0
      templates/_message/banning/banned_user.html
  298. 1 0
      templates/_message/base.html
  299. 7 0
      templates/_message/form_contains_errors.html
  300. 7 0
      templates/_message/invalid_request.html
  301. 9 0
      templates/_message/security/bad_credentials.html
  302. 6 0
      templates/_message/security/forbidden_authenticated.html
  303. 7 0
      templates/_message/security/forbidden_guest.html
  304. 11 0
      templates/_message/security/forbidden_jammed.html
  305. 7 0
      templates/_message/security/forbidden_request.html
  306. 7 0
      templates/_message/security/signed_in.html
  307. 7 0
      templates/_message/security/signed_out.html
  308. 23 0
      templates/admin/admin/form.html
  309. 31 0
      templates/admin/admin/layout.html
  310. 145 0
      templates/admin/admin/list.html
  311. 17 0
      templates/admin/base.html
  312. 23 0
      templates/admin/error403.html
  313. 23 0
      templates/admin/error404.html
  314. 11 0
      templates/admin/index.html
  315. 47 0
      templates/admin/layout.html
  316. 20 0
      templates/admin/layout_compact.html
  317. 5 0
      templates/admin/macros.html
  318. 1 0
      templates/admin/message/base.html
  319. 7 0
      templates/admin/message/form_contains_errors.html
  320. 7 0
      templates/admin/message/invalid_request.html
  321. 9 0
      templates/admin/message/security/bad_credentials.html
  322. 8 0
      templates/admin/message/security/not_admin.html
  323. 7 0
      templates/admin/message/security/signed_in.html
  324. 7 0
      templates/admin/message/security/signed_out.html
  325. 17 0
      templates/admin/messages.html
  326. 76 0
      templates/admin/overview/forums.html
  327. 107 0
      templates/admin/overview/home.html
  328. 77 0
      templates/admin/overview/plot.html
  329. 19 0
      templates/admin/overview/stats/form.html
  330. 33 0
      templates/admin/overview/stats/graph.html
  331. 15 0
      templates/admin/overview/stats/layout.html
  332. 9 0
      templates/admin/overview/stats/not_available.html
  333. 68 0
      templates/admin/overview/system.html
  334. 81 0
      templates/admin/overview/users.html
  335. 17 0
      templates/admin/settings/search_results.html
  336. 42 0
      templates/admin/settings/settings.html
  337. 25 0
      templates/admin/signin.html
  338. 12 0
      templates/admin/todo.html
  339. 15 0
      templates/admin/users/admin_users/list.html
  340. 54 0
      templates/debug_toolbar/base.html
  341. 56 0
      templates/debug_toolbar/panels/cache.html
  342. 17 0
      templates/debug_toolbar/panels/headers.html
  343. 28 0
      templates/debug_toolbar/panels/logger.html
  344. 36 0
      templates/debug_toolbar/panels/profiling.html
  345. 123 0
      templates/debug_toolbar/panels/request_vars.html
  346. 17 0
      templates/debug_toolbar/panels/settings_vars.html
  347. 19 0
      templates/debug_toolbar/panels/signals.html
  348. 87 0
      templates/debug_toolbar/panels/sql.html
  349. 33 0
      templates/debug_toolbar/panels/sql_explain.html
  350. 40 0
      templates/debug_toolbar/panels/sql_profile.html
  351. 37 0
      templates/debug_toolbar/panels/sql_select.html
  352. 14 0
      templates/debug_toolbar/panels/template_source.html
  353. 44 0
      templates/debug_toolbar/panels/templates.html
  354. 21 0
      templates/debug_toolbar/panels/timer.html
  355. 18 0
      templates/debug_toolbar/panels/versions.html
  356. 12 0
      templates/debug_toolbar/redirect.html
  357. 27 0
      templates/sora/auth/forgot_password.html
  358. 27 0
      templates/sora/auth/register.html
  359. 27 0
      templates/sora/auth/resend_activation.html
  360. 16 0
      templates/sora/base.html
  361. 26 0
      templates/sora/error403.html
  362. 26 0
      templates/sora/error404.html
  363. 17 0
      templates/sora/index.html
  364. 37 0
      templates/sora/layout.html
  365. 5 0
      templates/sora/macros.html
  366. 40 0
      templates/sora/signin.html
  367. 24 0
      templates/sora/userbar.html
  368. 12 0
      templates/sora/users/profile.html
  369. 10 0
      templates/sora/users/usercp/avatar.html
  370. 10 0
      templates/sora/users/usercp/credentials.html
  371. 10 0
      templates/sora/users/usercp/ignored.html
  372. 10 0
      templates/sora/users/usercp/options.html
  373. 10 0
      templates/sora/users/usercp/signature.html
  374. 19 0
      templates/sora/users/usercp/usercp.html
  375. 10 0
      templates/sora/users/usercp/username.html

+ 8 - 0
.gitignore

@@ -170,9 +170,17 @@ pip-log.txt
 ##################
 
 
+coffin/**
 debug_toolbar/**
+dev/**
+jinja2/**
 mptt/**
 pytz/**
 recaptcha/**
+south/**
+static/avatars/**
+static/strawman/**
+templates/strawman/**
 unidecode/**
 yaml/**
+path.py

+ 0 - 0
CHANGELOG.txt


+ 280 - 0
LICENSE.txt

@@ -0,0 +1,280 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS

+ 10 - 0
manage.py

@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dev.settings")
+
+    from django.core.management import execute_from_command_line
+
+    execute_from_command_line(sys.argv)

+ 23 - 0
misago/__init__.py

@@ -0,0 +1,23 @@
+VERSION = (0, 1, 0, 'alpha', 0)
+
+def get_version(version=None):
+    """Derives a PEP386-compliant version number from VERSION."""
+    if version is None:
+        version = VERSION
+    assert len(version) == 5
+    assert version[3] in ('alpha', 'beta', 'rc', 'final')
+
+    # Now build the two parts of the version number:
+    # main = X.Y[.Z]
+    # sub = .devN - for pre-alpha releases
+    #     | {a|b|c}N - for alpha, beta and rc releases
+
+    parts = 2 if version[2] == 0 else 3
+    main = '.'.join(str(x) for x in version[:parts])
+
+    sub = ''
+    if version[3] != 'final':
+        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
+        sub = mapping[version[3]] + str(version[4])
+
+    return main + sub

+ 0 - 0
misago/acl/__init__.py


+ 26 - 0
misago/acl/admin/__init__.py

@@ -0,0 +1,26 @@
+from django.conf.urls import patterns, include, url
+from django.utils.translation import ugettext_lazy as _
+from misago.admin import AdminSection, AdminAction
+
+ADMIN_SECTIONS=(
+    AdminSection(
+                 id='permissions',
+                 name=_("Permissions"),
+                 icon='adjust',
+                 after='forums',
+                 ),
+)
+
+ADMIN_ACTIONS=(
+   AdminAction(
+               section='permissions',
+               id='permissions',
+               name=_("Permissions"),
+               help=_("trolololo permissions"),
+               icon='adjust',
+               route='admin_permissions',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_permissions'),
+                    ),
+               ),
+)

+ 4 - 0
misago/acl/admin/views.py

@@ -0,0 +1,4 @@
+from django.http import HttpResponse
+
+def acl_list(request):
+    return HttpResponse("UNIMPLEMENTED ADMIN ACTION!")

+ 265 - 0
misago/admin/__init__.py

@@ -0,0 +1,265 @@
+from django.conf import settings
+from django.conf.urls import patterns, include, url
+from django.core.urlresolvers import resolve
+from django.utils.importlib import import_module
+
+"""
+Clean admin path if it was defined, or leave variable empty if ACP is turned off.
+"""
+ADMIN_PATH = ''
+if settings.ADMIN_PATH:
+    ADMIN_PATH = settings.ADMIN_PATH
+    while ADMIN_PATH[:1] == '/':
+        ADMIN_PATH = ADMIN_PATH[1:]
+    while ADMIN_PATH[-1:] == '/':
+        ADMIN_PATH = ADMIN_PATH[:-1]
+    ADMIN_PATH += '/'
+    
+
+"""
+Admin lists sorter for admin sections and actions
+"""
+class SortList(object):
+    def __init__(self, unsorted):
+        self.unsorted = unsorted
+        
+    def sort(self):
+        # Sort and return sorted list
+        order = []
+        cache = {}
+        for item in self.unsorted:
+            if item.after:
+                try:
+                    cache[item.after].append(item.id)
+                except KeyError:
+                    cache[item.after] = []
+                    cache[item.after].append(item.id)
+            else:
+                order.append(item.id)
+        while cache:
+            for item in cache.keys():
+                try:
+                    target_index = order.index(item)
+                    for new_item in cache[item]:
+                        target_index += 1
+                        order.insert(target_index, new_item)
+                    del cache[item]
+                except ValueError:
+                    pass
+        sorted = []
+        for item in order:
+            for object in self.unsorted:
+                if item == object.id:
+                    sorted.append(object)
+                    break
+        return sorted
+        
+            
+"""
+Admin site section
+"""
+class AdminSiteItem(object):
+    def __init__(self, id, name, icon, target=None, route=None, help=None, after=None):
+        self.id = id
+        self.name = name
+        self.help = help
+        self.after = after
+        self.icon = icon
+        self.target = target
+        self.route = route
+        self.sorted = False
+    
+    
+"""
+Admin site action
+"""
+class AdminAction(AdminSiteItem):
+    def __init__(self, section=None, actions=[], model=None, messages={}, urlpatterns=None, **kwargs):
+        self.actions = actions
+        self.section = section
+        self.model = model
+        self.messages = messages
+        self.urlpatterns = urlpatterns
+        super(AdminAction, self).__init__(**kwargs)
+    
+    def get_action_attr(self, id, attr):
+        for action in self.actions:
+            if action['id'] == id:
+                return action[attr]
+        return None
+    
+    def is_active(self, full_path, section=None):
+        if section:
+            action_path = '/%s%s/%s/' % (ADMIN_PATH, section, self.id)
+        else:
+            action_path = '/%s%s/' % (ADMIN_PATH, self.id)
+        # Paths overlap = active action
+        return len(action_path) <= full_path and full_path[:len(action_path)] == action_path
+
+
+"""
+Admin site section
+"""
+class AdminSection(AdminSiteItem):
+    def __init__(self, section=None, **kwargs):
+        self.actions = []
+        self.last = None
+        super(AdminSection, self).__init__(**kwargs)
+        
+    def get_routes(self):
+        routes = []
+        first_action = True
+        for action in self.actions:
+            if first_action:
+                routes += patterns('', url('^', include(action.urlpatterns)))
+                first_action = False
+            else:
+                routes += patterns('', url(('^%s/' % action.id), include(action.urlpatterns)))
+        return routes
+            
+    def is_active(self, full_path):
+        action_path = '/%s%s/' % (ADMIN_PATH, self.id)
+        # Paths overlap = active action
+        return len(action_path) <= full_path and full_path[:len(action_path)] == action_path
+
+
+"""
+Admin site class that knows ACP structure
+"""
+class AdminSite(object):
+    actions_index = {}
+    routes = []
+    sections = []
+    sections_index = {}
+    
+    def discover(self):
+        """
+        Build admin site structure
+        """
+        # Return discovered admin routes, so we dont repeat ourself
+        if self.routes:
+            return self.routes
+        
+        # Found actions
+        actions = []
+        
+        # Orphan actions that have no section yet
+        late_actions = []
+        
+        # Iterate over installed applications
+        for app_name in settings.INSTALLED_APPS:
+            try:
+                app = import_module(app_name + '.admin')
+                
+                # Attempt to import sections
+                try:
+                    for section in app.ADMIN_SECTIONS:
+                        self.sections.append(section)
+                        self.sections_index[section.id] = section
+                except AttributeError:
+                    pass
+                
+                # Attempt to import actions
+                try:
+                    for action in app.ADMIN_ACTIONS:
+                        self.actions_index[action.id] = action
+                        if action.section in self.sections_index:
+                            if not action.after:
+                                 action.after = self.sections_index[action.section].last
+                            actions.append(action)
+                            self.sections_index[action.section].last = action.after
+                        else:
+                            late_actions.append(action)
+                except AttributeError:
+                    pass
+            except ImportError:
+                pass
+                
+        # So actions and late actions
+        actions += late_actions
+        
+        # Sorth sections and actions
+        sort_sections = SortList(self.sections)
+        sort_actions = SortList(actions)
+        self.sections = sort_sections.sort()
+        actions = sort_actions.sort()
+        
+        # Put actions in sections
+        for action in actions:
+            self.sections_index[action.section].actions.append(action)
+        
+        # Return ready admin routing
+        first_section = True
+        for section in self.sections:
+            if first_section:
+                self.routes += patterns('', url('^', include(section.get_routes())))
+                first_section = False
+            else:
+                self.routes += patterns('', url(('^%s/' % section.id), include(section.get_routes())))
+        return self.routes
+    
+    def get_action(self, action):
+        """
+        Get admin action
+        """
+        return self.actions_index.get(action)
+            
+    def get_admin_index(self):
+        """
+        Return admin index route - first action of first section
+        """
+        return self.sections[0].actions[0].route
+            
+    def get_admin_navigation(self, request):
+        """
+        Find and return current admin navigation
+        """
+        sections = []
+        actions = []
+        active_section = False
+        active_action = False
+        
+        # Loop sections, build list of sections and find active section
+        for section in self.sections:
+            is_active = section.is_active(request.path)
+            sections.append({
+                             'is_active': is_active,
+                             'name': section.name,
+                             'icon': section.icon,
+                             'route': section.actions[0].route
+                             })
+            if is_active:
+                active_section = section
+        
+        # If no section was found to be active, default to first one
+        if not active_section:
+            active_section = self.sections[0]
+            sections[0]['is_active'] = True
+            
+        # Loop active section actions
+        for action in active_section.actions:
+            is_active = action.is_active(request.path, active_section.id if active_section != self.sections[0] else None)
+            actions.append({
+                             'is_active': is_active,
+                             'name': action.name,
+                             'icon': action.icon,
+                             'help': action.help,
+                             'route': action.route
+                             })
+            if is_active:
+                active_action = action
+        
+        # If no action was found to be active, default to first one
+        if not active_action:
+            active_action = active_section.actions[0]
+            actions[0]['is_active'] = True
+        
+        # Return admin navigation for this location
+        return {
+                'sections': sections,
+                'actions': actions,
+                'admin_index': self.get_admin_index(),
+                }
+
+
+site = AdminSite();

+ 4 - 0
misago/admin/context_processors.py

@@ -0,0 +1,4 @@
+from misago.admin import site
+
+def admin(request):
+    return site.get_admin_navigation(request)

+ 41 - 0
misago/admin/fixtures.py

@@ -0,0 +1,41 @@
+import base64
+from misago.admin.models import Section, Action
+from misago.utils import ugettext_lazy as _
+from misago.utils import get_msgid
+    
+admin_fixtures = {
+    # Overview
+    'overview': {
+        'name': _('Overview'),
+        'icon': 'signal',
+        'pos': 0,
+    },
+    
+    # Users
+    'users': {
+        'name': _('Users'),
+        'icon': 'user',
+        'pos': 100,
+    },
+    
+    # Forums
+    'forums': {
+        'name': _('Forums'),
+        'icon': 'comment',
+        'pos': 200,
+    },
+    
+    # Permissions
+    'permissions': {
+        'name': _('Permissions'),
+        'icon': 'adjust',
+        'pos': 300,
+    },
+    
+    # System
+    'system': {
+        'name': _('System'),
+        'icon': 'wrench',
+        'pos': 400,
+    },
+} 

+ 4 - 0
misago/admin/views.py

@@ -0,0 +1,4 @@
+from django.template import RequestContext
+
+def todo(request):
+    return request.theme.render_to_response('todo.html', context_instance=RequestContext(request));

+ 474 - 0
misago/admin/widgets.py

@@ -0,0 +1,474 @@
+from django import forms
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+from django.template import RequestContext
+from django.utils.translation import ugettext_lazy as _
+from jinja2 import TemplateNotFound
+import math
+from misago.forms import Form
+from misago.forms.layouts import *
+from misago.messages import Message, BasicMessage
+from misago.search import SearchException
+
+"""
+Class widgets
+"""
+class BaseWidget(object):
+    """
+    Admin Widget abstract class, providing widgets with common or shared functionality
+    """
+    admin = None
+    id = None
+    fallback = None
+    name = None
+    help = None
+    notfound_message = None
+    
+    def __new__(cls, request, **kwargs):
+        obj = super(BaseWidget, cls).__new__(cls)
+        if not obj.name:
+            obj.name = obj.get_name()
+        if not obj.help:
+            obj.help = obj.get_help()
+        return obj(request, **kwargs)
+    
+    def get_token(self, token):
+        return '%s_%s_%s' % (self.id, token, str('%s.%s' % (self.admin.model.__module__, self.admin.model.__class__.__name__)))
+        
+    def get_url(self):
+        return reverse(self.admin.get_action_attr(self.id, 'route'))
+    
+    def get_name(self):
+        return self.admin.get_action_attr(self.id, 'name')
+    
+    def get_help(self):
+        return self.admin.get_action_attr(self.id, 'help')
+    
+    def get_id(self):
+        return 'admin_%s' % self.id
+         
+    def get_templates(self, template):
+        return ('%s/%s/%s.html' % (str(self.admin.model.__module__).split('.')[1], str(self.admin.route).lower(), template),
+                'admin/%s.html' % template)
+            
+    def get_fallback_url(self, request):
+        return reverse(self.fallback)
+        
+    def get_target(self, request, model):
+        pass
+    
+    def get_target_name(self, model):
+        try:
+            return model.__dict__[self.target_name]
+        except AttributeError:
+            return None
+        
+    def get_and_validate_target(self, request, target):
+        try:
+            model = self.admin.model.objects.select_related().get(pk=target)
+            self.get_target(request, model)
+            return model
+        except self.admin.model.DoesNotExist:
+            request.messages.set_flash(BasicMessage(self.notfound_message), 'error', self.admin.id)
+        except ValueError as e:
+            request.messages.set_flash(BasicMessage(e.args[0]), 'error', self.admin.id)
+        return None
+
+
+class ListWidget(BaseWidget):
+    """
+    Items list widget
+    """
+    actions =[]
+    columns = []
+    sortables = {}
+    default_sorting = None
+    is_filtering = False
+    pagination = None
+    template = 'list'
+    hide_actions = False
+    empty_message = _('There are no items to display')
+    empty_search_message = _('Search has returned no items')
+    nothing_checked_message = _('You have to select at least one item.')
+    prompt_select = False
+    
+    def get_item_actions(self, request, item):
+        """
+        Provides request and item, should return list of tuples with item actions in following format:
+        (id, name, help, icon, link)
+        """
+        return []
+    
+    def action(self, icon=None, name=None, url=None, post=False, prompt=None):
+        """
+        Function call to make hash with item actions
+        """
+        if prompt:
+            self.prompt_select = True
+        return {
+                'icon': icon,
+                'name': name,
+                'url': url,
+                'post': post,
+                'prompt': prompt,
+                }
+        
+    def get_search_form(self, request):
+        """
+        Build a form object with items search
+        """
+        return None
+            
+    def set_filters(self, model, filters):
+        """
+        Set filters on model using filters from session
+        """
+        return None
+    
+    def get_table_form(self, request, page_items):
+        """
+        Build a form object with list of all items fields
+        """
+        return None
+    
+    def get_actions_form(self, page_items):
+        """
+        Build a form object with list of all items actions
+        """
+        if not self.actions:
+            return None # Dont build form
+        form_fields = {}
+        list_choices = []
+        for action in self.actions:
+            list_choices.append((action[0], action[1]))
+        form_fields['list_action'] = forms.ChoiceField(choices=list_choices)
+        list_choices = []
+        for item in page_items:
+            list_choices.append((item.pk, None))
+        form_fields['list_items'] = forms.MultipleChoiceField(choices=list_choices,widget=forms.CheckboxSelectMultiple)
+        return type('AdminListForm', (Form,), form_fields)
+        
+    def get_sorting(self, request):
+        """
+        Return list sorting method.
+        A list with three values:
+        - Field we use to sort over
+        - Sorting direction
+        - order_by() argument
+        """
+        sorting_method = None
+        if request.session.get(self.get_token('sort')) and request.session.get(self.get_token('sort'))[0] in self.sortables:
+            sorting_method = request.session.get(self.get_token('sort'))
+            
+        if request.GET.get('sort') and request.GET.get('sort') in self.sortables:
+            new_sorting = request.GET.get('sort')
+            sorting_dir = int(request.GET.get('dir')) == 1
+            sorting_method = [
+                    new_sorting,
+                    sorting_dir,
+                    new_sorting if sorting_dir else '-%s' % new_sorting
+                   ]
+            request.session[self.get_token('sort')] = sorting_method
+            
+        if not sorting_method:
+            new_sorting = self.sortables.keys()[0]
+            if self.default_sorting in self.sortables:
+                new_sorting = self.default_sorting
+            sorting_method = [
+                    new_sorting,
+                    self.sortables[new_sorting] == True,
+                    new_sorting if self.sortables[new_sorting] else '-%s' % new_sorting
+                   ]
+        return sorting_method
+    
+    def get_pagination_url(self, page):
+        return reverse(self.admin.get_action_attr(self.id, 'route'), kwargs={'page': page})
+    
+    def get_pagination(self, request, total, page):
+        """
+        Return list pagination.
+        A list with three values:
+        - Offset for ORM slicing
+        - Length of slice
+        - no. of prev page (or -1 for first page)
+        - no. of next page (or -1 for last page)
+        - Current page
+        - Pages total
+        """
+        if not self.pagination or total < 0:
+            # Dont do anything if we are not paging
+            return None
+        
+        # Set basic pagination, use either Session cache or new page value
+        pagination = {'start': 0, 'stop': 0, 'prev': -1, 'next': -1}
+        if request.session.get(self.get_token('pagination')):
+            pagination['start'] = request.session.get(self.get_token('pagination'))
+        page = int(page)
+        if page > 0:
+            pagination['start'] = (page - 1) * self.pagination
+            
+        # Set page and total stat
+        pagination['page'] = int(pagination['start'] / self.pagination) + 1
+        pagination['total'] = int(math.ceil(total / float(self.pagination)))
+            
+        # Fix too large offset
+        if pagination['start'] > total:
+            pagination['start'] = 0
+            
+        # Allow prev/next?
+        if total > self.pagination:
+            if pagination['page'] > 1:
+                pagination['prev'] = pagination['page'] - 1
+            if pagination['page'] < pagination['total']:
+                pagination['next'] = pagination['page'] + 1
+                
+        # Set stop offset
+        pagination['stop'] = pagination['start'] + self.pagination
+        return pagination
+    
+    def __call__(self, request, page=0):
+        """
+        Use widget as view
+        """
+        # Get basic list attributes
+        if request.session.get(self.get_token('filter')):
+            self.is_filtering = True
+            items_total = self.set_filters(self.admin.model.objects, request.session.get(self.get_token('filter'))).count()
+        else:
+            items_total = self.admin.model.objects.count()
+        sorting_method = self.get_sorting(request)
+        paginating_method = self.get_pagination(request, items_total, page)
+        
+        # List items
+        items = self.admin.model.objects
+        # Filter items?
+        if request.session.get(self.get_token('filter')):
+            items = self.set_filters(items, request.session.get(self.get_token('filter')))
+        else:
+            items = items.all()
+        items = items.order_by(sorting_method[2])
+        if self.pagination:
+            items = items[paginating_method['start']:paginating_method['stop']]
+            
+        # Default message
+        message = request.messages.get_message(self.admin.id)
+        
+        # See if we should make and handle search form
+        search_form = None
+        SearchForm = self.get_search_form(request)
+        if SearchForm:
+            if request.method == 'POST':
+                # New search
+                if request.POST.get('origin') == 'search':
+                    search_form = SearchForm(request.POST, request=request)
+                    if search_form.is_valid():
+                        search_criteria = {}
+                        for field, criteria in search_form.cleaned_data.items():
+                            if len(criteria) > 0:
+                                search_criteria[field] = criteria
+                        if not search_criteria:
+                            message = BasicMessage(_("No search criteria have been defined."))
+                        else:
+                            request.session[self.get_token('filter')] = search_criteria
+                            return redirect(self.get_url())
+                    else:
+                        message = BasicMessage(_("Search form contains errors."))
+                    message.type = 'error'
+                else:
+                        search_form = SearchForm(request=request)
+                    
+                # Kill search
+                if request.POST.get('origin') == 'clear' and self.is_filtering and request.csrf.request_secure(request):
+                    request.session[self.get_token('filter')] = None
+                    request.messages.set_flash(BasicMessage(_("Search criteria have been cleared.")), 'info', self.admin.id)
+                    return redirect(self.get_url())
+            else:
+                if self.is_filtering:
+                    search_form = SearchForm(request=request, initial=request.session.get(self.get_token('filter')))
+                else:
+                    search_form = SearchForm(request=request)
+        
+        # See if we sould make and handle tab form
+        table_form = None
+        TableForm = self.get_table_form(request, items)
+        if TableForm:
+            if request.method == 'POST' and request.POST.get('origin') == 'table':
+                pass
+            else:
+                table_form = TableForm(request=request)
+        
+        # See if we should make and handle list form
+        list_form = None
+        ListForm = self.get_actions_form(items)
+        if ListForm:
+            if request.method == 'POST' and request.POST.get('origin') == 'list':
+                list_form = ListForm(request.POST, request=request)
+                if list_form.is_valid():
+                    try:
+                        form_action = getattr(self, 'action_' + list_form.cleaned_data['list_action'])
+                        message, redirect_url = form_action(request, items, list_form.cleaned_data['list_items'])
+                        if redirect:
+                            request.messages.set_flash(message, message.type, self.admin.id)
+                            return redirect(redirect_url)
+                    except AttributeError:
+                        message = BasicMessage(_("Action requested is incorrect."))
+                        message.type = 'error'
+                else:
+                    if 'list_items' in list_form.errors:
+                        message = BasicMessage(self.nothing_checked_message)
+                    elif 'list_action' in list_form.errors:
+                        message = BasicMessage(_("Action requested is incorrect."))
+                    else:
+                        message = Message(request, list_form.non_field_errors()[0])
+                    message.type = 'error'
+            else:
+                list_form = ListForm(request=request)
+                
+        # Render list
+        return request.theme.render_to_response(self.get_templates(self.template),
+                                                {
+                                                 'admin': self.admin,
+                                                 'action': self,
+                                                 'request': request,
+                                                 'url': self.get_url(),
+                                                 'message': message,
+                                                 'sorting': self.sortables,
+                                                 'sorting_method': sorting_method,
+                                                 'pagination': paginating_method,
+                                                 'list_form': FormLayout(list_form) if list_form else None,
+                                                 'search_form': FormLayout(search_form) if search_form else None,
+                                                 'table_form': FormFields(table_form) if table_form else None,
+                                                 'items': items,
+                                                 'items_total': items_total,
+                                                },
+                                                context_instance=RequestContext(request));
+                                                
+class FormWidget(BaseWidget):
+    """
+    Form page widget
+    """
+    template = 'form'
+    submit_button = _("Save Changes")
+    form = None
+    layout = None
+    target_name = None
+    submit_fallback = False
+    
+    def get_url(self, request, model):
+        return reverse(self.admin.get_action_attr(self.id, 'route'))
+    
+    def get_form(self, request, model):
+        return self.form
+    
+    def get_layout(self, request, form, model):
+        if self.layout:
+            return self.layout
+        return form.layout
+    
+    def get_initial_data(self, request, model):
+        return {}
+    
+    def submit_form(self, request, form, model):
+        """
+        Handle form submission, ALWAYS return tuple with model and message
+        """
+        pass
+    
+    def __call__(self, request, target=None, slug=None):
+        # Default message
+        message = request.messages.get_message(self.admin.id)
+
+        # Fetch target?
+        model = None
+        if target:
+            model = self.get_and_validate_target(request, target)
+            if not model:
+                return redirect(self.get_fallback_url(request))
+        original_model = model
+        
+        # Get form type to instantiate
+        FormType = self.get_form(request, target)
+        
+        #Submit form
+        if request.method == 'POST':
+            form = FormType(request.POST, request=request, initial=self.get_initial_data(request, model))
+            if form.is_valid():
+                model, message = self.submit_form(request, form, model)
+                if message.type != 'error':
+                    request.messages.set_flash(message, message.type, self.admin.id)
+                    # Redirect back to right page
+                    try:
+                        if 'save_new' in request.POST and self.get_new_url:
+                            return redirect(self.get_new_url(request, model))
+                    except AttributeError:
+                        pass
+                    try:
+                        if 'save_edit' in request.POST and self.get_edit_url:
+                            return redirect(self.get_edit_url(request, model))
+                    except AttributeError:
+                        pass
+                    try:
+                        if self.get_submit_url:
+                            return redirect(self.get_submit_url(request, model))
+                    except AttributeError:
+                        pass
+                    return redirect(self.get_fallback_url(request))
+            else:
+                message = Message(request, form.non_field_errors()[0])
+                message.type = 'error'
+        else:
+            form = FormType(request=request, initial=self.get_initial_data(request, model))
+            
+        # Render form
+        return request.theme.render_to_response(self.get_templates(self.template),
+                                                {
+                                                 'admin': self.admin,
+                                                 'action': self,
+                                                 'request': request,
+                                                 'url': self.get_url(request, model),
+                                                 'fallback': self.get_fallback_url(request),
+                                                 'message': message,
+                                                 'target': self.get_target_name(original_model),
+                                                 'target_model': original_model,
+                                                 'form': FormLayout(form, self.get_layout(request, form, target)),
+                                                },
+                                                context_instance=RequestContext(request));
+
+                                        
+class ButtonWidget(BaseWidget):
+    """
+    Button Action Widget
+    This widget handles most basic and common type of admin action - button press:
+    - User presses button on list (for example "delete this user!")
+    - Widget checks if request is CSRF-valid and POST
+    - Widget optionally chcecks if target has been provided and action is allowed at all
+    - Widget does action and redirects us back to fallback url
+    """
+    def __call__(self, request, target=None, slug=None):
+        # Fetch target?
+        model = None
+        if target:
+            model = self.get_and_validate_target(request, target)
+            if not model:
+                return redirect(self.get_fallback_url(request))
+        original_model = model
+            
+        # Crash if this is invalid request
+        if not request.csrf.request_secure(request):
+            request.messages.set_flash(BasicMessage(_("Action authorization is invalid.")), 'error', self.admin.id)
+            return redirect(self.get_fallback_url(request))
+        
+        # Do something
+        message, url = self.action(request, model)
+        request.messages.set_flash(message, message.type, self.admin.id)
+        if url:
+            return redirect(url)
+        return redirect(self.get_fallback_url(request))
+        
+    def action(self, request, target):
+        """
+        Action to be executed when button is pressed
+        Define custom one in your Admin action.
+        It should return response and message objects 
+        """
+        pass

+ 0 - 0
misago/auth/__init__.py


+ 116 - 0
misago/auth/fixtures.py

@@ -0,0 +1,116 @@
+from misago.settings.fixtures import load_settings_fixture
+from misago.utils import ugettext_lazy as _
+from misago.utils import get_msgid
+
+settings_fixtures = (
+   # Register and Sign-In Settings
+   ('register-and-signin', {
+        'name': _("Register and Sign-In Settings"),
+        'description': _("Those settings allow you to increase security of your members accounts."),
+        'settings': (
+            ('account_activation', {
+                'type':         "string",
+                'input':        "choice",
+                'extra':        {'choices': [('', _("No validation required")), ('user', _("Activation Token sent to User")), ('admin', _("Activation by Administrator")), ('block', _("Dont allow new registrations"))]},
+                'separator':    _("Users Registrations"),
+                'name':         _("New accounts validation"),
+            }),
+            ('default_timezone', {
+                'value':        "utc",
+                'type':         "string",
+                'input':        "select",
+                'extra':        {'choices': '#TZ#'},
+                'name':         _("Default Timezone"),
+                'description':  _("Used by guests, crawlers and newly registered users."),
+            }),
+            ('password_length', {
+                'value':        4,
+                'type':         "integer",
+                'input':        "text",
+                'extra':        {'min': 1},
+                'separator':    _("Users Passwords"),
+                'name':         _("Minimum user password length"),
+            }),
+            ('password_complexity', {
+                'value':        [],
+                'type':         "array",
+                'input':        "mlist",
+                'extra':        {'choices': [('case', _("Require mixed Case")), ('digits', _("Require digits")), ('special', _("Require special characters"))]},
+                'name':         _("Password Complexity"),
+            }),
+            ('password_lifetime', {
+                'value':        0,
+                'type':         "integer",
+                'input':        "text",
+                'extra':        {'min': 0},
+                'name':         _("Password Lifetime"),
+                'description':  _("Enter number of days since password was set to force member to change it with new one, or 0 to dont force your members to change their passwords."),
+            }),
+            ('sessions_hidden', {
+                'value':        True,
+                'type':         "boolean",
+                'input':        "yesno",
+                'separator':    _("Sessions Settings"),
+                'name':         _("Allow hidden sessions"),
+                'description':  _("Enabling this option will allow users to hide their presence on forums from other members."),
+            }),
+            ('sessions_validate_ip', {
+                'value':        True,
+                'type':         "boolean",
+                'input':        "yesno",
+                'name':         _("Check IP on session authorization"),
+                'description':  _("Makes sessions more secure, but can cause problems with proxies and VPN's."),
+            }),
+            ('remember_me_allow', {
+                'value':        True,
+                'type':         "boolean",
+                'input':        "yesno",
+                'separator':    _("Sign-In Settings"),
+                'name':         _('Enable "Remember Me" functionality'),
+                'description':  _("Turning this option on allows users to sign in on to your board using cookie-based tokens. This may result in account compromisation when user fails to sign out on shared computer."),
+            }),
+            ('remember_me_lifetime', {
+                'value':        90,
+                'type':         "integer",
+                'input':        "text",
+                'name':         _('"Remember Me" token lifetime'),
+                'description':  _('Number of days since either last use or creation of "Remember Me" token to its expiration.'),
+            }),
+            ('remember_me_extensible', {
+                'value':        1,
+                'type':         "boolean",
+                'input':        "yesno",
+                'name':         _('Allow "Remember Me" tokens refreshing'),
+                'description':  _('Set this setting to off if you want to force your users to periodically update their "Remember Me" tokens by signing in. If this option is on, Tokens are updated when they are used to open new session.'),
+            }),
+            ('login_attempts_limit', {
+                'value':        3,
+                'default':      3,
+                'type':         "integer",
+                'input':        "text",
+                'separator':    _("Brute-Force Countermeasures"),
+                'name':         _("Limit Sign In attempts"),
+                'description':  _('Enter maximal number of allowed Sign In attempts before IP address "jams".'),
+            }),
+            ('registrations_jams', {
+                'value':        1,
+                'default':      1,
+                'type':         "boolean",
+                'input':        "yesno",
+                'name':         _("Count failed register attempts too"),
+                'description':  _("Set this setting to yes if you want failed register attempts to count into limit."),
+            }),
+            ('jams_lifetime', {
+                'value':        15,
+                'default':      15,
+                'type':         "integer",
+                'input':        "text",
+                'name':         _("Automaticaly unlock jammed IPs"),
+                'description':  _('Enter number of minutes since IP address "jams" to automatically unlock it, or 0 to never unlock jammed IP adresses. Jams dont count as bans.'),
+            }),
+        ),
+    }),
+)
+
+def load_fixture():
+    load_settings_fixture(settings_fixtures)

+ 109 - 0
misago/auth/forms.py

@@ -0,0 +1,109 @@
+import hashlib
+from django import forms
+from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
+from misago.forms import Form
+from misago.security import captcha
+from misago.users.models import User
+from misago.users.validators import validate_password, validate_email
+
+
+class UserRegisterForm(Form):
+    username = forms.CharField(max_length=15,help_text=_("Between 3 and 15 characters, only letters and digits are allowed."))
+    email = forms.EmailField(max_length=255,help_text=_("Working e-mail inbox is required to maintain control over your forum account."))
+    email_rep = forms.EmailField(max_length=255)
+    password = forms.CharField(max_length=255,help_text=_("Password you will be using to sign in to your account. Make sure it's strong."))
+    password_rep = forms.CharField(max_length=255)
+    captcha_qa = captcha.QACaptchaField()
+    recaptcha = captcha.ReCaptchaField()
+    accept_tos = forms.BooleanField(required=True,label=_("Forum Terms of Service"),error_messages={'required': _("Acceptation of board ToS is mandatory for membership.")})
+    
+    validate_repeats = (('email', 'email_rep'), ('password', 'password_rep'))
+    repeats_errors = [{
+                       'different': _("Entered addresses do not match."), 
+                       },
+                      {
+                       'different': _("Entered passwords do not match."),
+                       }]
+    
+    layout = [
+                 (
+                     None,
+                     [('username', {'placeholder': _("Enter your desired username")})]
+                 ),
+                 (
+                     None,
+                     [('nested', [('email', {'placeholder': _("Enter your e-mail"), 'width': 50}), ('email_rep', {'placeholder': _("Repeat your e-mail"), 'width': 50})]), 
+                      ('nested', [('password', {'has_value': False, 'placeholder': _("Enter your password"), 'width': 50}), ('password_rep', {'has_value': False, 'placeholder': _("Repeat your password"), 'width': 50})])]
+                 ),
+                 (
+                     None,
+                     ['captcha_qa', 'recaptcha']
+                 ),
+                 (
+                     None,
+                     [('accept_tos', {'inline': _("I have read and accept this forums Terms of Service.")})]
+                 ),
+             ]
+    
+    class Meta:
+        widgets = {
+            'password': forms.PasswordInput(),
+            'password_rep': forms.PasswordInput(),
+        }
+        
+    def clean_username(self):
+        new_user = User.objects.get_blank_user()
+        new_user.set_username(self.cleaned_data['username'])
+        try:
+            new_user.full_clean()
+        except ValidationError as e:
+            new_user.is_username_valid(e)
+        return self.cleaned_data['username']
+        
+    def clean_email(self):
+        new_user = User.objects.get_blank_user()
+        new_user.set_email(self.cleaned_data['email'])
+        try:
+            new_user.full_clean()
+        except ValidationError as e:
+            new_user.is_email_valid(e)
+        return self.cleaned_data['email']
+        
+    def clean_password(self):
+        new_user = User.objects.get_blank_user()
+        new_user.set_password(self.cleaned_data['password'])
+        try:
+            new_user.full_clean()
+        except ValidationError as e:
+            new_user.is_password_valid(e)
+        validate_password(self.cleaned_data['password'])
+        return self.cleaned_data['password']
+    
+    
+class UserSendSpecialMailForm(Form):
+    email = forms.EmailField(max_length=255,label=_("Your E-mail Address"),help_text=_("Your account's email address."))
+    captcha_qa = captcha.QACaptchaField()
+    recaptcha = captcha.ReCaptchaField()
+    error_source = 'email'
+    
+    layout = [
+                  (
+                      None,
+                      [('email', {'placeholder': _("Enter your e-mail address.")})]
+                  ),
+                  (
+                      None,
+                      ['captcha_qa', 'recaptcha']
+                  ),
+             ]
+    
+    def clean_email(self):
+        try:
+            email = self.cleaned_data['email'].lower()
+            email_hash = hashlib.md5(email).hexdigest()
+            self.found_user = User.objects.get(email_hash=email_hash)
+        except User.DoesNotExist:
+            raise ValidationError(_("There is no user with such e-mail address."))
+        return email
+    

+ 9 - 0
misago/auth/urls.py

@@ -0,0 +1,9 @@
+from django.conf.urls import patterns, url, include
+
+urlpatterns = patterns('misago.auth.views',
+    url(r'^register/$', 'register', name="register"),
+    url(r'^activate/(?P<username>\w+)-(?P<user>\d+)/(?P<token>[a-zA-Z0-9]+)/$', 'activate', name="activate"),
+    url(r'^resend-activation/$', 'send_activation', name="send_activation"),
+    url(r'^reset-pass/$', 'forgot_password', name="forgot_password"),
+    url(r'^reset-pass/(?P<username>\w+)-(?P<user>\d+)/(?P<token>[a-zA-Z0-9]+)/$', 'reset_password', name="reset_password"),
+)

+ 210 - 0
misago/auth/views.py

@@ -0,0 +1,210 @@
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+from django.template import RequestContext
+from django.utils import timezone
+from django.utils.translation import ugettext as _
+from misago.banning.decorators import block_banned
+from misago.banning.views import error_banned
+from misago.forms.layouts import FormLayout
+from misago.messages import Message
+from misago.security import get_random_string
+from misago.security.auth import sign_user_in
+from misago.security.decorators import *
+from misago.sessions.models import *
+from misago.auth.forms import *
+from misago.users.models import User, Group
+from misago.views import error403, error404
+
+@block_banned
+@block_authenticated
+@block_jammed
+def register(request):
+    if request.settings['account_activation'] == 'block':
+        return error403(request, Message(request, 'auth/registrations_off'))
+    message = None
+    if request.method == 'POST':
+        form = UserRegisterForm(request.POST, request=request)
+        if form.is_valid():
+            need_activation = 0
+            if request.settings['account_activation'] == 'user':
+                need_activation = User.ACTIVATION_USER
+            if request.settings['account_activation'] == 'admin':
+                need_activation = User.ACTIVATION_ADMIN
+            new_user = User.objects.create_user(
+                                                form.cleaned_data['username'],
+                                                form.cleaned_data['email'],
+                                                form.cleaned_data['password'],
+                                                Group.objects.get(pk=3), # Registered members
+                                                ip=request.session.get_ip(request),
+                                                activation=need_activation,
+                                                request=request
+                                                )
+            if need_activation == User.ACTIVATION_NONE:
+                # No need for activation, sign in user
+                sign_user_in(request, new_user)
+                request.messages.set_flash(Message(request, 'auth/registered_activation_none', extra={'user':new_user}), 'success')
+            if need_activation == User.ACTIVATION_USER:
+                # Mail user activation e-mail
+                request.messages.set_flash(Message(request, 'auth/registered_activation_user', extra={'user':new_user}), 'info')
+                new_user.email_user(
+                                    request,
+                                    'auth/activation_0',
+                                    _("Welcome aboard, %(username)s!" % {'username': new_user.username}),
+                                    )
+            if need_activation == User.ACTIVATION_ADMIN:
+                # Require admin activation
+                request.messages.set_flash(Message(request, 'users/registered_activation_admin', extra={'user':new_user}), 'info')
+            new_user.email_user(
+                                request,
+                                ('auth/activation_%s' % need_activation),
+                                _("Welcome aboard, %(username)s!" % {'username': new_user.username}),
+                                {'password': form.cleaned_data['password']}
+                                )
+            return redirect(reverse('index'))
+        else:
+            message = Message(request, form.non_field_errors()[0])
+            if request.settings['registrations_jams']:
+                SignInAttempt.objects.register_attempt(request.session.get_ip(request))
+            # Have we jammed our account?
+            if SignInAttempt.objects.is_jammed(request.session.get_ip(request)):
+                request.jam.expires = timezone.now()
+                return redirect(reverse('register'))
+    else:
+        form = UserRegisterForm(request=request)
+    return request.theme.render_to_response('auth/register.html',
+                                            {
+                                             'message': message,
+                                             'form': FormLayout(form),
+                                             'hide_signin': True, 
+                                            },
+                                            context_instance=RequestContext(request));
+
+
+@block_banned
+@block_authenticated
+@block_jammed
+def send_activation(request):
+    message = None
+    if request.method == 'POST':
+        form = UserSendSpecialMailForm(request.POST, request=request)
+        if form.is_valid():
+            user = form.found_user
+            if user.is_banned():
+                return error_banned(request, user)
+            if user.activation == User.ACTIVATION_NONE:
+                return error403(request, Message(request, 'auth/activation_not_required', extra={'user': user}))
+            if user.activation == User.ACTIVATION_ADMIN:
+                return error403(request, Message(request, 'auth/activation_only_by_admin', extra={'user': user}))
+            request.messages.set_flash(Message(request, 'auth/activation_resent', extra={'user':user}), 'success')
+            user.email_user(
+                            request,
+                            'auth/activation_resend',
+                            _("New Account Activation"),
+                            )
+            return redirect(reverse('index'))
+        else:
+            message = Message(request, form.non_field_errors()[0])
+    else:
+        form = UserSendSpecialMailForm(request=request)
+    return request.theme.render_to_response('auth/resend_activation.html',
+                                            {
+                                             'message': message,
+                                             'form': FormLayout(form),
+                                            },
+                                            context_instance=RequestContext(request));
+                                            
+                                            
+@block_banned
+@block_authenticated
+@block_jammed
+def activate(request, username="", user="0", token=""):
+    user = int(user)
+    try:
+        user = User.objects.get(pk=user)
+        current_activation = user.activation
+        # Run checks
+        if user.is_banned():
+            return error_banned(request, user)
+        if user.activation == User.ACTIVATION_NONE:
+            return error403(request, Message(request, 'auth/activation_not_required', extra={'user': user}))
+        if user.activation == User.ACTIVATION_ADMIN:
+            return error403(request, Message(request, 'auth/activation_only_by_admin', extra={'user': user}))
+        if not token or not user.token or user.token != token:
+            return error403(request, Message(request, 'auth/invalid_confirmation_activation', extra={'user': user}))
+        # Activate and sign in our member
+        user.activation = User.ACTIVATION_NONE
+        sign_user_in(request, user)
+        if current_activation == User.ACTIVATION_PASSWORD:
+            request.messages.set_flash(Message(request, 'auth/activated_password', extra={'user':user}), 'success')
+        else:
+            request.messages.set_flash(Message(request, 'auth/activated_new', extra={'user':user}), 'success')
+        return redirect(reverse('index'))
+    except User.DoesNotExist:
+        return error404(request)
+
+
+@block_banned
+@block_authenticated
+@block_jammed   
+def forgot_password(request):
+    message = None
+    if request.method == 'POST':
+        form = UserSendSpecialMailForm(request.POST, request=request)
+        if form.is_valid():
+            user = form.found_user
+            if user.is_banned():
+                return error_banned(request, user)
+            elif user.activation != User.ACTIVATION_NONE:
+                return error403(request, Message(request, 'auth/activation_required', {'user': user}))
+            user.token = get_random_string(12)
+            user.save(force_update=True)
+            request.messages.set_flash(Message(request, 'auth/password_reset_confirm', extra={'user':user}), 'success')
+            user.email_user(
+                            request,
+                            'auth/reset_confirm',
+                            _("Confirm New Password Request")
+                            )
+            return redirect(reverse('index'))
+        else:
+            message = Message(request, form.non_field_errors()[0])
+    else:
+        form = UserSendSpecialMailForm(request=request)
+    return request.theme.render_to_response('auth/forgot_password.html',
+                                            {
+                                             'message': message,
+                                             'form': FormLayout(form),
+                                            },
+                                            context_instance=RequestContext(request));
+
+
+@block_banned
+@block_authenticated
+@block_jammed
+def reset_password(request, username="", user="0", token=""):
+    user = int(user)
+    try:
+        user = User.objects.get(pk=user)
+        if user.is_banned():
+            return error_banned(request, user)
+        if user.activation != User.ACTIVATION_NONE:
+            return error403(request, Message(request, 'auth/activation_required', {'user': user}))
+        if not token or not user.token or user.token != token:
+            return error403(request, Message(request, 'auth/invalid_confirmation_link', {'user': user}))
+        new_password = get_random_string(6)
+        user.token = None
+        user.set_password(new_password)
+        user.save(force_update=True)
+        # Logout signed in and kill remember me tokens
+        Session.objects.filter(user=user).update(user=None)
+        Token.objects.filter(user=user).delete()
+        # Set flash and mail new password
+        request.messages.set_flash(Message(request, 'auth/password_reset_done', extra={'user':user}), 'success')
+        user.email_user(
+                        request,
+                        'auth/reset_new',
+                        _("Your New Password"),
+                        {'password': new_password}
+                        )
+        return redirect(reverse('sign_in'))
+    except User.DoesNotExist:
+        return error404(request)

+ 0 - 0
misago/banning/__init__.py


+ 6 - 0
misago/banning/context_processors.py

@@ -0,0 +1,6 @@
+def banning(request):
+    if request.user.is_crawler():
+        return {}
+    return {
+        'is_banned': request.ban.is_banned(),
+    }

+ 13 - 0
misago/banning/decorators.py

@@ -0,0 +1,13 @@
+from misago.banning.views import error_banned
+
+def block_banned(f):
+    def decorator(*args, **kwargs):
+        request = args[0]
+        try:
+            if request.user.is_banned() or request.ban.is_banned():
+                return error_banned(request);
+            return f(*args, **kwargs)
+        except AttributeError:
+            pass
+        return f(*args, **kwargs)
+    return decorator

+ 4 - 0
misago/banning/fixtures.py

@@ -0,0 +1,4 @@
+from misago.monitor.fixtures import load_monitor_fixture
+
+def load_fixture():
+    load_monitor_fixture({'bans_version': 0})

+ 17 - 0
misago/banning/middleware.py

@@ -0,0 +1,17 @@
+from misago.banning.models import BanCache
+from misago.users.models import Guest
+
+class BanningMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_crawler():
+            return None
+        try:
+            request.ban = request.session['ban']
+        except KeyError:
+            request.ban = BanCache()
+            request.session['ban'] = request.ban
+        if not request.firewall.admin:
+            request.ban.check_for_updates(request)
+            # Make sure banned session is downgraded to guest level
+            if request.user.is_banned() or request.ban.is_banned():
+                request.session.sign_out(request)

+ 69 - 0
misago/banning/models.py

@@ -0,0 +1,69 @@
+import re
+from django.utils import timezone
+from django.db import models
+from django.db.models import Q
+
+
+BAN_NAME_EMAIL = 0
+BAN_NAME = 1
+BAN_EMAIL = 2
+BAN_IP = 3
+
+
+class Ban(models.Model):
+    type = models.PositiveIntegerField(default=BAN_NAME_EMAIL)
+    ban = models.CharField(max_length=255)
+    expires = models.DateTimeField(null=True,blank=True,db_index=True)
+
+    
+def check_ban(ip=False, username=False, email=False):
+    bans_model = Ban.objects.filter(Q(expires=None) | Q(expires__gt=timezone.now()))
+    if not (ip and username and email):
+        if ip:
+            bans_model.filter(type=BAN_IP)
+        if username:
+            bans_model.filter(type=BAN_NAME_EMAIL)
+            bans_model.filter(type=BAN_NAME)
+        if email:
+            bans_model.filter(type=BAN_NAME_EMAIL)
+            bans_model.filter(type=BAN_EMAIL)
+    for ban in bans_model.order_by('-expires').iterator():
+        if (
+            # Check user name
+            ((username and (ban.type == BAN_NAME_EMAIL or ban.type == BAN_NAME))
+            and re.search('^'+re.escape(ban.ban).replace('\*', '(.*?)')+'$', username, flags=re.IGNORECASE))
+            or # Check user email
+            ((email and (ban.type == BAN_NAME_EMAIL or ban.type == BAN_EMAIL))
+            and re.search('^'+re.escape(ban.ban).replace('\*', '(.*?)')+'$', email, flags=re.IGNORECASE))
+            or # Check IP address
+            (ip and ban.type == BAN_IP
+            and re.search('^'+re.escape(ban.ban).replace('\*', '(.*?)')+'$', ip, flags=re.IGNORECASE))):
+                return ban
+    return False
+
+
+class BanCache(object):
+    banned = False
+    type = None
+    expires = None
+    version = 0
+    def check_for_updates(self, request):
+        if (self.version < request.monitor['bans_version']
+            or (self.expires != None and self.expires < timezone.now())):
+            self.version = request.monitor['bans_version']
+            ban = check_ban(
+                            ip=request.session.get_ip(request),
+                            )
+            if ban:
+                self.banned = True
+                self.expires = ban.expires
+                self.type = ban.type
+            else:
+                self.banned = False
+                self.expires = None
+                self.type = None
+            return True
+        return False    
+    
+    def is_banned(self):
+        return self.banned

+ 11 - 0
misago/banning/views.py

@@ -0,0 +1,11 @@
+from django.utils.translation import ugettext as _
+from misago.messages import Message
+from misago.views import error403
+
+def error_banned(request, user=False):
+    if not user:
+        user = request.user
+    if user.is_banned():
+        return error403(request, Message(request, 'banning/banned_user', extra={'user': user}), _("You are banned"));
+    if request.ban.is_banned():
+        return error403(request, Message(request, 'banning/banned_ip'), _("You are banned"));

+ 15 - 0
misago/context_processors.py

@@ -0,0 +1,15 @@
+from django.conf import settings
+from misago import get_version
+
+# Register context processors
+def core(request):
+    return {
+        'board_address': settings.BOARD_ADDRESS,
+        'version': get_version(),
+        'f': {
+                    'DATE_FORMAT': settings.DATE_FORMAT,
+                    'TIME_FORMAT': settings.TIME_FORMAT,
+                    'DATETIME_FORMAT': settings.DATETIME_FORMAT,
+                    'SHORT_DATETIME_FORMAT': settings.SHORT_DATETIME_FORMAT,
+                    }
+    }

+ 0 - 0
misago/cookie_jar/__init__.py


+ 38 - 0
misago/cookie_jar/cookie_jar.py

@@ -0,0 +1,38 @@
+from django.conf import settings
+
+class CookieJar(object):
+    _set_cookies = []
+    _delete_cookies = []
+    
+    def set(self, cookie, value, permanent=False):
+        if permanent:
+            # 360 days
+            max_age = 31104000
+        else:
+            # 48 hours
+            max_age = 172800
+        self._set_cookies.append({
+                                  'name': cookie,
+                                  'value': value,
+                                  'max_age': max_age,
+                                  })
+        
+    def delete(self, cookie):
+        self._delete_cookies.append(cookie)
+        
+    def flush(self, response):
+        for cookie in self._set_cookies:
+            response.set_cookie(
+                                settings.COOKIES_PREFIX + cookie['name'],
+                                cookie['value'],
+                                max_age=cookie['max_age'],
+                                path=settings.COOKIES_PATH or '/',
+                                domain=settings.COOKIES_DOMAIN or None,
+                                secure=settings.COOKIES_SECURE or False
+                                )
+        for cookie in self._delete_cookies:
+            response.delete_cookie(
+                                   settings.COOKIES_PREFIX + cookie,
+                                   path=settings.COOKIES_PATH or '/',
+                                   domain=settings.COOKIES_DOMAIN or None
+                                   )

+ 12 - 0
misago/cookie_jar/middleware.py

@@ -0,0 +1,12 @@
+from misago.cookie_jar.cookie_jar import CookieJar
+
+class CookieJarMiddleware(object):
+    def process_request(self, request):
+        request.cookie_jar = CookieJar()
+                    
+    def process_response(self, request, response):
+        try:
+            request.cookie_jar.flush(response)
+        except AttributeError:
+            pass
+        return response

+ 0 - 0
misago/crawlers/__init__.py


+ 23 - 0
misago/crawlers/crawler.py

@@ -0,0 +1,23 @@
+from database import CRAWLERS_NAMES, CRAWLERS_AGENTS, CRAWLERS_HOSTS
+
+class Crawler(object):
+    crawler = False
+    host = None
+    username = None
+    
+    def __init__(self, agent = None, ip = None):
+        if agent is not None:
+            for item in CRAWLERS_AGENTS.keys():
+            	if agent.find(item) != -1:
+                    self.crawler = True
+                    self.username = CRAWLERS_AGENTS[item]
+                    
+        if ip is not None:
+            for item in CRAWLERS_HOSTS.keys():
+            	if ip == item:
+                    self.crawler = True
+                    self.username = CRAWLERS_HOSTS[item]
+                    
+        if self.crawler:
+            self.username = CRAWLERS_NAMES[self.username]
+            self.host = ip

+ 16 - 0
misago/crawlers/database.py

@@ -0,0 +1,16 @@
+CRAWLERS_NAMES = {
+    'bing': 'Bingbot',
+    'google': 'Googlebot',
+    'yahoo': 'Yahoo! Slurp',
+    'yahooch': 'Yahoo! Slurp China',
+}
+
+CRAWLERS_AGENTS = {
+    'bingbot/': 'bing',
+    'Googlebot/': 'google',
+    'Yahoo! Slurp China': 'yahooch',
+    'Yahoo! Slurp': 'yahoo',
+}
+
+CRAWLERS_HOSTS = {
+}

+ 14 - 0
misago/crawlers/middleware.py

@@ -0,0 +1,14 @@
+from crawler import Crawler
+from misago.users import models
+
+class DetectCrawlerMiddleware(object):
+    def process_request(self, request):
+        # If its correct request (We have client IP), see if it exists in Crawlers DB
+        if request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR'):
+            found_crawler = Crawler(
+                                    request.META.get('HTTP_USER_AGENT', ''),
+                                    request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR')
+                                    )
+            # If crawler exists in database, use it as this request user
+            if found_crawler.crawler:
+                request.user = models.Crawler(found_crawler.username)

+ 149 - 0
misago/forms/__init__.py

@@ -0,0 +1,149 @@
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+from misago.forms.layouts import *
+from misago.security.models import QATest
+from recaptcha.client.captcha import submit as recaptcha_submit
+
+class Form(forms.Form):
+    """
+    Custom form implementation extending Django Forms functionality
+    """
+    validate_repeats = []
+    repeats_errors = []
+    dont_strip = []
+    allow_nl = []
+    error_source = None
+    def __init__(self, data=None, request=None, *args, **kwargs):
+        self.request = request
+        # Kill captcha fields
+        try:
+            if self.request.settings['bots_registration'] != 'recaptcha' or self.request.session.get('captcha_passed'):
+                del self.base_fields['recaptcha']
+        except KeyError:
+            pass
+        try:
+            if self.request.settings['bots_registration'] != 'qa' or self.request.session.get('captcha_passed'):
+                del self.base_fields['captcha_qa']
+            else:
+                # Make sure we have any questions loaded
+                self.base_fields['captcha_qa'].label = self.request.settings['qa_test']
+                self.base_fields['captcha_qa'].help_text = self.request.session['qa_test_help']
+        except KeyError:
+            pass
+        # Extract request from first argument
+        if data != None:
+            # Clean bad data
+            data = self._strip_badchars(data.copy())
+            super(Form, self).__init__(data, *args, **kwargs)
+        else:
+            super(Form, self).__init__(*args, **kwargs)
+        
+    def _strip_badchars(self, data):
+        """
+        Trim inputs and strip newlines
+        """
+        for key, field in self.base_fields.iteritems():
+            try:
+                if field.__class__.__name__ not in ['DateField', 'DateTimeField']:
+                    if not key in self.dont_strip:
+                        if field.__class__.__name__ in ['MultipleChoiceField', 'TypedMultipleChoiceField']:
+                            data[key] = [x.strip() for x in data.getlist(key.html_name, [])]
+                        else:
+                            data[key] = data[key.html_name].strip()
+                    if not key in self.allow_nl:
+                        if field.__class__.__name__ in ['MultipleChoiceField', 'TypedMultipleChoiceField']:
+                            data[key] = [x.replace("\n", '') for x in data.getlist(key.html_name, [])]
+                        else:
+                            data[key] = data[key.html_name].replace("\n", '')
+            except (KeyError, AttributeError):
+                pass
+        return data
+     
+    def clean(self):
+        """
+        Clean data, do magic checks and stuff
+        """
+        cleaned_data = super(Form, self).clean()
+        self._check_all()
+        return cleaned_data
+         
+    def clean_recaptcha(self):
+        """
+        Test reCaptcha, scream if it went wrong
+        """
+        response = recaptcha_submit(
+                                    self.request.POST.get('recaptcha_challenge_field'),
+                                    self.request.POST.get('recaptcha_response_field'),
+                                    self.request.settings['recaptcha_private'],
+                                    self.request.session.get_ip(self.request)
+                                    ).is_valid
+        if not response:
+            raise forms.ValidationError(_("Entered words are incorrect. Please try again."))
+        self.request.session['captcha_passed'] = True
+        return ''
+    
+    def clean_captcha_qa(self):
+        """
+        Test QA Captcha, scream if it went wrong
+        """
+        
+        if not unicode(self.cleaned_data['captcha_qa']).lower() in (name.lower() for name in unicode(self.request.settings['qa_test_answers']).splitlines()):
+            raise forms.ValidationError(_("The answer you entered is incorrect."))
+        self.request.session['captcha_passed'] = True
+        return self.cleaned_data['captcha_qa']
+    
+    def _check_all(self):
+        # Check repeated fields
+        self._check_repeats()
+        # Check CSRF, we dont allow un-csrf'd forms in Misago
+        self._check_csrf()
+        # Check if we have any errors from fields, if we do, we will set fancy form-wide error message
+        self._check_fields_errors()    
+       
+    def _check_repeats(self):
+        for index, repeat in enumerate(self.validate_repeats):
+            # Check empty fields
+            for field in repeat:
+                if not self.data[field]:
+                    try:
+                        if len(repeat) == 2:
+                            self.errors['_'.join(repeat)] = [self.repeats_errors[index]['fill_both']]
+                        else: 
+                            self.errors['_'.join(repeat)] = [self.repeats_errors[index]['fill_all']]
+                    except (IndexError, KeyError):
+                        if len(repeat) == 2:
+                            self.errors['_'.join(repeat)] = [_("You have to fill in both fields.")]
+                        else: 
+                            self.errors['_'.join(repeat)] = [_("You have to fill in all fields.")]
+                    break
+                    
+            else:
+                # Check different fields
+                past_field = self.data[repeat[0]]
+                for field in repeat:
+                    if self.data[field] != past_field:
+                        try:
+                            self.errors['_'.join(repeat)] = [self.repeats_errors[index]['different']]
+                        except (IndexError, KeyError):
+                            self.errors['_'.join(repeat)] = [_("Entered values differ from each other.")]
+                        break
+                    past_field = self.data[field]
+            
+        
+    def _check_csrf(self):
+        if not self.request.csrf.request_secure(self.request):
+            raise forms.ValidationError('invalid_request')
+        
+    def _check_fields_errors(self):
+        if self.errors:
+            if self.error_source and self.error_source in self.errors:
+                field_error, self.errors[self.error_source] = self.errors[self.error_source][0], []
+                raise forms.ValidationError(field_error)
+            raise forms.ValidationError('form_contains_errors')
+        
+        
+class YesNoSwitch(forms.CheckboxInput):
+    """
+    Custom Yes-No switch as fancier alternative to checkboxes
+    """
+    pass

+ 297 - 0
misago/forms/layouts.py

@@ -0,0 +1,297 @@
+from UserDict import IterableUserDict
+from django.utils import formats
+
+class FormLayout(object):
+    def __init__(self, form, fieldsets=False):
+        scaffold_fields = FormFields(form)
+        scaffold_fieldsets = FormFieldsets(form, scaffold_fields.fields, fieldsets)
+        
+        self.multipart_form = scaffold_fields.multipart_form
+        self.fieldsets = scaffold_fieldsets.fieldsets
+        self.fields = scaffold_fields.fields
+        self.hidden = scaffold_fields.hidden
+    
+class FormFields(object):
+    """
+    Hydrator that builds fields list from form and blueprint
+    """
+    def __init__(self, form):
+        self.multipart_form = False
+        self.fields = {}
+        self.hidden = []
+        
+        # Extract widgets from meta
+        self.meta_widgets = {}
+        try:
+            self.meta_widgets = form.Meta.widgets
+        except AttributeError:
+            pass
+                
+        # Find out field input types
+        for field in form.fields.keys():
+            widget = self._get_widget(field, form.fields[field])
+            widget_name = widget.__class__.__name__
+            bound_field = form[field]
+            blueprint = {
+                         'attrs': {
+                                    'id': bound_field.auto_id,
+                                    'name': bound_field.html_name,
+                                   },
+                         'endrow': False,
+                         'errors': [],
+                         'has_value': bound_field.value() != None,
+                         'help_text': bound_field.help_text,
+                         'hidden': widget.is_hidden,
+                         'html_id': bound_field.auto_id,
+                         'html_name': bound_field.html_name,
+                         'id': field,
+                         'initial': bound_field.field.initial,
+                         'label': bound_field.label,
+                         'last': False,
+                         'nested': [],
+                         'required': bound_field.field.widget.is_required,
+                         'show_hidden_initial': bound_field.field.show_hidden_initial,
+                         'value': bound_field.value(),
+                         'width': 100,
+                         'widget': '',
+                         'choices': [],
+                        }
+            
+            # Set multipart form
+            if widget.needs_multipart_form:
+                self.multipart_form = True
+            
+            # Get errors
+            for error in bound_field._errors():
+                blueprint['errors'].append(error)
+            try:
+                for error in form.errors[field]:
+                    if not error in blueprint['errors']:
+                        blueprint['errors'].append(error)
+            except KeyError:
+                pass
+            
+            # Use clean value instead?
+            try:
+                if field in form.cleaned_data:
+                    blueprint['value'] = form.cleaned_data[field]
+            except AttributeError:
+                pass
+            
+            # TextInput
+            if widget_name in ['TextInput', 'PasswordInput', 'Textarea']:
+                blueprint['widget'] = 'text'
+                blueprint['attrs']['type'] = 'text'
+                try:
+                    blueprint['attrs']['maxlength'] = bound_field.field.max_length
+                except AttributeError:
+                    pass
+            
+            # PasswordInput
+            if widget_name == 'PasswordInput':
+                blueprint['attrs']['type'] = 'password'
+              
+            # Textarea      
+            if widget_name == 'Textarea':
+                blueprint['widget'] = 'textarea'
+                
+            # ReCaptcha      
+            if widget_name == 'ReCaptchaWidget':
+                blueprint['widget'] = 'recaptcha'
+                blueprint['attrs'] = get_captcha_dict(
+                                                      form.request.settings,
+                                                      bound_field.field.api_error
+                                                      )
+                
+            # HiddenInput
+            if widget_name == 'HiddenInput':
+                blueprint['widget'] = 'hidden'
+                
+            # MultipleHiddenInput
+            if widget_name == 'MultipleHiddenInput':
+                blueprint['widget'] = 'multiple_hidden'
+                blueprint['attrs'] = {
+                                      'choices': widget.choices
+                                     }
+            
+            # FileInput
+            if widget_name == 'FileInput':
+                blueprint['widget'] = 'file'
+            
+            # ClearableFileInput
+            if widget_name == 'ClearableFileInput':
+                blueprint['widget'] = 'file_clearable'
+                
+            # DateInput
+            if widget_name == 'DateInput':
+                blueprint['widget'] = 'date'
+                try:
+                    blueprint['value'] = blueprint['value'].strftime('%Y-%m-%d')
+                except AttributeError as e:
+                    pass
+            
+            # DateTimeInput
+            if widget_name == 'DateTimeInput':
+                blueprint['widget'] = 'datetime'
+                try:
+                    blueprint['value'] = blueprint['value'].strftime('%Y-%m-%d %H:%M')
+                except AttributeError as e:
+                    pass
+            
+            # TimeInput
+            if widget_name == 'TimeInput':
+                blueprint['widget'] = 'time'
+                try:
+                    blueprint['value'] = blueprint['value'].strftime('%H:%M')
+                except AttributeError as e:
+                    pass
+                
+            # CheckboxInput
+            if widget_name == 'CheckboxInput':
+                blueprint['widget'] = 'checkbox'
+                
+            # Select, NullBooleanSelect, SelectMultiple, RadioSelect, CheckboxSelectMultiple
+            if widget_name in ['Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple']:
+                blueprint['choices'] = widget.choices
+                
+            # Yes-no radio select
+            if widget_name == 'YesNoSwitch':
+                blueprint['widget'] = 'yes_no_switch'
+            
+            # Select
+            if widget_name == 'Select':
+                blueprint['widget'] = 'select'
+                if not blueprint['value']:
+                    blueprint['value'] = u''
+                
+            # NullBooleanSelect
+            if widget_name == 'NullBooleanSelect':
+                blueprint['widget'] = 'null_boolean_select'
+                
+            # SelectMultiple
+            if widget_name == 'SelectMultiple':
+                blueprint['widget'] = 'select_multiple'
+                
+            # RadioSelect
+            if widget_name == 'RadioSelect':
+                blueprint['widget'] = 'radio_select'
+                if not blueprint['value']:
+                    blueprint['value'] = u''
+                
+            # CheckboxSelectMultiple
+            if widget_name == 'CheckboxSelectMultiple':
+                blueprint['widget'] = 'checkbox_select_multiple'
+            
+            # MultiWidget
+            if widget_name == 'MultiWidget':
+                blueprint['widget'] = 'multi'
+            
+            # SplitDateTimeWidget
+            if widget_name == 'SplitDateTimeWidget':
+                blueprint['widget'] = 'split_datetime'
+            
+            # SplitHiddenDateTimeWidget
+            if widget_name == 'SplitHiddenDateTimeWidget':
+                blueprint['widget'] = 'split_hidden_datetime'
+            
+            # SelectDateWidget
+            if widget_name == 'SelectDateWidget':
+                blueprint['widget'] = 'select_date'
+                blueprint['years'] = widget.years
+                
+            # Store field in either of collections
+            if blueprint['hidden']:
+                blueprint['attrs']['type'] = 'hidden'
+                self.hidden.append(blueprint)
+            else:
+                self.fields[field] = blueprint
+                                
+    def _get_widget(self, name, field):
+        if name in self.meta_widgets:
+            return self.meta_widgets[name]
+        return field.widget
+    
+    
+class FormFieldsets(object):
+    """
+    Hydrator that builds fieldset from form and blueprint
+    """
+    def __init__(self, form, fields, fieldsets=None):
+        self.fieldsets = []
+        
+        # Use form layout
+        if not fieldsets:
+            try:
+                fieldsets = form.layout
+            except AttributeError:
+                pass
+        
+        # Build fieldsets data
+        if fieldsets:
+            for blueprint in fieldsets:
+                fieldset = {'legend': None, 'fields': [], 'help': None, 'last': False}
+                fieldset['legend'] = blueprint[0]
+                row_width = 0
+                for field in blueprint[1]:
+                    try:
+                        if isinstance(field, basestring):
+                            fieldset['fields'].append(fields[field])
+                        elif field[0] == 'nested':
+                            subfields = {'label': None, 'help_text': None, 'nested': [], 'errors':[], 'endrow': False, 'last': False, 'width': 100}
+                            subfiels_ids = []
+                            try:
+                                subfields = field[2].update(subfields)
+                            except IndexError:
+                                pass
+                            for subfield in field[1]:
+                                if isinstance(subfield, basestring):
+                                    subfiels_ids.append(subfield)
+                                    subfields['nested'].append(fields[subfield])
+                                    for error in fields[subfield]['errors']:
+                                        if not error in subfields['errors']:
+                                            subfields['errors'].append(error)
+                                else:
+                                    subfiels_ids.append(subfield[0])
+                                    try:
+                                        subfield[1]['attrs'] = dict(fields[subfield[0]]['attrs'], **subfield[1]['attrs'])
+                                    except KeyError:
+                                        pass
+                                    subfields['nested'].append(dict(fields[subfield[0]], **subfield[1]))
+                                    for error in fields[subfield[0]]['errors']:
+                                        if not error in subfields['errors']:
+                                            subfields['errors'].append(error)
+                            if not subfields['label']:
+                                subfields['label'] = subfields['nested'][0]['label']
+                            if not subfields['help_text']:
+                                subfields['help_text'] = subfields['nested'][0]['help_text']
+                            try:
+                                subfields['errors'] = form.errors["_".join(subfiels_ids)] 
+                            except KeyError:
+                                pass
+                            fieldset['fields'].append(subfields)
+                        else:
+                            try:
+                                field[1]['attrs'] = dict(fields[field[0]]['attrs'], **field[1]['attrs'])
+                            except KeyError:
+                                pass
+                            fieldset['fields'].append(dict(fields[field[0]], **field[1]))
+                        row_width += fieldset['fields'][-1]['width']
+                        if row_width >= 100:
+                            fieldset['fields'][-1]['endrow'] = True
+                            row_width = 0
+                    except (AttributeError, IndexError, KeyError):
+                        pass
+                if fieldset['fields']:
+                    fieldset['fields'][-1]['endrow'] = True
+                    fieldset['fields'][-1]['last'] = True
+                try:
+                    fieldset['help'] = blueprint[2]
+                except IndexError:
+                    pass
+                
+                # Append complete fieldset
+                if fieldset['fields']:
+                    self.fieldsets.append(fieldset)
+            self.fieldsets[-1]['last'] = True
+        
+        

+ 0 - 0
misago/forums/__init__.py


+ 37 - 0
misago/forums/admin/__init__.py

@@ -0,0 +1,37 @@
+from django.conf.urls import patterns, include, url
+from django.utils.translation import ugettext_lazy as _
+from misago.admin import AdminSection, AdminAction
+
+ADMIN_SECTIONS=(
+    AdminSection(
+                 id='forums',
+                 name=_("Forums"),
+                 icon='comment',
+                 after='users',
+                 ),
+)
+
+ADMIN_ACTIONS=(
+   AdminAction(
+               section='forums',
+               id='forums',
+               name=_("Forums List"),
+               help=_("Create, edit and delete forums."),
+               icon='comment',
+               route='admin_forums',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_forums'),
+                    ),
+               ),
+   AdminAction(
+               section='forums',
+               id='attachments',
+               name=_("Attachments"),
+               help=_("Manage allowed attachment types."),
+               icon='download-alt',
+               route='admin_forums_attachments',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_forums_attachments'),
+                    ),
+               ),
+)

+ 4 - 0
misago/forums/admin/views.py

@@ -0,0 +1,4 @@
+from django.http import HttpResponse
+
+def forums_list(request):
+    return HttpResponse("UNIMPLEMENTED ADMIN ACTION!")

+ 10 - 0
misago/forums/fixtures.py

@@ -0,0 +1,10 @@
+from misago.monitor.fixtures import load_monitor_fixture
+
+monitor_fixtures = {
+                  'threads': 0,
+                  'posts': 0,
+                  }
+
+
+def load_fixture():
+    load_monitor_fixture(monitor_fixtures)

+ 167 - 0
misago/forums/models.py

@@ -0,0 +1,167 @@
+from django.conf import settings
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from mptt.models import MPTTModel, TreeForeignKey
+
+class Forum(MPTTModel):
+    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
+    position = models.IntegerField()   
+    role = models.CharField(max_length=12, choices=(
+        ('cat', 'Category'),
+        ('for', 'Forum'),
+        ('red', 'Redirect')
+    ))
+    special = models.CharField(max_length=255,null=True, blank=True)
+    name = models.CharField(max_length=255)
+    slug = models.SlugField(max_length=255)
+    style = models.CharField(max_length=255)
+    description = models.TextField(null=True, blank=True)
+    description_preparsed = models.TextField(null=True, blank=True)
+    threads = models.PositiveIntegerField(default=0)
+    posts = models.PositiveIntegerField(default=0)
+    last_thread = models.ForeignKey('Thread', related_name='+', null=True, blank=True)
+    last_thread_name = models.CharField(max_length=255, null=True, blank=True)
+    last_thread_slug = models.SlugField(null=True, blank=True)
+    last_thread_date = models.DateTimeField(null=True, blank=True)
+    last_poster = models.ForeignKey('users.User', related_name='+')
+    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
+    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
+    closed = models.BooleanField(default=False)
+    
+    class MPTTMeta:
+            order_insertion_by = ['position']
+
+
+class ThreadManager(models.Manager):
+    def filter_overview(self, start, end):
+        return self.filter(start__gte=start).filter(start__lte=end)
+
+
+class Thread(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    name = models.CharField(max_length=255)
+    slug = models.SlugField(max_length=255)
+    replies = models.PositiveIntegerField()
+    views = models.PositiveIntegerField(default=0)
+    start = models.DateTimeField(default=0)
+    start_post = models.ForeignKey('Post', related_name='+', null=True, blank=True)
+    start_poster = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
+    start_poster_name = models.CharField(max_length=255)
+    start_poster_slug = models.SlugField(max_length=255)
+    start_poster_style = models.CharField(max_length=255)
+    last = models.DateTimeField()
+    last_post = models.ForeignKey('Post', related_name='+', null=True, blank=True)
+    last_poster = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
+    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
+    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
+    poster_styles_list = models.CharField(max_length=255, null=True, blank=True)
+    hidden = models.BooleanField(default=False)
+    closed = models.BooleanField(default=False)
+    
+    objects = ThreadManager()
+    
+    statistics_name = _('New Threads')
+        
+    def get_date(self):
+        return self.start
+
+
+class PostManager(models.Manager):
+    def filter_overview(self, start, end):
+        return self.filter(date__gte=start).filter(date__lte=end)
+    
+
+class Post(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    thread = models.ForeignKey(Thread, related_name='+')
+    user = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
+    user_name = models.CharField(max_length=255)
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    post = models.TextField()
+    post_preparsed = models.TextField()
+    upvotes = models.IntegerField(default=0)
+    downvotes = models.IntegerField(default=0)
+    date = models.DateTimeField()
+    attachments = models.BooleanField(default=False)
+    attachments_list = models.CommaSeparatedIntegerField(max_length=255)
+    edited = models.BooleanField(default=False)
+    edits = models.PositiveIntegerField(default=0)
+    edit_date = models.DateTimeField(null=True, blank=True)
+    edit_reason = models.CharField(max_length=255, null=True, blank=True)
+    edit_user = models.ForeignKey('users.User', related_name='+', null=True)
+    edit_user_name = models.CharField(max_length=255, null=True, blank=True)
+    edit_user_slug = models.SlugField(max_length=255, null=True, blank=True)
+    reported = models.BooleanField(default=False)
+    hidden = models.BooleanField(default=False)
+    protected = models.BooleanField(default=False)
+    
+    objects = PostManager()
+    
+    statistics_name = _('New Posts')
+    
+    def get_date(self):
+        return self.date
+
+
+class AttachmentType(models.Model):
+    mime = models.CharField(max_length=255)
+    extension = models.CharField(max_length=255)
+    
+        
+class Attachment(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    thread = models.ForeignKey(Thread, related_name='+')
+    post = models.ForeignKey(Post, related_name='+')
+    type = models.ForeignKey(AttachmentType, related_name='+')
+    user = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
+    created = models.DateTimeField()
+    size = models.PositiveIntegerField(default=0)
+    name = models.CharField(max_length=255)
+    file = models.FileField(upload_to=settings.MEDIA_ROOT + '/attachments/%m_%Y/',max_length=255)
+    downloads = models.PositiveIntegerField(default=0)
+    
+    
+class Poll(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    thread = models.ForeignKey(Thread, related_name='+')
+    name = models.CharField(max_length=255)
+    name_slug = models.SlugField(max_length=255)
+    user = models.ForeignKey('users.User', related_name='+')
+    user_name = models.CharField(max_length=255)
+    user_slug = models.SlugField(max_length=255)
+    public = models.BooleanField(default=False)
+    multiple = models.BooleanField(default=False)
+    changing = models.BooleanField(default=False)
+    created = models.DateTimeField()
+    length = models.PositiveIntegerField(default=0)
+    votes = models.PositiveIntegerField(default=0)
+    
+    
+class Vote(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    thread = models.ForeignKey(Thread, related_name='+')
+    poll = models.ForeignKey(Poll, related_name='+')
+    user = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
+    ip = models.GenericIPAddressField()
+    option = models.PositiveIntegerField()
+    
+   
+class Moderator(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    group = models.ForeignKey('users.Group', related_name='+', null=True, blank=True)
+    user = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
+    
+    
+class Report(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    thread = models.ForeignKey(Thread, related_name='+')
+    post = models.ForeignKey(Post, related_name='+')
+    
+    
+class Edit(models.Model):
+    forum = models.ForeignKey(Forum, related_name='+')
+    thread = models.ForeignKey(Thread, related_name='+')
+    post = models.ForeignKey(Post, related_name='+')    

+ 1 - 0
misago/forums/views.py

@@ -0,0 +1 @@
+# Create your views here.

+ 37 - 0
misago/messages/__init__.py

@@ -0,0 +1,37 @@
+from coffin.template.loader import select_template
+
+class Message(object):
+    """
+    Template based mesage used by frontend
+    """
+    def __init__(self, request, type='base', message=None, extra={}, owner=None):
+        self.type = type
+        self.message = message
+        self.owner = owner
+        for key, value in extra.iteritems():
+            setattr(self, key, value)
+        self.tpl = select_template((
+                                    '%s/message/%s.html' % (request.theme.get_theme(), type),
+                                    '_message/%s.html' % type,
+                                    '%s/message/base.html' % request.theme.get_theme(),
+                                    '_message/base.html'
+                                    ))
+        self.tpl = self.tpl.name
+        if self.tpl[9:-5] == 'base':
+            self.message = type
+            
+    def is_basic(self):
+        return False
+
+
+class BasicMessage(object):
+    """
+    Text based mesage used by ACP
+    """
+    def __init__(self, message=None, type='info', owner=None):
+        self.type = type
+        self.message = message
+        self.owner = owner
+            
+    def is_basic(self):
+        return True

+ 4 - 0
misago/messages/context_processors.py

@@ -0,0 +1,4 @@
+def messages(request):
+    return {
+        'messages' : request.messages.messages,
+    }

+ 21 - 0
misago/messages/messages.py

@@ -0,0 +1,21 @@
+class Messages(object):
+    def __init__(self, session):
+        self.session = session
+        self.messages = session.get('messages_list', [])
+        self.session['messages_list'] = []
+        
+    def set_message(self, message, type='info', owner=None):
+        message.type = type
+        message.owner = owner
+        self.messages.append(message)
+    
+    def set_flash(self, message, type='info', owner=None):
+        self.set_message(message, type, owner)
+        self.session['messages_list'].append(message)
+        
+    def get_message(self, owner=None):
+        for index, message in enumerate(self.messages):
+            if message.owner == owner:
+                del self.messages[index]
+                return message
+        return None

+ 5 - 0
misago/messages/middleware.py

@@ -0,0 +1,5 @@
+from misago.messages.messages import Messages
+
+class MessagesMiddleware(object):
+    def process_request(self, request):
+        request.messages = Messages(request.session)

+ 0 - 0
misago/monitor/__init__.py


+ 4 - 0
misago/monitor/context_processors.py

@@ -0,0 +1,4 @@
+def monitor(request):
+    return {
+        'monitor' : request.monitor,
+    }

+ 11 - 0
misago/monitor/fixtures.py

@@ -0,0 +1,11 @@
+from django.utils import timezone
+from misago.monitor.models import Item
+    
+def load_monitor_fixture(fixture):
+    for id in fixture.keys():
+        item = Item(
+                    id=id,
+                    value=fixture[id],
+                    updated=timezone.now()
+                    )
+        item.save(force_insert=True)

+ 5 - 0
misago/monitor/middleware.py

@@ -0,0 +1,5 @@
+from misago.monitor.monitor import Monitor
+
+class MonitorMiddleware(object):
+    def process_request(self, request):
+        request.monitor = Monitor()

+ 6 - 0
misago/monitor/models.py

@@ -0,0 +1,6 @@
+from django.db import models
+
+class Item(models.Model):
+    id = models.CharField(max_length=255, primary_key=True)
+    value = models.TextField(blank=True, null=True)
+    updated = models.DateTimeField(blank=True, null=True)

+ 69 - 0
misago/monitor/monitor.py

@@ -0,0 +1,69 @@
+from django.db.utils import DatabaseError
+from django.core.cache import cache
+from django.utils import timezone
+from misago.monitor.models import Item
+
+class Monitor(object):
+    def __init__(self):
+        self._cache_deleted = False
+        self._items = {}
+        self.refresh()
+            
+    def refresh(self):
+        self._items = cache.get('misago.monitor')
+        if not self._items:
+            self._items = {}
+            try:
+                for i in Item.objects.all():
+                    self._items[i.id] = [i.value, i.updated]
+                cache.set('misago.monitor', self._items)
+            except DatabaseError:
+                pass
+
+    def __contains__(self, key):
+        return key in self._items
+
+    def __getitem__(self, key):
+        return self._items[key][0]
+
+    def __setitem__(self, key, value):
+        self._items[key][0] = value
+        cache.set('misago.monitor', self._items)
+        sync_item = Item(id=key, value=value, updated=timezone.now())
+        sync_item.save()
+        return value
+        
+    def __delitem__(self, key):
+        pass
+        
+    def get(self, key, default=None):
+        if not key in self._items:
+            return default
+        return self._items[key][0]
+    
+    def get_updated(self, key):
+        if key in self._items:
+            return self._items[key][1]
+        return None
+        
+    def has_key(self, key):
+        return key in self._items
+
+    def keys(self):
+        return self._items.keys()
+
+    def values(self):
+        return self._items.values()
+
+    def items(self):
+        return self._items.items()
+
+    def iterkeys(self):
+        return self._items.iterkeys()
+
+    def itervalues(self):
+        return self._items.itervalues()
+
+    def iteritems(self):
+        return self._items.iteritems()
+

+ 0 - 0
misago/overview/__init__.py


+ 59 - 0
misago/overview/admin/__init__.py

@@ -0,0 +1,59 @@
+from django.conf.urls import patterns, include, url
+from django.utils.translation import ugettext_lazy as _
+from misago.admin import AdminSection, AdminAction
+
+ADMIN_SECTIONS=(
+    AdminSection(
+                 id='overview',
+                 name=_("Overview"),
+                 icon='signal',
+                 ),
+)
+
+ADMIN_ACTIONS=(
+   AdminAction(
+               section='overview',
+               id='home',
+               name=_("Home"),
+               help=_("Your forums right now"),
+               icon='home',
+               route='admin_overview_home',
+               urlpatterns=patterns('misago.overview.admin.views',
+                        url(r'^$', 'overview_home', name='admin_overview_home'),
+                    ),
+               ),
+   AdminAction(
+               section='overview',
+               id='stats',
+               name=_("Stats"),
+               help=_("Create Statistics Reports"),
+               icon='signal',
+               route='admin_overview_stats',
+               urlpatterns=patterns('misago.overview.admin.views',
+                        url(r'^$', 'overview_stats', name='admin_overview_stats'),
+                        url(r'^(?P<model>[a-zA-Z0-9]+)/(?P<date_start>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])/(?P<date_end>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])/(?P<precision>\w+)$', 'overview_graph', name='admin_overview_graph'),
+                    ),
+               ),
+   AdminAction(
+               section='overview',
+               id='online',
+               name=_("Online"),
+               help=_("See who is currently online on forums."),
+               icon='fire',
+               route='admin_overview_online',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_overview_online'),
+                    ),
+               ),
+   AdminAction(
+               section='overview',
+               id='staff',
+               name=_("Forum Team"),
+               help=_("List of all forum team members"),
+               icon='user',
+               route='admin_overview_staff',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_overview_staff'),
+                    ),
+               ),
+)

+ 28 - 0
misago/overview/admin/forms.py

@@ -0,0 +1,28 @@
+from datetime import timedelta
+from django import forms
+from django.utils import timezone as tz
+from django.utils.translation import ugettext_lazy as _
+from misago.forms import Form
+
+class GenerateStatisticsForm(Form):
+    provider_model = forms.ChoiceField()
+    date_start = forms.DateField(initial=tz.now() - timedelta(days=7))
+    date_end = forms.DateField(initial=tz.now())
+    stats_precision = forms.ChoiceField(choices=(('day', _('For each day')), ('week', _('For each week')), ('month', _('For each month')), ('year', _('For each year'))))
+    
+    layout = (
+              (None, (
+                        ('provider_model', {'label': _('Report Type'), 'help_text': _('Select statistics provider.')}),
+                        ('nested', (
+                            ('date_start', {'label': _('Time'), 'help_text': _('Enter start and end date for time period you want to take data from to use in graph.'), 'attrs': {'placeholder': _('Start Date: YYYY-MM-DD')}, 'width': 50}),
+                            ('date_end', {'attrs': {'placeholder': _('End Date: YYYY-MM-DD')}, 'width': 50}),
+                        )),
+                        ('stats_precision', {'label': _('Graph Precision')}),
+                      )),
+              )
+    
+    def __init__(self, *args, **kwargs):
+        provider_choices = kwargs.get('provider_choices')
+        del kwargs['provider_choices']
+        super(GenerateStatisticsForm, self).__init__(*args, **kwargs)
+        self.fields['provider_model'] = forms.ChoiceField(choices=provider_choices)

+ 195 - 0
misago/overview/admin/views.py

@@ -0,0 +1,195 @@
+from datetime import datetime, timedelta
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.db import models
+from django.http import Http404
+from django.shortcuts import redirect
+from django.template import RequestContext
+from django.utils import formats, timezone
+from django.utils.translation import ugettext as _
+import math
+from misago.forms import FormLayout
+from misago.forums.models import Thread, Post
+from misago.messages import Message, BasicMessage
+from misago.overview.admin.forms import GenerateStatisticsForm
+from misago.sessions.models import Session
+from misago.users.models import User
+
+def overview_home(request):
+    return request.theme.render_to_response('overview/home.html', {
+        'users': request.monitor['users'],
+        'users_inactive': request.monitor['users_inactive'],
+        'threads': request.monitor['threads'],
+        'posts': request.monitor['posts'],
+        'admins': Session.objects.filter(user__isnull=False).filter(admin=1).order_by('user__username_slug').select_related(depth=1),
+        }, context_instance=RequestContext(request));
+
+
+def overview_stats(request):
+    """
+    Allow admins to generate fancy statistic graphs for different models
+    """
+    statistics_providers = []
+    models_map = {}
+    for model in models.get_models():
+        try:
+            getattr(model.objects, 'filter_overview')
+            statistics_providers.append((str(model.__name__).lower(), model.statistics_name))
+            models_map[str(model.__name__).lower()] = model
+        except AttributeError:
+            pass
+
+    if not statistics_providers:
+        """
+        Something went FUBAR - Misago ships with some stats providers out of box
+        If those providers cant be found, this means Misago filesystem is corrupted
+        """
+        return request.theme.render_to_response('overview/stats/not_available.html',
+                                                context_instance=RequestContext(request));
+    
+    message = None
+    if request.method == 'POST':
+        form = GenerateStatisticsForm(request.POST, provider_choices=statistics_providers, request=request)
+        if form.is_valid():
+            date_start = form.cleaned_data['date_start']
+            date_end = form.cleaned_data['date_end']
+            if date_start > date_end:
+                # Reverse dates if start is after end
+                date_temp = date_end
+                date_end = date_start
+                date_start = date_temp
+            # Assert that dates are correct
+            if date_end == date_start:
+                message = BasicMessage(_('Start and end date are same'), type='error')
+            elif check_dates(date_start, date_end, form.cleaned_data['stats_precision']):
+                message = check_dates(date_start, date_end, form.cleaned_data['stats_precision'])
+            else:
+                request.messages.set_flash(BasicMessage(_('Statistical report has been created.')), 'success', 'admin_stats')
+                return redirect(reverse('admin_overview_graph', kwargs={
+                                                       'model': form.cleaned_data['provider_model'],
+                                                       'date_start': date_start.strftime('%Y-%m-%d'),
+                                                       'date_end': date_end.strftime('%Y-%m-%d'),
+                                                       'precision': form.cleaned_data['stats_precision']
+                                                        }))
+        else:
+            message = Message(request, form.non_field_errors()[0])
+            message.type = 'error'
+    else:
+        form = GenerateStatisticsForm(provider_choices=statistics_providers, request=request)
+    
+    return request.theme.render_to_response('overview/stats/form.html', {
+                                            'form': FormLayout(form),
+                                            'message': message,
+                                            }, context_instance=RequestContext(request));
+
+
+def overview_graph(request, model, date_start, date_end, precision):
+    """
+    Generate fancy graph for model and stuff
+    """
+    if date_start == date_end:
+        # Bad dates
+        raise Http404()
+    
+    # Turn stuff into datetime's
+    date_start = datetime.strptime(date_start, '%Y-%m-%d')
+    date_end = datetime.strptime(date_end, '%Y-%m-%d')
+    
+    
+    statistics_providers = []
+    models_map = {}
+    for model_obj in models.get_models():
+        try:
+            getattr(model_obj.objects, 'filter_overview')
+            statistics_providers.append((str(model_obj.__name__).lower(), model_obj.statistics_name))
+            models_map[str(model_obj.__name__).lower()] = model_obj
+        except AttributeError:
+            pass
+
+    if not statistics_providers:
+        # Like before, q.q on lack of models
+        return request.theme.render_to_response('overview/stats/not_available.html',
+                                                context_instance=RequestContext(request));
+    
+    if not model in models_map or check_dates(date_start, date_end, precision):
+        # Bad model name or graph data!
+        raise Http404()
+    
+    form = GenerateStatisticsForm(
+                                  provider_choices=statistics_providers,
+                                  request=request,
+                                  initial={'provider_model': model, 'date_start': date_start, 'date_end': date_end, 'stats_precision': precision})
+    return request.theme.render_to_response('overview/stats/graph.html', {
+                                            'title': models_map[model].statistics_name,
+                                            'graph': build_graph(models_map[model], date_start, date_end, precision),
+                                            'form': FormLayout(form),
+                                            'message': request.messages.get_message('admin_stats'),
+                                            }, context_instance=RequestContext(request));
+
+
+def check_dates(date_start, date_end, precision):
+    date_diff = date_end - date_start
+    date_diff = date_diff.seconds + date_diff.days * 86400
+    
+    if ((precision == 'day' and date_diff / 86400 > 60)
+        or (precision == 'week' and date_diff / 604800 > 60)
+        or (precision == 'month' and date_diff / 2592000 > 60)
+        or (precision == 'year' and date_diff / 31536000 > 60)):
+        return BasicMessage(_('Too many many items to display on graph.'), type='error')
+    elif ((precision == 'day' and date_diff / 86400 < 1)
+          or (precision == 'week' and date_diff / 604800 < 1)
+          or (precision == 'month' and date_diff / 2592000 < 1)
+          or (precision == 'year' and date_diff / 31536000 < 1)):
+        return BasicMessage(_('Too few items to display on graph'), type='error')
+    return None
+
+
+def overview_forums(request, mode=None):
+    return request.theme.render_to_response('overview/forums.html', {                                        
+        'graph_posts': build_stat(Post, mode),                                            
+        'graph_threads': build_stat(Thread, mode),
+        'posts': request.monitor['posts'],
+        'threads': request.monitor['threads'],
+        'mode': mode,
+        }, context_instance=RequestContext(request));
+       
+        
+def build_graph(model, date_start, date_end, precision):
+    if precision == 'day':
+        format = 'F j, Y'
+        step = 86400
+    if precision == 'week':
+        format = 'W, Y'
+        step = 604800
+    if precision == 'month':
+        format = 'F, Y'
+        step = 2592000
+    if precision == 'year':
+        format = 'Y'
+        step = 31536000
+    
+    date_end = timezone.make_aware(date_end, timezone.get_current_timezone())
+    date_start = timezone.make_aware(date_start, timezone.get_current_timezone())
+    
+    date_diff = date_end - date_start
+    date_diff = date_diff.seconds + date_diff.days * 86400
+    steps = int(math.ceil(float(date_diff / step))) + 1
+    timeline = [0 for i in range(0, steps)]
+    for i in range(0, steps):
+        step_date = date_end - timedelta(seconds=(i * step));
+        timeline[steps - i - 1] = step_date    
+    stat = {'total': 0, 'max': 0, 'stat': [0 for i in range(0, steps)], 'timeline': timeline, 'start': date_start, 'end': date_end, 'format': format}
+        
+    # Loop model items
+    for item in model.objects.filter_overview(date_start, date_end).iterator():
+        date_diff = date_end - item.get_date()
+        date_diff = date_diff.seconds + date_diff.days * 86400
+        date_diff = steps - int(math.floor(float(date_diff / step))) - 2
+        stat['stat'][date_diff] += 1
+        stat['total'] += 1
+        
+    # Find max
+    for i in stat['stat']:
+        if i > stat['max']:
+            stat['max'] = i
+    return stat

+ 85 - 0
misago/search/__init__.py

@@ -0,0 +1,85 @@
+from django.utils.translation import ugettext_lazy as _
+
+class SearchException(Exception):
+    def __init__(self, message):
+        self.message = message
+
+
+class SearchQuery(object):
+    def __init__(self, raw_query=None):
+        """
+        Build search query object
+        """
+        if raw_query:
+            self.parse_query(raw_query)
+        
+    def parse_query(self, raw_query):
+        """
+        Parse raw search query into dict of lists of words that should be found and cant be found in string
+        """
+        self.criteria = {'+': [], '-': []}
+        for word in unicode(raw_query).split():
+            # Trim word and skip it if its empty
+            word = unicode(word).strip().lower()
+            if len(word) == 0:
+                pass
+            
+            # Find word mode
+            mode = '+'
+            if word[0] == '-':
+                mode = '-'
+                word = unicode(word[1:]).strip()
+                
+            # Strip extra crap
+            word = ''.join(e for e in word if e.isalnum())
+            
+            # Slice word?
+            if len(word) <= 3:
+                raise SearchException(_("One or more search phrases are shorter than four characters."))
+            if mode == '+':
+                if len(word) == 5:
+                    word = word[0:-1]
+                if len(word) == 6:
+                    word = word[0:-2]
+                if len(word) > 6:
+                    word = word[0:-3]
+            self.criteria[mode].append(word)
+            
+        # Complain that there are no positive matches
+        if not self.criteria['+'] and not self.criteria['-']:
+            raise SearchException(_("Search query is invalid."))
+    
+    def search(self, value):
+        """
+        See if value meets search criteria, return True for success and False otherwhise
+        """
+        try:
+            value = unicode(value).strip().lower()
+            # Search for only
+            if self.criteria['+'] and not self.criteria['-']:
+               return self.search_for(value)
+            # Search against only
+            if self.criteria['-'] and not self.criteria['+']:
+               return self.search_against(value)
+            # Search if contains for values but not against values
+            return self.search_for(value) and not self.search_against(value)
+        except AttributeError:
+            raise SearchException(_("You have to define search query before you will be able to search."))
+        
+    def search_for(self, value):
+        """
+        See if value is required
+        """
+        for word in self.criteria['+']:
+            if value.find(word) != -1:
+                return True
+        return False
+        
+    def search_against(self, value):
+        """
+        See if value is forbidden
+        """
+        for word in self.criteria['-']:
+            if value.find(word) != -1:
+                return True
+        return False

+ 4 - 0
misago/security/__init__.py

@@ -0,0 +1,4 @@
+from django.utils import crypto
+    
+def get_random_string(length):
+    return crypto.get_random_string(length, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")

+ 51 - 0
misago/security/admin/__init__.py

@@ -0,0 +1,51 @@
+from django.conf.urls import patterns, include, url
+from django.utils.translation import ugettext_lazy as _
+from misago.admin import AdminAction
+from misago.security.models import QATest
+
+ADMIN_ACTIONS=(
+   AdminAction(
+               section='system',
+               id='qa',
+               name=_("Q&A Tests"),
+               help=_("Question & Answer Tests"),
+               icon='question-sign',
+               model=QATest,
+               actions=[
+                        {
+                         'id': 'list',
+                         'icon': 'list-alt',
+                         'name': _("Browse Tests"),
+                         'help': _("Browse all Question & Answer Tests"),
+                         'route': 'admin_qa'
+                         },
+                        {
+                         'id': 'new',
+                         'icon': 'plus',
+                         'name': _("New Test"),
+                         'help': _("Crete new Q&A Test"),
+                         'route': 'admin_qa_new'
+                         },
+                        ],
+               route='admin_qa',
+               urlpatterns=patterns('misago.security.admin.qatest.views',
+                        url(r'^$', 'List', name='admin_qa'),
+                        url(r'^(?P<page>\d+)/$', 'List', name='admin_qa'),
+                        url(r'^new/$', 'New', name='admin_qa_new'),
+                        url(r'^edit/(?P<slug>([a-zA-Z0-9]|-)+)\.(?P<target>\d+)/$', 'Edit', name='admin_qa_edit'),
+                        url(r'^delete/(?P<slug>([a-zA-Z0-9]|-)+)\.(?P<target>\d+)/$', 'Delete', name='admin_qa_delete'),
+                    ),
+               after='settings',
+               ),
+   AdminAction(
+               section='system',
+               id='api',
+               name=_("API Keys"),
+               help=_("Connect other apps with your forums"),
+               icon='barcode',
+               route='admin_api',
+               urlpatterns=patterns('misago.security.admin.views',
+                        url(r'^$', 'api_list', name='admin_api'),
+                    ),
+               ),
+)

+ 0 - 0
misago/security/admin/qatest/__init__.py


+ 26 - 0
misago/security/admin/qatest/forms.py

@@ -0,0 +1,26 @@
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+from misago.forms import Form
+
+class QATestForm(Form):
+    """
+    New/Edit QA Test form
+    """
+    question = forms.CharField(max_length=255)
+    helptext = forms.CharField(max_length=255, required=False)
+    answers = forms.CharField(widget=forms.Textarea)
+    layout = (
+               (
+                 _("Question and Help"),
+                 (
+                  ('question', {'label': _("Test Question"), 'help_text': _("Question that is displayed to user.")}),
+                  ('helptext', {'label': _("Test Help"), 'help_text': _("Optional help text that is displayed next to question.")}),
+                 ),
+                ),
+                (
+                 _("Answers"),
+                 (
+                  ('answers', {'label': _("Test Answers"), 'help_text': _("Enter accepted answers to this question. Every answer should be entered in new line. Answers are case-insensitive.")}),
+                 ),
+                ),
+               )

+ 0 - 0
misago/security/admin/qatest/helpers.py


+ 124 - 0
misago/security/admin/qatest/views.py

@@ -0,0 +1,124 @@
+from django.core.urlresolvers import reverse as django_reverse
+from django.utils.translation import ugettext as _
+from misago.admin import site
+from misago.admin.widgets import *
+from misago.utils import slugify
+from misago.security.admin.qatest.forms import QATestForm
+from misago.security.models import QATest
+
+def reverse(route, target=None):
+    if target:
+        return django_reverse(route, kwargs={'target': target.pk, 'slug': slugify(target.question)})
+    return django_reverse(route)
+
+"""
+Views
+"""
+class List(ListWidget):
+    """
+    List QA Tests
+    """
+    admin = site.get_action('qa')
+    id = 'list'
+    columns=(
+             ('question', _("Question"), 50),
+             ('helptext', _("Help")),
+             )
+    default_sorting = 'question'
+    sortables={
+               'question': 1,
+               'helptext': 1,
+              }
+    filters = ['question', 'helptext']
+    filters_layout = []
+    empty_message = _('No Question and Answer tests have been found. Change search criteria and try again.')
+    nothing_checked_message = _('You have to check at least one test.')
+    actions=(
+             ('delete', _("Delete selected"), _("Are you sure you want to delete selected questions?")),
+             )
+    
+    def get_item_actions(self, request, item):
+        return (
+                self.action('pencil', _("Edit Test"), reverse('admin_qa_edit', item)),
+                self.action('remove', _("Delete Test"), reverse('admin_qa_delete', item), post=True, prompt=_("Are you sure you want to delete this test?")),
+                )
+
+    def action_delete(self, request, items, checked):
+        QATest.objects.filter(id__in=checked).delete()
+        return BasicMessage(_('Selected Q&A Tests have been deleted successfully.'), 'success'), reverse('admin_qa')
+    
+
+class New(FormWidget):
+    """
+    Create New QA Test
+    """
+    admin = site.get_action('qa')
+    id = 'new'
+    fallback = 'admin_qa' 
+    form = QATestForm
+    submit_button = _("Save Test")
+        
+    def get_new_url(self, request, model):
+        return reverse('admin_qa_new')
+    
+    def get_edit_url(self, request, model):
+        return reverse('admin_qa_edit', model)
+    
+    def submit_form(self, request, form, target):
+        new_test = QATest(
+                          question=form.cleaned_data['question'],
+                          helptext=form.cleaned_data['helptext'],
+                          answers=form.cleaned_data['answers'],
+                          )
+        new_test.save(force_insert=True)
+        return new_test, BasicMessage(_('New Q&A Test "%(name)s" has been saved.' % {'name': form.cleaned_data['question']}), 'success')
+    
+   
+class Edit(FormWidget):
+    """
+    Edit QA Test
+    """
+    admin = site.get_action('qa')
+    id = 'edit'
+    name = _("Edit QA Test")
+    fallback = 'admin_qa'
+    form = QATestForm
+    target_name = 'question'
+    notfound_message = _('Requested Question and Answer test could not be found.')
+    submit_fallback = True
+    
+    def get_url(self, request, model):
+        return reverse('admin_qa_edit', model)
+    
+    def get_edit_url(self, request, model):
+        return self.get_url(request, model)
+    
+    def get_initial_data(self, request, model):
+        return {
+                'question': model.question,
+                'helptext': model.helptext,
+                'answers': model.answers,
+                }
+    
+    def submit_form(self, request, form, target):
+        old_question = target.question
+        target.question = form.cleaned_data['question']
+        target.helptext = form.cleaned_data['helptext']
+        target.answers = form.cleaned_data['answers']
+        target.save(force_update=True)
+        return target, BasicMessage(_('Changes in Q&A Test "%(name)s" have been saved.' % {'name': old_question}), 'success')
+
+
+class Delete(ButtonWidget):
+    """
+    Delete QA Test
+    """
+    admin = site.get_action('qa')
+    id = 'delete'
+    fallback = 'admin_qa'
+    notfound_message = _('Requested Question and Answer test could not be found.')
+    
+    def action(self, request, target):
+        target.delete()
+        return BasicMessage(_('Q&A Test "%(name)s" has been deleted.' % {'name': target.question}), 'success'), False
+        

+ 119 - 0
misago/security/auth.py

@@ -0,0 +1,119 @@
+from datetime import timedelta
+from django.conf import settings
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+from misago.banning.models import check_ban
+from misago.security.models import SignInAttempt
+from misago.sessions.models import Token
+from misago.users.models import User
+    
+"""
+Exception constants
+"""
+CREDENTIALS = 'security/bad_credentials'
+ACTIVATION_USER = 'users/activation_user'
+ACTIVATION_ADMIN = 'users/activation_admin'
+BANNED = 'banning/banned_user'
+NOT_ADMIN = 'security/not_admin'
+
+
+class AuthException(Exception):
+    """
+    Auth Exception is thrown when auth_* method finds problem with allowing user to sign-in
+    """
+    def __init__(self, type=None, error=None, user=None):
+        self.type = type
+        self.error = error
+        self.user = user
+        
+    def __str__(self):
+        return self.error
+      
+    
+def get_user(email, password, admin=False):
+    """
+    Fetch user from DB using email/pass pair, scream if either of data is incorrect
+    """
+    try:
+        user = User.objects.get_by_email(email)
+        if not user.check_password(password):
+            raise AuthException(CREDENTIALS, user)
+        if not admin:
+            if user.activation == User.ACTIVATION_ADMIN:
+                # Only admin can activate your account.
+                raise AuthException(ACTIVATION_ADMIN, user)
+            if user.activation != User.ACTIVATION_NONE:
+                # Only admin can activate your account.
+                raise AuthException(ACTIVATION_USER, user)
+        
+    except User.DoesNotExist:
+        raise AuthException(CREDENTIALS)
+    return user;
+
+
+def auth_forum(request, email, password):
+    """
+    Forum auth - check bans and if we are in maintenance - maintenance access
+    """
+    user = get_user(email, password)
+    if user.is_banned():
+        raise AuthException(
+                            BANNED,
+                            user.ban_reason_user
+                            )
+    return user;
+
+
+def auth_remember(request, ip):
+    """
+    Remember-me auth - check if token is valid
+    """
+    if request.firewall.admin:
+        raise AuthException()
+    if SignInAttempt.objects.is_jammed(request.settings, ip):
+        raise AuthException()
+    cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
+    try:
+        cookie_token = request.COOKIES[cookie_token]
+        if len(cookie_token) != 42:
+            raise AuthException()
+        try:
+            token_rk = Token.objects.select_related().get(pk=cookie_token)
+        except Token.DoesNotExist:
+            request.cookie_jar.delete('TOKEN')
+            raise AuthException()
+        # See if token is not expired
+        token_expires = timezone.now() - timedelta(days=request.settings['remember_me_lifetime'])
+        if request.settings['remember_me_extensible'] and token_rk.accessed < token_expires:
+            # Token expired because it's last use is smaller than expiration date
+            raise AuthException()
+        if not request.settings['remember_me_extensible'] and token_rk.created < token_expires:
+            # Token expired because it was created before expiration date
+            raise AuthException()
+        # Update token date
+        token_rk.accessed = timezone.now()
+        token_rk.save(force_update=True)
+        request.cookie_jar.set('TOKEN', token_rk.id, True)
+    except (AttributeError, KeyError):
+        raise AuthException()
+    return token_rk
+
+
+def auth_admin(request, email, password):
+    """
+    Admin auth - check ACP permissions
+    """
+    user = get_user(email, password, True)
+    if not user.is_admin():
+        raise AuthException(NOT_ADMIN)
+    return user;
+
+
+def sign_user_in(request, user, as_hidden=False):
+    user.set_last_visit(
+                        request.session.get_ip(request),
+                        request.META.get('HTTP_USER_AGENT', ''),
+                        as_hidden
+                        )
+    user.save(force_update=True)
+    request.session.set_user(user)

+ 30 - 0
misago/security/captcha.py

@@ -0,0 +1,30 @@
+from django.forms.fields import CharField
+from django.forms.widgets import TextInput
+from django.utils.translation import ugettext_lazy as _
+from recaptcha.client.captcha import API_SSL_SERVER, API_SERVER, VERIFY_SERVER
+
+class ReCaptchaWidget(TextInput):
+    pass
+
+
+class ReCaptchaField(CharField):
+    widget = ReCaptchaWidget # Fakey widget for FormLayout
+    api_error = None # Api error
+    def __init__(self, label=_("Verification Code"), *args, **kwargs):
+        kwargs['label'], kwargs['required'] = label, False
+        super(ReCaptchaField, self).__init__(*args, **kwargs)
+
+
+class QACaptchaField(CharField):
+    pass
+
+
+def get_captcha_dict(settings, api_error = None):
+    error_param = ''
+    if api_error:
+        error_param = '&error=%s' % api_error
+    return {
+            'api_server': API_SERVER,
+            'public_key': settings['recaptcha_public'],
+            'error_param': error_param,
+            }

+ 8 - 0
misago/security/context_processors.py

@@ -0,0 +1,8 @@
+def security(request):
+    if request.user.is_crawler():
+        return {}
+    return {
+        'csrf_id': request.csrf.csrf_id,
+        'csrf_token': request.csrf.csrf_token,
+        'is_jammed': request.jam.is_jammed(),
+    }

+ 9 - 0
misago/security/csrf.py

@@ -0,0 +1,9 @@
+from django import forms
+
+class CSRFProtection(object):
+    def __init__(self, csrf_token):
+        self.csrf_id = '_csrf_token'
+        self.csrf_token = csrf_token
+        
+    def request_secure(self, request):
+        return request.method == 'POST' and request.POST.get(self.csrf_id) == self.csrf_token

+ 38 - 0
misago/security/decorators.py

@@ -0,0 +1,38 @@
+from misago.messages import Message
+from misago.security.models import SignInAttempt
+from misago.views import error403
+
+def block_authenticated(f):
+    def decorator(*args, **kwargs):
+        request = args[0]
+        if not request.firewall.admin and request.user.is_authenticated():
+            return error403(request, Message(request, 'security/forbidden_authenticated'))
+        return f(*args, **kwargs)
+    return decorator
+
+
+def block_jammed(f):
+    def decorator(*args, **kwargs):
+        request = args[0]
+        if not request.firewall.admin and request.jam.is_jammed():
+            return error403(request, Message(request, 'security/forbidden_jammed'))
+        return f(*args, **kwargs)
+    return decorator
+
+
+def block_guest(f):
+    def decorator(*args, **kwargs):
+        request = args[0]
+        if not request.user.is_authenticated():
+            return error403(request, Message(request, 'security/forbidden_guest'))
+        return f(*args, **kwargs)
+    return decorator
+
+
+def check_csrf(f):
+    def decorator(*args, **kwargs):
+        request = args[0]
+        if not request.csrf.request_secure(request):
+            return error403(request, Message(request, 'security/forbidden_request'))
+        return f(*args, **kwargs)
+    return decorator

+ 35 - 0
misago/security/firewalls.py

@@ -0,0 +1,35 @@
+from django.conf import settings
+from misago.admin import ADMIN_PATH
+from misago.views import error403, error404
+from misago.security.views import signin
+
+class FirewallForum(object):
+    """
+    Firewall Abstraction
+    """
+    admin = False
+    prefix = ''
+    def behind_firewall(self, path):
+        """
+        Firewall test, it checks if requested path is behind firewall
+        """
+        return path[:len(self.prefix)] == self.prefix
+    
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        return None
+    
+    
+class FirewallAdmin(FirewallForum):
+    admin = True
+    prefix = '/' + ADMIN_PATH    
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        # Block all crawlers with 403
+        if request.user.is_crawler():
+            request.theme.reset_theme()
+            return error403(request)
+        else:
+            # If we are not authenticated or not admin, force us to sign in right way
+            if not request.user.is_authenticated():
+                return signin(request)
+            else:
+                return None

+ 57 - 0
misago/security/fixtures.py

@@ -0,0 +1,57 @@
+from misago.settings.fixtures import load_settings_fixture
+from misago.utils import ugettext_lazy as _
+from misago.utils import get_msgid
+
+
+settings_fixtures = (
+   # Spam Countermeasures
+   ('spam', {
+        'name': _("Spam Countermeasures"),
+        'description': _("Those settings allow you to combat automatic registrations and spam messages on your forum."),
+        'settings': (
+            ('bots_registration', {
+                'type':         "string",
+                'input':        "choice",
+                'extra':        {'choices': [('', _("No protection")), ('recaptcha', _("reCaptcha")), ('qa', _("Question & Answer"))]},
+                'separator':    _("Spambots Registrations"),
+                'name':         _("CAPTCHA type"),
+                'description':  _('CAPTCHA stands for "Completely Automated Public Turing test to tell Computers and Humans Apart". Its type of test developed on purpose of blocking automatic registrations.'),
+            }),
+            ('recaptcha_public', {
+                'type':         "string",
+                'input':        "text",
+                'separator':    _("reCaptcha"),
+                'name':         _("Public Key"),
+                'description':  _("Enter public API key that you have received from reCaptcha."),
+            }),
+            ('recaptcha_private', {
+                'type':         "string",
+                'input':        "text",
+                'name':         _("Private Key"),
+                'description':  _("Enter private API key that you have received from reCaptcha."),
+            }),
+            ('qa_test', {
+                'type':         "string",
+                'input':        "text",
+                'separator':    _("Question and Answer Test"),
+                'name':         _("Question"),
+                'description':  _("Question visible to your users."),
+            }),
+            ('qa_test_help', {
+                'type':         "string",
+                'input':        "text",
+                'name':         _("Help Message"),
+                'description':  _("Optional help message displayed on form."),
+            }),
+            ('qa_test_answers', {
+                'type':         "string",
+                'input':        "textarea",
+                'name':         _("Answers"),
+                'description':  _("Enter allowed answers to this question, each in new line. Test is case-insensitive."),
+            }),
+        ),
+    }),
+)
+
+def load_fixture():
+    load_settings_fixture(settings_fixtures)

+ 26 - 0
misago/security/forms.py

@@ -0,0 +1,26 @@
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+from misago.forms import Form
+
+class SignInForm(Form):
+    user_email = forms.EmailField(max_length=255, label=_("Your email"))
+    user_password = forms.CharField(max_length=255, label=_("Your password"))
+    user_remember_me = forms.BooleanField(label=_("Stay Signed In"), help_text=_("Sign me In automatically next time"), required=False)
+    user_stay_hidden = forms.BooleanField(label=_("Sign In as Hidden"), help_text=_("Dont show me on any on-line lists"), required=False)
+    
+    def __init__(self, *args, **kwargs):
+        show_remember_me = kwargs['show_remember_me']
+        show_stay_hidden = kwargs['show_stay_hidden']
+        del kwargs['show_remember_me']
+        del kwargs['show_stay_hidden']
+        
+        super(SignInForm, self).__init__(*args, **kwargs)
+        if not show_remember_me:
+            del self.fields['user_remember_me']
+        if not show_stay_hidden:
+            del self.fields['user_stay_hidden']
+    
+    class Meta:
+        widgets = {
+            'user_password': forms.PasswordInput(),
+        }

+ 0 - 0
misago/security/management/__init__.py


+ 0 - 0
misago/security/management/commands/__init__.py


+ 14 - 0
misago/security/management/commands/clearattempts.py

@@ -0,0 +1,14 @@
+from datetime import timedelta
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from misago.security.models import SignInAttempt
+
+class Command(BaseCommand):
+    """
+    This command is intended to work as CRON job fired every few days to remove failed sign-in attempts
+    """
+    help = 'Clears sign-in attempts log'
+    def handle(self):
+        SignInAttempt.objects.filter(date__lte=timezone.now() - timedelta(hours=24)).delete()
+        self.stdout.write('Failed Sign-In attempts older than 24h have been removed.\n')

+ 43 - 0
misago/security/middleware.py

@@ -0,0 +1,43 @@
+from django.conf import settings
+from misago.security import get_random_string
+from misago.security.csrf import CSRFProtection
+from misago.security.firewalls import *
+from misago.security.models import JamCache
+from misago.themes.theme import Theme
+
+class FirewallMiddleware(object):
+    firewall_admin = FirewallAdmin()
+    firewall_forum = FirewallForum()
+    def process_request(self, request):
+        # Admin firewall test
+        if settings.ADMIN_PATH and self.firewall_admin.behind_firewall(request.path_info):
+            request.firewall = self.firewall_admin
+            request.theme.set_theme('admin')
+        else:
+            request.firewall = self.firewall_forum
+
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        return request.firewall.process_view(request, callback, callback_args, callback_kwargs)
+
+class JamMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_crawler():
+            return None
+        try:
+            request.jam = request.session['jam']
+        except KeyError:
+            request.jam = JamCache()
+            request.session['jam'] = request.jam
+        if not request.firewall.admin:
+            request.jam.check_for_updates(request)
+
+class CSRFMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_crawler():
+            return None
+        if 'csrf_token' in request.session:
+            csrf_token = request.session['csrf_token']
+        else:
+            csrf_token = get_random_string(16);
+            request.session['csrf_token'] = csrf_token
+        request.csrf = CSRFProtection(csrf_token)

+ 75 - 0
misago/security/models.py

@@ -0,0 +1,75 @@
+from datetime import timedelta
+from random import randint
+from django.db import models
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+
+"""
+IP's that have exhausted their quota of sign-in attempts are automatically banned for set amount of time.
+
+That IP ban cuts bad IP address from signing into board by either making another sign-in attempts or
+registering "fresh" account.
+"""
+class SignInAttemptsManager(models.Manager):
+    """
+    Attempts manager
+    """
+    def register_attempt(self, ip):
+        attempt = SignInAttempt(ip=ip, date=timezone.now())
+        attempt.save(force_insert=True)
+        
+    def is_jammed(self, settings, ip):
+        # Limit is off, dont jam IPs?
+        if settings['login_attempts_limit'] == 0:
+            return False
+        # Check jam
+        if settings['jams_lifetime'] > 0:
+            attempts = SignInAttempt.objects.filter(
+                                                    date__gt=timezone.now() - timedelta(minutes=settings['jams_lifetime']),
+                                                    ip=ip
+                                                    )
+        else:
+            attempts = SignInAttempt.objects.filter(ip=ip)
+        return attempts.count() > settings['login_attempts_limit']
+    
+    
+class SignInAttempt(models.Model):
+    ip = models.GenericIPAddressField(db_index=True)
+    date = models.DateTimeField()
+    
+    objects = SignInAttemptsManager()
+    
+    
+class JamCache(object):
+    jammed = False
+    expires = timezone.now()
+    def check_for_updates(self, request):
+        if self.expires < timezone.now():
+            self.jammed = SignInAttempt.objects.is_jammed(request.settings, request.session.get_ip(request))
+            self.expires = timezone.now() + timedelta(minutes=request.settings['jams_lifetime'])
+            return True
+        return False
+    
+    def is_jammed(self):
+        return self.jammed
+
+ 
+"""
+Question-Answer Tests
+"""
+class QATestManager(models.Manager):
+    def random(self):
+        count = self.aggregate(count=models.Count('id'))['count']
+        random_index = randint(0, count - 1)
+        return self.all()[random_index]
+
+    
+class QATest(models.Model):
+    question = models.CharField(_("Question"),max_length=255)
+    helptext = models.TextField(null=True,blank=True)
+    answers = models.TextField()
+    
+    objects = QATestManager()
+    
+    def is_answer_correct(self, answer):
+        return unicode(answer).lower() in (name.lower() for name in unicode(self.answers).splitlines())

+ 13 - 0
misago/security/urls.py

@@ -0,0 +1,13 @@
+from django.conf.urls import patterns, url, include
+from misago.admin import ADMIN_PATH
+
+urlpatterns = patterns('misago.security.views',
+    url(r'^signin/$', 'signin', name="sign_in"),
+    url(r'^signout/$', 'signout', name="sign_out"),
+)
+
+# Include admin patterns
+if ADMIN_PATH:
+    urlpatterns += patterns('misago.security.views',
+        url(r'^' + ADMIN_PATH + 'signout/$', 'signout', name="admin_sign_out"),
+    )

+ 113 - 0
misago/security/views.py

@@ -0,0 +1,113 @@
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+from django.template import RequestContext
+from django.utils import timezone
+from django.utils.translation import ugettext as _
+from misago.admin import site
+from misago.banning.decorators import block_banned
+from misago.forms.layouts import FormLayout
+from misago.messages import Message
+from misago.security import get_random_string
+import misago.security.auth as auth
+from misago.security.auth import AuthException, auth_admin, auth_forum, sign_user_in
+from misago.security.decorators import *
+from misago.security.models import SignInAttempt
+from misago.sessions.models import Token
+from forms import SignInForm
+
+@block_banned
+@block_authenticated
+@block_jammed
+def signin(request):
+    message = request.messages.get_message('security')
+    if request.method == 'POST':
+        form = SignInForm(
+                          request.POST,
+                          show_remember_me=not request.firewall.admin and request.settings['remember_me_allow'],
+                          show_stay_hidden=not request.firewall.admin and request.settings['sessions_hidden'],
+                          request=request
+                          )
+        if form.is_valid():
+            try:
+                # Configure correct auth and redirect links
+                if request.firewall.admin:
+                    auth_method = auth_admin
+                    success_redirect = reverse(site.get_admin_index())
+                else:
+                    auth_method = auth_forum
+                    success_redirect = reverse('index')
+                
+                # Authenticate user
+                user = auth_method(
+                                  request,
+                                  form.cleaned_data['user_email'],
+                                  form.cleaned_data['user_password'],
+                                  )
+                
+                if not request.firewall.admin and request.settings['sessions_hidden'] and form.cleaned_data['user_stay_hidden']:
+                    request.session.hidden = True                    
+                
+                sign_user_in(request, user, request.session.hidden)     
+                           
+                remember_me_token = False
+                if not request.firewall.admin and request.settings['remember_me_allow'] and form.cleaned_data['user_remember_me']:
+                    remember_me_token = get_random_string(42)
+                    remember_me = Token(
+                                        id=remember_me_token,
+                                        user=user,
+                                        created=timezone.now(),
+                                        accessed=timezone.now(),
+                                        hidden=request.session.hidden
+                                        )
+                    remember_me.save()
+                if remember_me_token:
+                    request.cookie_jar.set('TOKEN', remember_me_token, True)
+                request.messages.set_flash(Message(request, 'security/signed_in', extra={'user': user}), 'success', 'security')
+                return redirect(success_redirect)
+            except AuthException as e:
+                message = Message(request, e.type, extra={'user':e.user})
+                message.type = 'error'
+                # If not in Admin, register failed attempt
+                if not request.firewall.admin and e.type == auth.CREDENTIALS:
+                    SignInAttempt.objects.register_attempt(request.session.get_ip(request))
+                    # Have we jammed our account?
+                    if SignInAttempt.objects.is_jammed(request.settings, request.session.get_ip(request)):
+                        request.jam.expires = timezone.now()
+                        return redirect(reverse('sign_in'))
+        else:
+            message = Message(request, form.non_field_errors()[0])
+            message.type = 'error'
+    else:
+        form = SignInForm(
+                          show_remember_me=not request.firewall.admin and request.settings['remember_me_allow'],
+                          show_stay_hidden=not request.firewall.admin and request.settings['sessions_hidden'],
+                          request=request
+                          )
+    return request.theme.render_to_response('signin.html',
+                                            {
+                                             'message': message,
+                                             'form': FormLayout(form, [
+                                                 (
+                                                     None,
+                                                     [('user_email', {'attrs': {'placeholder': _("Enter your e-mail")}}), ('user_password', {'has_value': False, 'placeholder': _("Enter your password")})]
+                                                 ),
+                                                 (
+                                                     None,
+                                                     ['user_remember_me', 'user_stay_hidden'],
+                                                 ),
+                                             ]),
+                                             'hide_signin': True, 
+                                            },
+                                            context_instance=RequestContext(request));
+
+
+@block_guest
+@check_csrf
+def signout(request):
+    user = request.user
+    request.session.sign_out(request)
+    request.messages.set_flash(Message(request, 'security/signed_out', extra={'user': user}), 'info', 'security')
+    if request.firewall.admin:
+        return redirect(reverse(site.get_admin_index()))
+    return redirect(reverse('index'))
+    

+ 0 - 0
misago/sessions/__init__.py


+ 0 - 0
misago/sessions/management/__init__.py


+ 0 - 0
misago/sessions/management/commands/__init__.py


+ 14 - 0
misago/sessions/management/commands/clearsessions.py

@@ -0,0 +1,14 @@
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from optparse import make_option
+from misago.sessions.models import Session
+
+class Command(BaseCommand):
+    """
+    This command is intended to work as CRON job fired every few hours to keep sessions table reasonable 
+    """
+    help = 'Clears users sessions'
+    def handle(self, *args, **options):
+        Session.objects.filter(last__lte=timezone.now() - timedelta(hours=12)).delete()
+        self.stdout.write('Sessions have been cleared.\n')

+ 14 - 0
misago/sessions/management/commands/cleartokens.py

@@ -0,0 +1,14 @@
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from optparse import make_option
+from misago.sessions.models import Token
+
+class Command(BaseCommand):
+    """
+    This command is intended to work as CRON job fired every few days to remove unused tokens 
+    """
+    help = 'Clears "Remember Me" tokens'
+    def handle(self, *args, **options):
+        Token.objects.filter(accessed__lte=timezone.now() - timedelta(days=5)).delete()
+        self.stdout.write('Sessions tokens have been cleared.\n')        

+ 19 - 0
misago/sessions/middleware.py

@@ -0,0 +1,19 @@
+from sessions import SessionCrawler, SessionHuman
+
+class SessionMiddleware(object):
+    def process_request(self, request):
+        try:
+            if request.user.is_crawler():
+                # Crawler Session
+                request.session = SessionCrawler(request)
+        except AttributeError:
+            # Human Session
+            request.session = SessionHuman(request)
+            request.user = request.session.get_user()
+                    
+    def process_response(self, request, response):
+        try:
+            request.session.save(request, response)
+        except AttributeError:
+            pass
+        return response

+ 22 - 0
misago/sessions/models.py

@@ -0,0 +1,22 @@
+from django.db import models
+       
+class Session(models.Model):
+    id = models.CharField(max_length=42, primary_key=True)
+    data = models.TextField(db_column="session_data")
+    user = models.ForeignKey('users.User', related_name='+', null=True, on_delete=models.SET_NULL)
+    crawler = models.CharField(max_length=255, blank=True, null=True)
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    start = models.DateTimeField()
+    last = models.DateTimeField()
+    staff = models.BooleanField(default=False)
+    admin = models.BooleanField(default=False)
+    matched = models.BooleanField(default=False)
+    hidden = models.BooleanField(default=False)
+
+class Token(models.Model):
+    id = models.CharField(max_length=42, primary_key=True)
+    user = models.ForeignKey('users.User', related_name='+')
+    created = models.DateTimeField()
+    accessed = models.DateTimeField()
+    hidden = models.BooleanField(default=False)

+ 236 - 0
misago/sessions/sessions.py

@@ -0,0 +1,236 @@
+from datetime import timedelta
+from django.conf import settings
+from django.contrib.sessions.backends.base import SessionBase, CreateError
+from django.db.models.loading import cache as model_cache
+from django.utils import timezone
+from django.utils.crypto import salted_hmac
+from django.utils.encoding import force_unicode
+from misago.security import get_random_string
+from misago.security.auth import auth_remember, AuthException
+from misago.users.models import Guest, User
+from misago.sessions.models import *
+
+# Assert models are loaded
+if not model_cache.loaded:
+    model_cache.get_models()
+    
+    
+class IncorrectSessionException(Exception):
+    pass
+
+    
+class SessionMisago(SessionBase):
+    """
+    Abstract class for sessions to inherit and extend
+    """
+    def _get_new_session_key(self):
+        return get_random_string(42)
+      
+    def _get_session(self):
+        try:
+            return self._session_cache
+        except AttributeError:
+            self._session_cache = self.load()
+        return self._session_cache
+        
+    def _hash(self, value):
+        key_salt = "misago.sessions" + self.__class__.__name__
+        return salted_hmac(key_salt, value).hexdigest()
+       
+    def delete(self):
+        """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
+        pass
+       
+    def flush(self):
+        """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
+        pass
+
+    def load(self):
+        return self.decode(force_unicode(self._session_rk.data))
+    
+    def session_expired(self):
+        return False
+        
+    def get_hidden(self):
+        return False
+    
+    def set_hidden(self, hidden=False):
+        pass
+    
+    def get_ip(self, request):
+        return request.META.get('HTTP_X_FORWARDED_FOR', '') or request.META.get('REMOTE_ADDR')
+    
+    def set_user(self, user = None):
+        pass
+    
+    def get_ban(self):
+        return False
+    
+    def set_ban(self, ban):
+        return False
+    
+    def save(self, request, response):
+        self._session_rk.data = self.encode(self._get_session())
+        self._session_rk.last = timezone.now()
+        self._session_rk.save(force_update=True)
+
+
+class SessionCrawler(SessionMisago):
+    """
+    Crawler Session controller
+    """
+    def __init__(self, request):
+        self._ip = self.get_ip(request)
+        try:
+            self._session_rk = Session.objects.get(crawler=request.user.username, ip=self._ip)
+            self._session_key = self._session_rk.id
+        except Session.DoesNotExist:
+            self.create(request)
+            
+    def create(self, request):
+        while True:
+            try:
+                self._session_key = self._get_new_session_key()
+                self._session_rk = Session(
+                                         id=self._session_key,
+                                         data=self.encode({}),
+                                         crawler=request.user.username,
+                                         ip=self._ip,
+                                         agent=request.META.get('HTTP_USER_AGENT', ''),
+                                         start=timezone.now(),
+                                         last=timezone.now(),
+                                         matched=True
+                                         )
+                self._session_rk.save(force_insert=True)
+                break
+            except CreateError:
+                # Key wasn't unique. Try again.
+                continue
+            
+    def human_session(self):
+        return False
+
+
+class SessionHuman(SessionMisago):
+    """
+    Human Session controller
+    """
+    def __init__(self, request):
+        self.expired = False
+        self.hidden = False
+        self.staff = False
+        self.remember_me = None
+        self._user = None
+        self._ip = self.get_ip(request)
+        self._session_token = None
+        if request.firewall.admin:
+            self._cookie_sid = settings.COOKIES_PREFIX + 'ASID'
+        else:   
+            self._cookie_sid = settings.COOKIES_PREFIX + 'SID'
+        try:
+            # Do we have correct session ID?
+            if self._cookie_sid not in request.COOKIES or len(request.COOKIES[self._cookie_sid]) != 42:
+                raise IncorrectSessionException()
+            self._session_key = request.COOKIES[self._cookie_sid]
+            self._session_rk = Session.objects.select_related().get(
+                                                                    pk=self._session_key,
+                                                                    admin=request.firewall.admin
+                                                                    )
+            # IP invalid
+            if request.settings.sessions_validate_ip and self._session_rk.ip != self._ip:
+                raise IncorrectSessionException()
+            # Session expired
+            if timezone.now() - self._session_rk.last > timedelta(seconds=settings.SESSION_LIFETIME):
+                self.expired = True
+                raise IncorrectSessionException()
+            # Change session to matched and extract session user and hidden flag
+            self._session_rk.matched = True
+            self._user = self._session_rk.user
+            if request.settings['sessions_hidden']:
+                self.hidden = self._session_rk.hidden
+        except (Session.DoesNotExist, IncorrectSessionException):
+            # Attempt autolog
+            try:
+                self.remember_me = auth_remember(request, self.get_ip(request))
+                self.create(request, user=self.remember_me.user, hidden=self.remember_me.hidden)
+            except AuthException as e:
+                # Autolog failed
+                self.create(request)
+        # Make cookie live longer
+        if request.firewall.admin:
+            request.cookie_jar.set('ASID', self._session_rk.id)
+        else:   
+            request.cookie_jar.set('SID', self._session_rk.id)
+            
+    def create(self, request, user=None, hidden=False):
+        self._user = user
+        self.hidden = hidden and request.settings['sessions_hidden']
+        while True:
+            try:
+                self._session_key = self._get_new_session_key()
+                self._session_rk = Session(
+                                         id=self._session_key,
+                                         data=self.encode({}),
+                                         user=self._user,
+                                         ip=self._ip,
+                                         agent=request.META.get('HTTP_USER_AGENT', ''),
+                                         start=timezone.now(),
+                                         last=timezone.now(),
+                                         admin=request.firewall.admin,
+                                         hidden=self.hidden
+                                         )
+                self._session_rk.save(force_insert=True)
+                if user:
+                    # Update user data
+                    user.set_last_visit(
+                                        self.get_ip(request),
+                                        request.META.get('HTTP_USER_AGENT', ''),
+                                        hidden=self.hidden
+                                        )
+                    user.save(force_update=True)
+                break
+            except CreateError:
+                # Key wasn't unique. Try again.
+                continue
+    
+    def save(self, request, response):
+        self._session_rk.user = self._user
+        self._session_rk.hidden = self.hidden
+        self._session_rk.staff = self.staff
+        super(SessionHuman, self).save(request, response)
+        
+    def human_session(self):
+        return True
+    
+    def session_expired(self):
+        return self.expired
+        
+    def get_user(self):
+        if self._user == None:
+            return Guest()
+        return self._user
+    
+    def set_user(self, user=None):
+        self._user = user
+    
+    def sign_out(self, request):
+        try:
+            if self._user.is_authenticated():
+                if not request.firewall.admin:
+                    cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
+                    if cookie_token in request.COOKIES:
+                        if len(request.COOKIES[cookie_token]) > 0:
+                            Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
+                        request.cookie_jar.delete('TOKEN')
+                self.hidden = False
+                self._user = None
+                request.user = Guest()
+        except AttributeError:
+            pass
+        
+    def get_hidden(self):
+        return self.hidden
+    
+    def set_hidden(self, hidden=False):
+        if settings.SESSIONS_HIDDEN:
+            self.hidden = hidden

+ 0 - 0
misago/settings/__init__.py


+ 28 - 0
misago/settings/admin/__init__.py

@@ -0,0 +1,28 @@
+from django.conf.urls import patterns, include, url
+from django.utils.translation import ugettext_lazy as _
+from misago.admin import AdminSection, AdminAction
+
+ADMIN_SECTIONS=(
+    AdminSection(
+                 id='system',
+                 name=_("System"),
+                 icon='cog',
+                 after='permissions',
+                 ),
+)
+
+ADMIN_ACTIONS=(
+   AdminAction(
+               section='system',
+               id='settings',
+               name=_("Settings"),
+               help=_("Change your forum configuration"),
+               icon='wrench',
+               route='admin_settings',
+               urlpatterns=patterns('misago.settings.admin.views',
+                        url(r'^$', 'settings', name='admin_settings'),
+                        url(r'^search/$', 'settings_search', name='admin_settings_search'),
+                        url(r'^(?P<group_slug>([a-zA-Z0-9]|-)+)\.(?P<group_id>\d+)/$', 'settings', name='admin_settings')
+                    ),
+               ),
+)

+ 6 - 0
misago/settings/admin/forms.py

@@ -0,0 +1,6 @@
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+from misago.forms import Form
+
+class SearchForm(Form):
+    search_text = forms.CharField(max_length=255)

+ 116 - 0
misago/settings/admin/views.py

@@ -0,0 +1,116 @@
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+from django.template import RequestContext
+from django.utils.translation import ungettext, ugettext as _
+from misago.forms import Form
+from misago.forms.layouts import FormLayout, FormFields
+from misago.messages import Message, BasicMessage
+from misago.search import SearchQuery, SearchException
+from misago.settings.admin.forms import SearchForm
+from misago.settings.models import Group, Setting
+from misago.views import error404
+
+def settings(request, group_id=None, group_slug=None):
+    # Load groups and find selected group
+    settings_groups = Group.objects.all().order_by('key')
+    if not group_id:
+        active_group = settings_groups[0]
+        group_id = active_group.pk
+    else:
+        group_id = int(group_id)
+        for group in settings_groups:
+            if group.pk == group_id:
+                active_group = group
+                break
+        else:
+            return error404(request, BasicMessage(_('The requested settings group could not be found.')))
+            
+    # Load selected group settings and turn them into form
+    group_settings = Setting.objects.filter(group=active_group).order_by('position')
+    last_fieldset = (None, [])
+    group_form = {'layout': []}
+    for setting in group_settings:
+        # New field subgroup?
+        if setting.separator and last_fieldset[0] != setting.separator:
+            if last_fieldset[0]:
+                group_form['layout'].append(last_fieldset)
+            last_fieldset = (_(setting.separator), [])
+        last_fieldset[1].append(setting.pk)
+        group_form[setting.pk] = setting.get_field()
+    group_form['layout'].append(last_fieldset)
+    SettingsGroupForm = type('SettingsGroupForm', (Form,), group_form)
+    
+    #Submit form
+    message = request.messages.get_message('admin_settings')
+    if request.method == 'POST':
+        form = SettingsGroupForm(request.POST, request=request)
+        if form.is_valid():
+            for setting in form.cleaned_data.keys():
+                request.settings[setting] = form.cleaned_data[setting]
+            request.messages.set_flash(BasicMessage(_('Configuration has been saved.')), 'success', 'admin_settings')
+            return redirect(reverse('admin_settings', kwargs={
+                                                       'group_id': active_group.pk,
+                                                       'group_slug': active_group.key,
+                                                       }))
+        else:
+            message = Message(request, form.non_field_errors()[0])
+            message.type = 'error'
+    else:
+        form = SettingsGroupForm(request=request)
+    
+    # Display settings group form      
+    return request.theme.render_to_response('settings/settings.html',
+                                            {
+                                            'message': message,
+                                            'groups': settings_groups,
+                                            'active_group': active_group,
+                                            'search_form': FormFields(SearchForm(request=request)),
+                                            'form': FormLayout(form),
+                                            'raw_form': form,
+                                            },
+                                            context_instance=RequestContext(request));
+
+
+def settings_search(request):
+    settings_groups = Group.objects.all().order_by('key')
+    message = None
+    found_settings = []
+    try:
+        if request.method == 'POST' and request.csrf.request_secure(request):
+            form = SearchForm(request.POST, request=request)
+            if form.is_valid():
+                # Start search
+                search_strings = SearchQuery(form.cleaned_data['search_text'])
+                
+                # Loop over groups using our search query
+                for setting in Setting.objects.all().order_by('setting'):
+                    if (search_strings.search(_(setting.name))
+                        or (setting.description and search_strings.search(_(setting.description)))
+                        or (setting.value and search_strings.search(setting.value))):
+                        found_settings.append(setting)
+                        
+                # Scream if nothing could be found
+                if found_settings:
+                    message = BasicMessage(ungettext(
+                                                    'One setting that match search criteria has been found.',
+                                                    '%(count)d settings that match search criteria have been found.',
+                                                len(found_settings)) % {
+                                                    'count': len(found_settings),
+                                                }, 'success')
+                else:                    
+                    raise SearchException(_('No settings that match search criteria has been found.'))
+            else:
+                raise SearchException(_('Search query is empty.'))
+        else:
+            raise SearchException(_('Search query is invalid.'))
+    except SearchException as e: 
+        message = BasicMessage(e.message, 'error')
+    return request.theme.render_to_response('settings/search_results.html',
+                                    {
+                                    'message': message,
+                                    'groups': settings_groups,
+                                    'active_group': None,
+                                    'found_settings': found_settings,
+                                    'search_form': FormFields(form),
+                                    },
+                                    context_instance=RequestContext(request));

+ 4 - 0
misago/settings/context_processors.py

@@ -0,0 +1,4 @@
+def settings(request):
+    return {
+        'settings' : request.settings,
+    }

+ 106 - 0
misago/settings/fixtures.py

@@ -0,0 +1,106 @@
+import base64
+from misago.settings.models import Group, Setting
+from misago.utils import ugettext_lazy as _
+from misago.utils import get_msgid
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+    
+settings_fixture = (
+   # Basic options
+   ('basic', {
+        'name': _("Basic Settings"),
+        'settings': (
+            ('board_name', {
+                'value':        "Misago",
+                'type':         "string",
+                'input':        "text",
+                'separator':    _("Board Name"),
+                'name':         _("Board Name"),
+            }),
+            ('board_header', {
+                'type':         "string",
+                'input':        "text",
+                'name':         _("Board Header"),
+                'description':  _("Some themes allow you to define text in board header. Leave empty to use Board Name instead."),
+            }),
+            ('board_header_postscript', {
+                'value':        "Work in progress ",
+                'type':         "string",
+                'input':        "text",
+                'name':         _("Board Header Postscript"),
+                'description':  _("Additional text displayed in some themes board header after board name."),
+            }),
+            ('board_index_title', {
+                'type':         "string",
+                'input':        "text",
+                'separator':    _("Board Index"),
+                'name':         _("Board Index Title"),
+                'description':  _("If you want to, you can replace page title content on Board Index with custom one."),
+            }),
+            ('board_index_meta', {
+                'type':         "string",
+                'input':        "text",
+                'name':         _("Board Index Meta-Description"),
+                'description':  _("Meta-Description used to describe your board's index page."),
+            }),
+            ('board_credits', {
+                'type':         "string",
+                'input':        "textarea",
+                'separator':    _("Board Footer"),
+                'name':         _("Custom Credit"),
+                'description':  _("Custom Credit to display in board footer above software and theme copyright information. You can use HTML."),
+            }),
+            ('email_footnote', {
+                'type':         "string",
+                'input':        "textarea",
+                'separator':    _("Board E-Mails"),
+                'name':         _("Custom Footnote"),
+                'description':  _("Custom Footnote to display in e-mail messages sent by board."),
+            }),
+        ),
+   }),
+)
+
+
+def load_settings_group_fixture(group, fixture):
+    model_group = Group(
+                  key=group,
+                  name=get_msgid(fixture['name']),
+                  description=get_msgid(fixture.get('description'))
+                  )
+    model_group.save(force_insert=True)
+    position = 0
+    fixture = fixture.get('settings', ())
+    for setting in fixture:
+        value = setting[1].get('value')
+        value_default = setting[1].get('default')
+        # Convert boolean True and False to 1 and 0, otherwhise it wont work
+        if setting[1].get('type') == 'boolean':
+            value = 1 if value else 0
+            value_default = 1 if value_default else 0
+        # Store setting in database
+        model_setting = Setting(
+                                setting=setting[0],
+                                group=model_group,
+                                value=value,
+                                value_default=value_default,
+                                type=setting[1].get('type'),
+                                input=setting[1].get('input'),
+                                extra=base64.encodestring(pickle.dumps(setting[1].get('extra', {}), pickle.HIGHEST_PROTOCOL)),
+                                position=position,
+                                separator=get_msgid(setting[1].get('separator')),
+                                name=get_msgid(setting[1].get('name')),
+                                description=get_msgid(setting[1].get('description')),
+                            )
+        model_setting.save(force_insert=True)
+        position += 1
+
+
+def load_settings_fixture(fixture):
+    for group in fixture:
+        load_settings_group_fixture(group[0], group[1])
+    
+def load_fixture():
+    load_settings_fixture(settings_fixture)

+ 5 - 0
misago/settings/middleware.py

@@ -0,0 +1,5 @@
+from misago.settings.settings import Settings
+
+class SettingsMiddleware(object):
+    def process_request(self, request):
+        request.settings = Settings()

+ 146 - 0
misago/settings/models.py

@@ -0,0 +1,146 @@
+import base64
+from django import forms
+from django.core import validators
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from misago.forms import YesNoSwitch
+from misago.timezones import tzlist
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+class Group(models.Model):
+    key = models.CharField(max_length=255, unique=True)
+    name = models.CharField(max_length=255)
+    description = models.TextField(null=True,blank=True)
+    
+    def is_active(self, active_group):
+        try:
+            return self.pk == active_group.pk
+        except AttributeError:
+            return False
+
+class Setting(models.Model):
+    setting = models.CharField(max_length=255, primary_key=True)
+    group = models.ForeignKey('Group', to_field='key')
+    value = models.TextField(null=True,blank=True)
+    value_default = models.TextField(null=True,blank=True)
+    type = models.CharField(max_length=255)
+    input = models.CharField(max_length=255)
+    extra = models.TextField(null=True,blank=True)
+    position = models.IntegerField(default=0)
+    separator = models.CharField(max_length=255,null=True,blank=True)
+    name = models.CharField(max_length=255)
+    description = models.TextField(null=True,blank=True)
+    
+    def get_extra(self):
+        return pickle.loads(base64.decodestring(self.extra))
+                
+    def get_value(self):
+        if self.type == 'array':
+            return self.value.split(',')
+        if self.type == 'integer':
+            return int(self.value)
+        if self.type == 'float':
+            return float(self.value)
+        if self.type == 'boolean':
+            return True if self.value == "1" else False
+        return self.value
+            
+    def set_value(self, value):
+        if self.type == 'array':
+            self.value = ','.join(value)
+        elif self.type == 'integer':
+            self.value = int(value)
+        elif self.type == 'float':
+            self.value = float(value)
+        elif self.type == 'boolean':
+            self.value = 1 if value else 0
+        else:
+            self.value = value
+        if not self.value and self.value_default:
+            self.value = self.value_default
+        return self.value
+    
+    def get_field(self):
+        extra = self.get_extra()
+        
+        # Set validators
+        field_validators = []
+        if 'min' in extra:
+            if self.type == 'string' or self.type == 'array':
+                field_validators.append(validators.MinLengthValidator(extra['min']))
+            if self.type == 'integer' or self.type == 'float':
+                field_validators.append(validators.MinValueValidator(extra['min']))
+        if 'max' in extra:
+            if self.type == 'string' or self.type == 'array':
+                field_validators.append(validators.MinValueValidator(extra['max']))
+            if self.type == 'integer' or self.type == 'float':
+                field_validators.append(validators.MaxLengthValidator(extra['max']))
+                
+        
+        # Yes-no
+        if self.input == 'yesno':
+            return forms.BooleanField(
+                                   initial=self.get_value(),
+                                   label=_(self.name),
+                                   help_text=_(self.description) if self.description else None,
+                                   required=False,
+                                   widget=YesNoSwitch,
+                                   )
+        
+        # Multi-list
+        if self.input == 'mlist':
+            return forms.MultipleChoiceField(
+                                     initial=self.get_value(),
+                                     label=_(self.name),
+                                     help_text=_(self.description) if self.description else None,
+                                     widget=forms.CheckboxSelectMultiple,
+                                     validators=field_validators,
+                                     required=False,
+                                     choices=extra['choices']
+                                     )
+        
+        # Select or choice
+        if self.input == 'select' or self.input == 'choice':
+            # Timezone list?
+            if extra['choices'] == '#TZ#':
+                extra['choices'] = tzlist()
+            return forms.ChoiceField(
+                                     initial=self.get_value(),
+                                     label=_(self.name),
+                                     help_text=_(self.description) if self.description else None,
+                                     widget=forms.RadioSelect if self.input == 'choice' else forms.Select,
+                                     validators=field_validators,
+                                     required=False,
+                                     choices=extra['choices']
+                                     )        
+        
+        # Textarea
+        if self.input == 'textarea':
+            return forms.CharField(
+                                   initial=self.get_value(),
+                                   label=_(self.name),
+                                   help_text=_(self.description) if self.description else None,
+                                   validators=field_validators,
+                                   required=False,
+                                   widget=forms.Textarea
+                                   )
+            
+        # Default input
+        default_input = forms.CharField
+        if self.type == 'integer':
+            default_input = forms.IntegerField
+        if self.type == 'float':
+            default_input = forms.FloatField
+            
+        # Make text-input
+        return default_input(
+                             initial=self.get_value(),
+                             label=_(self.name),
+                             help_text=_(self.description) if self.description else None,
+                             validators=field_validators,
+                             required=False,
+                             )
+        

+ 68 - 0
misago/settings/settings.py

@@ -0,0 +1,68 @@
+from django.db.utils import DatabaseError
+from django.core.cache import cache
+from misago.settings.models import Setting
+
+class Settings(object):
+    def __init__(self):
+        self._settings = {}
+        self._models = {}
+        self.refresh()
+        
+    def refresh(self):
+        self._models = cache.get('misago.settings')
+        if not self._models:
+            self._models = {}
+            try:
+                for i in Setting.objects.all():
+                    self._models[i.pk] = i
+                    self._settings[i.pk] = i.get_value()
+                cache.set('misago.settings', self._models)
+            except DatabaseError:
+                pass
+            
+    def __getattr__(self, key):
+        return self._settings[key]
+    
+    def __contains__(self, key):
+        return key in self._settings.keys()
+
+    def __getitem__(self, key):
+        return self._settings[key]
+
+    def __setitem__(self, key, value):
+        if key in self._settings.keys():
+            self._models[key].set_value(value)
+            self._models[key].save(force_update=True)
+            self._settings[key] = value
+            cache.set('misago.settings', self._models)
+        return value
+        
+    def __delitem__(self, key):
+        pass
+        
+    def get(self, key, default=None):
+        try:
+            return self._settings[key]
+        except KeyError:
+            return None
+        
+    def has_key(self, key):
+        return key in self._settings.keys()
+
+    def keys(self):
+        return self._settings.keys()
+
+    def values(self):
+        return self._settings.values()
+
+    def items(self):
+        return self._settings.items()
+
+    def iterkeys(self):
+        return self._settings.iterkeys()
+
+    def itervalues(self):
+        return self._settings.itervalues()
+
+    def iteritems(self):
+        return self._settings.iteritems()

+ 178 - 0
misago/settings_base.py

@@ -0,0 +1,178 @@
+import os
+
+# Board address
+BOARD_ADDRESS = 'http://127.0.0.1:8000/'
+
+# Admin control panel path
+# Leave this setting empty
+ADMIN_PATH = ''
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# List of directories that contain Misago locale files
+# Defautly set Django to look for Misago translations in misago/locale directory
+LOCALE_PATHS = (
+    ('%slocale%s' % (os.path.dirname( __file__ ) + os.sep, os.sep)),
+)
+
+# Catch-all e-mail address
+# If DEBUG_MODE is on, all emails will be sent to this address instead of real recipient.
+CATCH_ALL_EMAIL_ADDRESS = ''
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+)
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+)
+
+# Context processors
+TEMPLATE_CONTEXT_PROCESSORS = (
+    'django.core.context_processors.debug',
+    'django.core.context_processors.i18n',
+    'django.core.context_processors.media',
+    'django.core.context_processors.static',
+    'django.core.context_processors.tz',
+    'django.contrib.messages.context_processors.messages',
+    'misago.context_processors.core',
+    'misago.admin.context_processors.admin',
+    'misago.banning.context_processors.banning',
+    'misago.messages.context_processors.messages',
+    'misago.monitor.context_processors.monitor',
+    'misago.settings.context_processors.settings',
+    'misago.security.context_processors.security',
+    'misago.users.context_processors.user',
+)
+
+# Jinja2 Template Extensions
+JINJA2_EXTENSIONS = (
+    'jinja2.ext.do',
+)
+
+# List of application middlewares
+MIDDLEWARE_CLASSES = (
+    # Uncomment the next line for simple stopwatch
+    # Measured times will be logged in "stopwatch.txt"
+    # file located in directory containing settings.py
+    # 'misago.stopwatch.middleware.StopwatchMiddleware',
+    'debug_toolbar.middleware.DebugToolbarMiddleware',
+    'misago.cookie_jar.middleware.CookieJarMiddleware',
+    'misago.settings.middleware.SettingsMiddleware',
+    'misago.monitor.middleware.MonitorMiddleware',
+    'misago.themes.middleware.ThemeMiddleware',
+    'misago.security.middleware.FirewallMiddleware',
+    'misago.crawlers.middleware.DetectCrawlerMiddleware',
+    'misago.sessions.middleware.SessionMiddleware',
+    'misago.security.middleware.JamMiddleware',
+    'misago.security.middleware.CSRFMiddleware',
+    'misago.banning.middleware.BanningMiddleware',
+    'misago.messages.middleware.MessagesMiddleware',
+    'misago.users.middleware.UserMiddleware',
+    'django.middleware.common.CommonMiddleware',
+)
+
+# Name of root urls configuration
+ROOT_URLCONF = 'misago.urls'
+
+#Installed applications
+INSTALLED_APPS = (
+    # Applications that have no dependencies first!
+    'south',
+    'coffin',
+    'django.contrib.contenttypes',
+    'django.contrib.staticfiles',
+    'django.contrib.humanize',
+    'mptt', # Modified Pre-order Tree Transversal - allows us to nest forums 
+    'debug_toolbar', # Debug toolbar
+    'misago.settings', # Database level application configuration
+    'misago.monitor', # Forum statistics monitor
+    'misago.utils', # Utility classes
+    # Applications with dependencies
+    'misago.acl', # Web crawlers handling
+    'misago.auth', # User register, sign-in and sign-out
+    'misago.banning', # Banning and blacklisting users
+    'misago.crawlers', # Web crawlers handling
+    'misago.cookie_jar', # Cookies helper
+    'misago.forums', # Forums, threads and posts
+    'misago.messages', # Messages and Flashes
+    'misago.overview', # Admin system overview
+    'misago.security', # Security: CSRF, Firewall, etc ect
+    'misago.sessions', # Sessions
+    'misago.setup', # Installation/update tool
+    'misago.stopwatch', # Simple stopwatch to measure time spent on request
+    'misago.template', # Templates extensions
+    'misago.themes', # Themes
+    'misago.users', # Users and groups
+)
+
+# IP's that can see debug toolbar
+INTERNAL_IPS = ('127.0.0.1', '::1')
+
+# Debug toolbar config
+DEBUG_TOOLBAR_CONFIG = {
+    'INTERCEPT_REDIRECTS': False
+}
+
+# List panels displayed by toolbar
+DEBUG_TOOLBAR_PANELS = (
+    'debug_toolbar.panels.timer.TimerDebugPanel',
+    'debug_toolbar.panels.sql.SQLDebugPanel',
+    'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
+    'debug_toolbar.panels.headers.HeaderDebugPanel',
+    'debug_toolbar.panels.template.TemplateDebugPanel',
+    'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
+    'debug_toolbar.panels.signals.SignalDebugPanel',
+    'debug_toolbar.panels.logger.LoggingPanel',
+    'debug_toolbar.panels.version.VersionDebugPanel',
+)
+
+# Turn off caching
+CACHES = {
+    'default': {
+        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+    }
+}
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}

+ 0 - 0
misago/setup/__init__.py


+ 14 - 0
misago/setup/fixtures.py

@@ -0,0 +1,14 @@
+from django.utils.importlib import import_module
+
+def load_app_fixtures(app):
+    """
+    See if application has fixtures module defining load_fixtures function
+    If it does, execute that function
+    """
+    app += '.fixtures'
+    try:
+        fixture = import_module(app)
+        fixture.load_fixture()
+        return True
+    except (ImportError, AttributeError):
+        return False

+ 0 - 0
misago/setup/management/__init__.py


+ 0 - 0
misago/setup/management/commands/__init__.py


+ 39 - 0
misago/setup/management/commands/about.py

@@ -0,0 +1,39 @@
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from misago import get_version
+from optparse import make_option
+
+class Command(BaseCommand):
+    """
+    Displays version number and license
+    """
+    help = 'Displays Misago Credits'
+    def handle(self, *args, **options):
+        self.stdout.write('\n')
+        self.stdout.write('                                    _\n')
+        self.stdout.write('                         ____ ___  (_)________  ____  ____ \n')
+        self.stdout.write('                        / __ `__ \/ / ___/ __ `/ __ `/ __ \ \n')
+        self.stdout.write('                       / / / / / / (__  ) /_/ / /_/ / /_/ / \n')
+        self.stdout.write('                      /_/ /_/ /_/_/____/\__,_/\__, /\____/ \n')
+        self.stdout.write('                                             /____/\n')
+        self.stdout.write('\n')
+        self.stdout.write('                    Your community is powered by Misago v.%s' % get_version())
+        self.stdout.write('\n              For help and feedback visit http://misago-project.org')
+        self.stdout.write('\n\n')
+        self.stdout.write('================================================================================')
+        self.stdout.write('\n\n')
+        self.stdout.write('Copyright (C) %s, Rafal Piton' % timezone.now().year)
+        self.stdout.write('\n')
+        self.stdout.write('\nThis program is free software; you can redistribute it and/or modify it under')
+        self.stdout.write('\nthe terms of the GNU General Public License version 2 as published by')
+        self.stdout.write('\nthe Free Software Foundation')
+        self.stdout.write('\n')
+        self.stdout.write('\nThis program is distributed in the hope that it will be useful, but')
+        self.stdout.write('\nWITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY')
+        self.stdout.write('\nor FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License')
+        self.stdout.write('\nfor more details.')
+        self.stdout.write('\n')
+        self.stdout.write('\nYou should have received a copy of the GNU General Public License along')
+        self.stdout.write('\nwith this program; if not, write to the Free Software Foundation, Inc.,')
+        self.stdout.write('\n51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.')
+        self.stdout.write('\n\n')

+ 18 - 0
misago/setup/management/commands/loadfixtures.py

@@ -0,0 +1,18 @@
+from django.conf import settings
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from misago.setup.fixtures import load_app_fixtures
+from optparse import make_option
+
+class Command(BaseCommand):
+    """
+    Loads misago fixtures
+    """
+    help = 'Load Misago fixtures'
+    def handle(self, *args, **options):
+        fixtures = 0
+        for app in settings.INSTALLED_APPS:
+            if load_app_fixtures(app):
+                fixtures += 1
+                print 'Loading fixtures from %s' % app
+        self.stdout.write('\nLoaded fixtures from %s applications.\n' % fixtures)

+ 7 - 0
misago/stopwatch/__init__.py

@@ -0,0 +1,7 @@
+import time
+
+class Stopwatch(object):
+    def __init__(self):
+        self.start_time = time.time()
+    def time(self):
+        return time.time() - self.start_time 

+ 16 - 0
misago/stopwatch/middleware.py

@@ -0,0 +1,16 @@
+from . import Stopwatch
+
+class StopwatchMiddleware(object):
+    def process_request(self, request):
+        request.stopwatch = Stopwatch()
+        
+    def process_response(self, request, response):
+        try:
+            time = request.stopwatch.time()
+            import os
+            stat_file = open(os.getcwd() + os.sep + 'stopwatch.txt', 'a')
+            stat_file.write("%s s\n" % time)
+            stat_file.close()
+        except AttributeError:
+            pass
+        return response

+ 0 - 0
misago/template/__init__.py


+ 0 - 0
misago/template/templatetags/__init__.py


+ 13 - 0
misago/template/templatetags/django2jinja.py

@@ -0,0 +1,13 @@
+import urllib
+from coffin.template import Library
+register = Library()
+
+@register.object(name='widthratio')
+def widthratio(min=0, max=100, range=100):
+    return int(float(min) / float(max) * int(range))
+
+
+@register.object(name='query')
+def query_string(**kwargs):
+    query = urllib.urlencode(kwargs)
+    return '?%s' % query if kwargs else ''

+ 0 - 0
misago/themes/__init__.py


+ 8 - 0
misago/themes/middleware.py

@@ -0,0 +1,8 @@
+from django.conf import settings
+from theme import Theme
+
+class ThemeMiddleware(object):
+    def process_request(self, request):
+        if not settings.INSTALLED_THEMES:
+            raise ValueError('There are no themes installed!')
+        request.theme = Theme(settings.INSTALLED_THEMES[0])

+ 55 - 0
misago/themes/theme.py

@@ -0,0 +1,55 @@
+from django.conf import settings
+from coffin.shortcuts import render, render_to_response
+from coffin.template.loader import get_template, select_template, render_to_string
+
+'''Monkeypatch Django to mimic Jinja2 behaviour'''
+from django.utils import safestring
+if not hasattr(safestring, '__html__'):
+    safestring.SafeString.__html__ = lambda self: str(self)
+    safestring.SafeUnicode.__html__ = lambda self: unicode(self)
+
+class Theme(object):
+    def __init__(self, theme):
+        self.set_theme(theme);
+    
+    def set_theme(self, theme):
+        if theme not in settings.INSTALLED_THEMES:
+            raise ValueError('"%s" is not correct theme name.' % theme)
+        if theme[0] == '_':
+            raise ValueError('"%s" is a template package, not a theme.' % theme[1:])
+        self._theme = theme;
+          
+    def reset_theme(self):
+        self._theme = settings.INSTALLED_THEMES[0]
+    
+    def get_theme(self):
+        return self._theme
+    
+    def prefix_templates(self, templates):
+        if isinstance(templates, str):
+            return ('%s/%s' % (self._theme, templates), templates)
+        else:
+            prefixed = []
+            for template in templates:
+                prefixed.append('%s/%s' % (self._theme, template))
+            prefixed += templates
+            return prefixed
+    
+    def render(self, request, *args, **kwargs):
+        return render(request, *args, **kwargs)
+    
+    def render_to_string(self, templates, *args, **kwargs):
+        templates = self.prefix_templates(templates)
+        return render_to_string(templates, *args, **kwargs)
+    
+    def render_to_response(self, templates, *args, **kwargs):
+        templates = self.prefix_templates(templates)
+        return render_to_response(templates, *args, **kwargs)
+        
+    def get_email_templates(self, template, contex={}):
+            email_type_plain = '_email/%s_plain.html' % template
+            email_type_html = '_email/%s_html.html' % template
+            return (
+                    select_template(('%s/%s' % (self._theme, email_type_plain[1:]), email_type_plain)),
+                    select_template(('%s/%s' % (self._theme, email_type_html[1:]), email_type_html)),
+                    )

+ 87 - 0
misago/timezones/__init__.py

@@ -0,0 +1,87 @@
+import datetime
+from django.utils.translation import ugettext_lazy as _
+from django.utils import timezone
+import pytz
+
+raw_tz = (
+    ('Pacific/Apia', _('(UTC-13:00) Samoa'), _('(UTC-14:00) Samoa')),
+    ('Pacific/Midway', _('(UTC-11:00) Midway Islands, American Samoa')),
+    ('Pacific/Honolulu', _('(UTC-10:00) Cook Islands, Hawaii, Society Islands')),
+    ('America/Adak', _('(UTC-10:00) Aleutian Islands'), _('(UTC-09:00) Aleutian Islands')),
+    ('Pacific/Marquesas', _('(UTC-09:30) Marquesas Islands')),
+    ('Pacific/Gambier', _('(UTC-09:00) Gambier Islands')),
+    ('America/Anchorage', _('(UTC-09:00) Alaska Standard Time'), _('(UTC-08:00) Alaska Daylight Time')),
+    ('Pacific/Pitcairn', _('(UTC-08:00) Pitcairn Islands')),
+    ('America/Los_Angeles', _('(UTC-08:00) Pacific Time (Canada and US)'), _('(UTC-07:00) Pacific Time (Canada and US)')),
+    ('America/Santa_Isabel', _('(UTC-08:00) Baja California'), _('(UTC-07:00) Baja California')),
+    ('America/Phoenix', _('(UTC-07:00) Mountain Standard Time (No DST)')),
+    ('America/Hermosillo', _('(UTC-07:00) Sonora')),
+    ('America/Denver', _('(UTC-07:00) Mountain Standard Time'), _('(UTC-06:00) Mountain Summer Time')),
+    ('America/Chihuahua', _('(UTC-07:00) Baja California Sur, Chihuahua, Nayarit, Sinaloa'), _('(UTC-06:00) Baja California Sur, Chihuahua, Nayarit, Sinaloa')),
+    ('America/Costa_Rica', _('(UTC-06:00) Costa Rica, El Salvador, Galapagos, Guatemala, Managua')),
+    ('America/Chicago', _('(UTC-06:00) Central Standard Time'), _('(UTC-05:00) Central Daylight Time')),
+    ('America/Mexico_City', _('(UTC-06:00) Mexican Central Zone'), _('(UTC-05:00) Mexican Central Zone')),
+    ('America/Panama', _('(UTC-05:00) Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama')),
+    ('America/New_York', _('(UTC-05:00) Eastern Standard Time'), _('(UTC-04:00) Eastern Daylight Time')),
+    ('America/Caracas', _('(UTC-04:30) Caracas')),
+    ('America/Puerto_Rico', _('(UTC-04:00) Barbados, Dominica, Puerto Rico, Santo Domingo')),
+    ('America/Santiago', _('(UTC-04:00) Bermuda, Campo Grande, Goose Bay, Santiago, Thule'), _('(UTC-03:00) Bermuda, Campo Grande, Goose Bay, Santiago, Thule')),
+    ('America/St_Johns', _('(UTC-03:30) Newfoundland Time')),
+    ('America/Argentina/La_Rioja', _('(UTC-03:00) San Juan, San Luis, Santa Cruz')),
+    ('America/Sao_Paulo', _('(UTC-03:00) Buenos Aires, Godthab, Sao Paulo, Montevideo'), _('(UTC-02:00) Buenos Aires, Godthab, Sao Paulo, Montevideo')),
+    ('America/Noronha', _('(UTC-02:00) Atlantic islands')),
+    ('Atlantic/Cape_Verde', _('(UTC-01:00) Cape Verde Time')),
+    ('Atlantic/Azores', _('(UTC-01:00) Azores, Scoresbysund'), _('(UTC) Azores, Scoresbysund')),
+    ('UTC', _('(UTC) Coordinated Universal Time')),
+    ('Africa/Dakar', _('(UTC) Dakar, Rabat')),
+    ('Europe/Lisbon', _('(UTC) Western European Time'), _('(UTC+01:00) Western European Summer Time')),
+    ('Africa/Algiers', _('(UTC+01:00) West Africa Time')),
+    ('Europe/Zurich', _('(UTC+01:00) Central European Time'), _('(UTC+02:00) Central European Summer Time')),
+    ('Africa/Cairo', _('(UTC+02:00) Central Africa Time')),
+    ('Europe/Athens', _('(UTC+02:00) Eastern European Time'), _('(UTC+03:00) Eastern European Summer Time')),
+    ('Asia/Qatar', _('(UTC+03:00) East Africa Time')),
+    ('Europe/Minsk', _('(UTC+03:00) Further-eastern European Time')),
+    ('Asia/Tehran', _('(UTC+3:30) Iran Time'), _('(UTC+4:30) Iran Time')),
+    ('Europe/Moscow', _('(UTC+4:00) Moscow Standard Time, Georgian Time')),
+    ('Asia/Dubai', _('(UTC+04:00) United Arab Emirates Standard Time')),
+    ('Asia/Baku', _('(UTC+05:00) Baku, Yerevan'), _('(UTC+06:00) Baku, Yerevan')),
+    ('Asia/Kabul', _('(UTC+04:30) Afghanistan Standard Time')),
+    ('Asia/Karachi', _('(UTC+05:00) Ashgabat, Dushanbe, Karachi, Maldives, Tashkent')),
+    ('Asia/Kolkata', _('(UTC+05:30) Colombo, Kolkata')),
+    ('Asia/Kathmandu', _('(UTC+05:45) Kathmandu')),
+    ('Asia/Almaty', _('(UTC+06:00) Astana, Bishkek, Dhaka, Thimphu, Yekaterinburg')),
+    ('Asia/Rangoon', _('(UTC+06:30) Yangon, Cocos Islands')),
+    ('Asia/Bangkok', _('(UTC+07:00) Bangkok, Ho Chi Minh, Jakarta, Novosibirsk')),
+    ('Asia/Taipei', _('(UTC+08:00) Beijing, Hong Kong, Kuala Lumpur, Singapore, Taipei')),
+    ('Australia/Perth', _('(UTC+08:00) Australian Western Standard Time')),
+    ('Australia/Eucla', _('(UTC+08:45) Eucla Area')),
+    ('Asia/Tokyo', _('(UTC+09:00) Tokyo, Seoul, Irkutsk, Pyongyang')),
+    ('Australia/Darwin', _('(UTC+09:30) Australian Central Standard Time')),
+    ('Australia/Adelaide', _('(UTC+09:30) Australian Central Standard Time')),
+    ('Australia/Melbourne', _('(UTC+10:00) Australian Eastern Standard Time'), _('(UTC+11:00) Australian Eastern Summer Time')),
+    ('Australia/Lord_Howe', _('(UTC+10:30) Lord Howe Island'), _('(UTC+11:00) Lord Howe Island')),
+    ('Pacific/Guadalcanal', _('(UTC+11:00) Guadalcanal, Honiara, Noumea, Vladivostok')),
+    ('Pacific/Norfolk', _('(UTC+11:30) Norfolk Island')),
+    ('Pacific/Wake', _('(UTC+12:00) Kamchatka, Marshall Islands')),
+    ('Pacific/Auckland', _('(UTC+12:00) Auckland, Fiji'), _('(UTC+13:00) Auckland, Fiji')),
+    ('Pacific/Chatham', _('(UTC+12:45) Chatham Islands'), _('(UTC+13:45) Chatham Islands')),
+    ('Pacific/Enderbury', _('(UTC+13:00) Phoenix Islands')),
+    ('Pacific/Kiritimati', _('(UTC+14:00) Nuku\'alofa')),
+)
+
+def tzlist():
+    """
+    Generate user-friendly timezone list for selects
+    """
+    ready_list = []
+    utc_dt = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
+    for tz in raw_tz:
+        if len(tz) == 3:
+            tzinfo = pytz.timezone(tz[0])
+            if utc_dt.astimezone(tzinfo).dst().seconds > 0:
+                ready_list.append((tz[0], tz[2]))
+            else:
+                ready_list.append((tz[0], tz[1]))
+        else:
+           ready_list.append((tz[0], tz[1])) 
+    return tuple(ready_list)

+ 28 - 0
misago/urls.py

@@ -0,0 +1,28 @@
+from django.conf import settings
+from django.conf.urls import patterns, include, url
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from misago.admin import ADMIN_PATH, site
+
+# Include frontend patterns
+urlpatterns = patterns('',
+    (r'^', include('misago.security.urls')),
+    (r'^', include('misago.auth.urls')),
+    (r'^', include('misago.users.urls')),
+    url(r'^$', 'misago.views.home', name="index"),
+)
+
+# Include admin patterns
+if ADMIN_PATH:
+    urlpatterns += patterns('',
+        url(r'^' + ADMIN_PATH, include(site.discover())),
+    )
+    
+# Include static and media patterns in DEBUG
+if settings.DEBUG:
+    urlpatterns += patterns('django.views.static',
+        (r'media/(?P<path>.*)', 'serve', {'document_root': settings.MEDIA_ROOT}),
+    )
+
+# Set error handlers
+handler403 = 'misago.views.error403'
+handler404 = 'misago.views.error404'

+ 0 - 0
misago/users/__init__.py


+ 90 - 0
misago/users/admin/__init__.py

@@ -0,0 +1,90 @@
+from django.conf.urls import patterns, include, url
+from django.utils.translation import ugettext_lazy as _
+from misago.admin import AdminSection, AdminAction
+from misago.users.models import User
+
+ADMIN_SECTIONS=(
+    AdminSection(
+                 id='users',
+                 name=_("Users"),
+                 icon='user',
+                 after='overview',
+                 ),
+)
+
+ADMIN_ACTIONS=(
+   AdminAction(
+               section='users',
+               id='users',
+               name=_("Users List"),
+               help=_("Search and browse users"),
+               icon='user',
+               model=User,
+               actions=[
+                        {
+                         'id': 'list',
+                         'icon': 'list-alt',
+                         'name': _("Browse Users"),
+                         'help': _("Browse all registered user accounts"),
+                         'route': 'admin_users'
+                         },
+                        {
+                         'id': 'new',
+                         'icon': 'plus',
+                         'name': _("Add User"),
+                         'help': _("Create new user account"),
+                         'route': 'admin_users_new'
+                         },
+                        ],
+               route='admin_users',
+               urlpatterns=patterns('misago.users.admin.users.views',
+                        url(r'^$', 'List', name='admin_users'),
+                        url(r'^new/$', 'List', name='admin_users_new'),
+                        url(r'^delete/(?P<slug>([a-zA-Z0-9]|-)+)\.(?P<target>\d+)/$', 'Delete', name='admin_users_delete'),
+                    ),
+               ),
+   AdminAction(
+               section='users',
+               id='ranks',
+               name=_("Ranks"),
+               help=_("Administrate User Ranking system"),
+               icon='star',
+               route='admin_users_ranks',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_users_ranks'),
+                    ),
+               ),
+   AdminAction(
+               section='users',
+               id='bans',
+               name=_("Banning"),
+               help=_("Ban or unban users from forums."),
+               icon='lock',
+               route='admin_users_bans',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_users_bans'),
+                    ),
+               ),
+   AdminAction(
+               section='users',
+               id='prune',
+               name=_("Prune Users"),
+               help=_("Delete multiple Users"),
+               icon='remove',
+               route='admin_users_prune',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_users_prune'),
+                    ),
+               ),
+   AdminAction(
+               section='users',
+               id='newsletters',
+               name=_("Newsletters"),
+               help=_("Manage and send Newsletters"),
+               icon='envelope',
+               route='admin_users_newsletters',
+               urlpatterns=patterns('misago.admin.views',
+                        url(r'^$', 'todo', name='admin_users_newsletters'),
+                    ),
+               ),
+)

+ 0 - 0
misago/users/admin/users/__init__.py


+ 22 - 0
misago/users/admin/users/forms.py

@@ -0,0 +1,22 @@
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+from misago.forms import Form
+
+class SearchUsersForm(Form):
+    """
+    Search Users
+    """
+    username = forms.CharField(max_length=255, required=False)
+    email = forms.CharField(max_length=255, required=False)
+    activation = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,choices=(('0', _("Already Active")), ('1', _("By User")), ('2', _("By Administrator"))),required=False)
+    layout = (
+              (
+               _("Search Users"),
+               (
+                ('username', {'label': _("Username"), 'attrs': {'placeholder': _("Username contains...")}}),
+                ('email', {'label': _("E-mail Address"), 'attrs': {'placeholder': _("E-mail address contains...")}}),
+                ('activation', {'label': _("Activation Requirement")}),
+               ),
+              ),
+             )
+    

+ 88 - 0
misago/users/admin/users/views.py

@@ -0,0 +1,88 @@
+from django.core.urlresolvers import reverse as django_reverse
+from django.utils.translation import ugettext as _
+from misago.admin import site
+from misago.admin.widgets import *
+from misago.utils import slugify
+from misago.users.admin.users.forms import SearchUsersForm
+from misago.users.models import User
+
+def reverse(route, target=None):
+    if target:
+        return django_reverse(route, kwargs={'target': target.pk, 'slug': slugify(target.username)})
+    return django_reverse(route)
+
+"""
+Views
+"""
+class List(ListWidget):
+    """
+    List Users
+    """
+    admin = site.get_action('users')
+    id = 'list'
+    columns=(
+             ('username_slug', _("User Name"), 50),
+             ('join_date', _("Join Date")),
+             )
+    default_sorting = 'username'
+    sortables={
+               'username_slug': 1,
+               'join_date': 0,
+              }
+    pagination = 15
+    nothing_checked_message = _('You have to check at least one user.')
+    actions=(
+             ('reset', _("Reset passwords"), _("Are you sure you want to reset selected members passwords?")),
+             ('remove_avs', _("Remove avatars"), _("Are you sure you want to reset selected members passwords?")),
+             ('delete', _("Delete selected"), _("Are you sure you want to delete selected users?")),
+             )
+    
+    def get_search_form(self, request):
+        return SearchUsersForm
+    
+    def set_filters(self, model, filters):
+        if 'username' in filters:
+            model = model.filter(username_slug__contains=filters['username'])
+        if 'email' in filters:
+            model = model.filter(email__contains=filters['email'])
+        if 'activation' in filters:
+            model = model.filter(activation__in=filters['activation'])
+        return model
+    
+    def get_item_actions(self, request, item):
+        return (
+                self.action('pencil', _("Edit User Details"), reverse('admin_qa_edit', item)),
+                self.action('remove', _("Delete User"), reverse('admin_users_delete', item), post=True, prompt=_("Are you sure you want to delete this user account?")),
+                )
+
+    def action_delete(self, request, items, checked):
+        print '%r' % checked
+        for user in items:
+            if unicode(user.pk) in checked:
+                if user.pk == request.user.id:
+                    return BasicMessage(_('You cannot delete yourself.'), 'error'), reverse('admin_users')
+                if user.is_god():
+                    return BasicMessage(_('You cannot delete system administrator.'), 'error'), reverse('admin_users')
+                
+        User.objects.filter(id__in=checked).delete()
+        User.objects.resync_monitor(request.monitor)
+        return BasicMessage(_('Selected users have been deleted successfully.'), 'success'), reverse('admin_users')
+
+
+class Delete(ButtonWidget):
+    """
+    Delete QA Test
+    """
+    admin = site.get_action('users')
+    id = 'delete'
+    fallback = 'admin_users'
+    notfound_message = _('Requested user account could not be found.')
+    
+    def action(self, request, target):
+        if target.pk == request.user.id:
+            return BasicMessage(_('You cannot delete yourself.'), 'error'), False
+        if target.is_god():
+            return BasicMessage(_('You cannot delete system administrator.'), 'error'), False
+        target.delete()
+        User.objects.resync_monitor(request.monitor)
+        return BasicMessage(_('User "%(name)s" has been deleted.' % {'name': target.username}), 'success'), False

+ 4 - 0
misago/users/context_processors.py

@@ -0,0 +1,4 @@
+def user(request):
+    return {
+        'user': request.user,
+    }

+ 139 - 0
misago/users/fixtures.py

@@ -0,0 +1,139 @@
+from misago.monitor.fixtures import load_monitor_fixture
+from misago.settings.fixtures import load_settings_fixture
+from misago.users.models import Rank, Group
+from misago.utils import ugettext_lazy as _
+from misago.utils import get_msgid
+
+monitor_fixtures = {
+                  'users': 0,
+                  'users_inactive': 0,
+                  'users_reported': 0,
+                  'last_user': None,
+                  'last_user_name': None,
+                  'last_user_slug': None,
+                  }
+
+settings_fixtures = (
+   # Avatars Settings
+   ('avatars', {
+        'name': _("Users Avatars Settings"),
+        'description': _("Those settings allow you to control your users avatars."),
+        'settings': (
+            ('avatars_types', {
+                'value':        ['gravatar', 'gallery'],
+                'type':         "array",
+                'input':        "mlist",
+                'extra':        {'choices': [('gravatar', _("Gravatar")), ('upload', _("Uploaded Avatar")), ('gallery', _("Avatars Gallery"))]},
+                'separator':    _("General Settings"),
+                'name':         _("Allowed Avatars"),
+                'description':  _("Select Avatar types allowed on your forum."),
+            }),
+            ('default_avatar', {
+                'value':        "gravatar",
+                'type':         "string",
+                'input':        "select",
+                'extra':        {'choices': [('gravatar', _("Gravatar")), ('gallery', _("Random Avatar from Gallery"))]},
+                'name':         _("Default Avatar"),
+                'description':  _("Default Avatar assigned to new members. If you creade directory and name it \"_default\", forum will select random avatar from that directory instead of regular gallery. If no avatar can be picked from gallery, Gravatar will be used."),
+            }),
+            ('upload_limit', {
+                'value':        128,
+                'type':         "integer",
+                'input':        "text",
+                'extra':        {'min': 0},
+                'separator':    _("Avatar Upload Settings"),
+                'name':         _("Maxmimum size of uploaded file"),
+                'description':  _("Select maximum allowed file size (in KB) for Avatar uploads."),
+            }),
+        ),
+    }),
+)
+
+def load_fixture():
+    load_monitor_fixture(monitor_fixtures)
+    load_settings_fixture(settings_fixtures)
+    
+    rank_staff = Rank(
+                      name=_("Forum Staff").message,
+                      style='staff',
+                      title=_("Forum Staff").message,
+                      special=True,
+                      order=1,
+                      )
+    rank_guest = Rank(
+                      name=_("Unregistered").message,
+                      style='guest',
+                      title=_("Guest").message,
+                      special=True,
+                      order=2,
+                      )
+    rank_lurker = Rank(
+                      name=_("Lurker").message,
+                      style='lurker',
+                      title=_("Lurker").message,
+                      order=3,
+                      criteria=0
+                      )
+    rank_member = Rank(
+                      name=_("Member").message,
+                      title=_("Member").message,
+                      order=4,
+                      criteria=">15"
+                      )
+    rank_active = Rank(
+                      name=_("Active Member").message,
+                      title=_("Active Member").message,
+                      order=5,
+                      criteria="15%"
+                      )
+    
+    rank_staff.save(force_insert=True)
+    rank_guest.save(force_insert=True)
+    rank_lurker.save(force_insert=True)
+    rank_member.save(force_insert=True)
+    rank_active.save(force_insert=True)
+    
+    group_admins = Group(
+                         name=_("Administrators").message,
+                         name_slug='administrators',
+                         tab=_("Staff").message,
+                         position=1,
+                         rank=rank_staff,
+                         special=True,
+                         )
+    group_mods = Group(
+                       name=_("Moderators").message,
+                       name_slug='moderators',
+                       tab=_("Staff").message,
+                       position=2,
+                       rank=rank_staff,
+                       )
+    group_registered = Group(
+                         name=_("Registered").message,
+                         name_slug='registered',
+                         hidden=True,
+                         position=3,
+                         special=True,
+                         )
+    group_guests = Group(
+                         name=_("Guests").message,
+                         name_slug='guests',
+                         hidden=True,
+                         position=4,
+                         rank=rank_guest,
+                         special=True,
+                         )
+    group_crawlers = Group(
+                           name=_("Web Crawlers").message,
+                           name_slug='web-crawlers',
+                           hidden=True,
+                           position=5,
+                           rank=rank_guest,
+                           special=True,
+                           )
+    
+    group_admins.save(force_insert=True)
+    group_mods.save(force_insert=True)
+    group_registered.save(force_insert=True)
+    group_guests.save(force_insert=True)
+    group_crawlers.save(force_insert=True)    

+ 0 - 0
misago/users/management/__init__.py


+ 0 - 0
misago/users/management/commands/__init__.py


+ 39 - 0
misago/users/management/commands/adduser.py

@@ -0,0 +1,39 @@
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from optparse import make_option
+from misago.users.models import UserManager, Group
+
+class Command(BaseCommand):
+    args = 'username email password'
+    help = 'Creates new user account'
+    option_list = BaseCommand.option_list + (
+        make_option('--admin',
+            action='store_true',
+            dest='admin',
+            default=False,
+            help='Make a new user an administrator'),
+        )
+
+    def handle(self, *args, **options):
+        if len(args) < 3:
+            raise CommandError('adduser requires exactly three arguments: user name, e-mail addres and password')
+        
+        # Get group
+        if options['admin']:
+            group = Group.objects.get(pk=1)
+        else:
+            group = Group.objects.get(pk=3)
+           
+        # Set user
+        try:
+            manager = UserManager()
+            manager.create_user(args[0], args[1], args[2], group=group)
+        except ValidationError as e:
+            raise CommandError("New user cannot be created because of following errors:\n\n%s" % '\n'.join(e.messages))
+        
+        if options['admin']:
+            self.stdout.write('Successfully created new administrator "%s"' % args[0])
+        else:
+            self.stdout.write('Successfully created new user "%s"' % args[0])
+        self.stdout.write('\n\nNew user should use "%s" e-mail address and "%s" password to sign in.\n' % (args[1], args[2]))

+ 12 - 0
misago/users/management/commands/updateranks.py

@@ -0,0 +1,12 @@
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
+from django.core.management.base import BaseCommand, CommandError
+from django.utils import timezone
+from optparse import make_option
+
+class Command(BaseCommand):
+    """
+    This command is intended to work as CRON job fired of once per day or less if you have more users.
+    """
+    help = 'Updates users ranking'
+    def handle(self):
+        self.stdout.write('Users ranking has been updated.\n')

+ 26 - 0
misago/users/middleware.py

@@ -0,0 +1,26 @@
+from django.conf import settings
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+
+
+def set_timezone(new_tz):
+    if settings.USE_TZ:
+        try:
+            import pytz
+            timezone.activate(pytz.timezone(new_tz))
+        except ImportError:
+            pass
+
+
+class UserMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_authenticated():
+            # Set user timezone
+            set_timezone(request.user.timezone)
+            
+            # Display "welcome back!" message
+            if request.session.remember_me:
+                request.messages.set_message(_("We have signed you in automatically."), 'info', _("Welcome back, %(username)s!" % {'username': request.user.username}))
+        else:
+            # Set guest's timezone
+            set_timezone(request.settings['default_timezone'])

+ 421 - 0
misago/users/models.py

@@ -0,0 +1,421 @@
+import hashlib
+from random import choice
+from django.conf import settings
+from django.contrib.auth.hashers import (
+    check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
+from django.core.exceptions import ValidationError
+from django.core.mail import EmailMultiAlternatives
+from django.db import models
+from django.template import RequestContext
+from django.utils import timezone as tz_util
+from django.utils.translation import ugettext_lazy as _
+from misago.monitor.monitor import Monitor
+from misago.security import get_random_string
+from misago.settings.settings import Settings as DBSettings
+from misago.users.validators import validate_username, validate_password, validate_email
+from misago.utils import slugify
+from path import path
+
+class UserManager(models.Manager):
+    """
+    User Manager provides us with some additional methods for users
+    """
+    def get_blank_user(self):
+        blank_user = User(
+                        join_date=tz_util.now(),
+                        join_ip='127.0.0.1'
+                        )
+        return blank_user
+    
+    def resync_monitor(self, monitor):
+        monitor['users'] = self.count()
+        monitor['users_inactive'] = self.filter(activation__gt=0).count()
+        last_user = self.latest('id')
+        monitor['last_user'] = last_user.pk
+        monitor['last_user_name'] = last_user.username
+        monitor['last_user_slug'] = last_user.username_slug
+    
+    def create_user(self, username, email, password, group, timezone=False, ip='127.0.0.1', activation=0, request=False):
+        token = ''
+        if activation > 0:
+            token = get_random_string(12)
+            
+        if timezone == False:
+            try:
+                timezone = request.settings['default_timezone']
+                db_settings = request.settings
+            except AttributeError:
+                db_settings = DBSettings()
+                timezone = db_settings['default_timezone']
+        
+        # Store user in database
+        new_user = User(
+                        join_date=tz_util.now(),
+                        join_ip=ip,
+                        activation=activation,
+                        token=token,
+                        timezone=timezone,
+                        group=group,
+                        )
+        
+        new_user.set_username(username)
+        new_user.set_email(email)
+        new_user.set_password(password)
+        new_user.full_clean()
+        new_user.default_avatar(db_settings)
+        new_user.save(force_insert=True)
+        
+        # Set second group and default avatar
+        new_user.groups.add(group)
+        new_user.save(force_update=True)
+        
+        # Load monitor
+        try:
+            monitor = request.monitor
+        except AttributeError:
+            monitor = Monitor()
+        
+        # Update forum stats
+        if activation == 0:
+            monitor['users'] = int(monitor['users']) + 1
+            monitor['last_user'] = new_user.pk
+            monitor['last_user_name'] = new_user.username
+            monitor['last_user_slug'] = new_user.username_slug
+        else:
+            monitor['users_inactive'] = int(monitor['users_inactive']) + 1
+            
+        # Return new user
+        return new_user
+            
+    def get_by_email(self, email):
+        return self.get(email_hash=hashlib.md5(email).hexdigest())
+    
+    def filter_overview(self, start, end):
+        return self.filter(join_date__gte=start).filter(join_date__lte=end)
+    
+        
+class User(models.Model):
+    """
+    Misago User model
+    """
+    username = models.CharField(max_length=255,validators=[validate_username])
+    username_slug = models.SlugField(max_length=255,unique=True,
+                                     error_messages={'unique': _("This user name is already in use by another user.")})
+    email = models.EmailField(max_length=255,validators=[validate_email])
+    email_hash = models.CharField(max_length=32,unique=True,
+                                     error_messages={'unique': _("This email address is already in use by another user.")})
+    password = models.CharField(max_length=255)
+    password_date = models.DateTimeField()
+    avatar_type = models.CharField(max_length=10,null=True,blank=True)
+    avatar_image = models.CharField(max_length=255,null=True,blank=True)
+    signature = models.TextField(null=True,blank=True)
+    signature_preparsed = models.TextField(null=True,blank=True)
+    join_date = models.DateTimeField()
+    join_ip = models.GenericIPAddressField()
+    join_agent = models.TextField(null=True,blank=True)
+    last_date = models.DateTimeField(null=True,blank=True)
+    last_ip = models.GenericIPAddressField(null=True,blank=True)
+    last_agent = models.TextField(null=True,blank=True)
+    hide_activity = models.BooleanField(default=False)
+    topics = models.PositiveIntegerField(default=0)
+    topics_delta = models.IntegerField(default=0)
+    posts = models.PositiveIntegerField(default=0)
+    posts_delta = models.IntegerField(default=0)
+    karma = models.IntegerField(default=0)
+    karma_delta = models.IntegerField(default=0)
+    followers = models.PositiveIntegerField(default=0)
+    followers_delta = models.IntegerField(default=0)
+    score = models.FloatField(default=0,db_index=True)
+    rank = models.ForeignKey('Rank',null=True,blank=True,db_index=True,on_delete=models.SET_NULL)
+    rank_freezed = models.BooleanField(default=False,db_index=True)
+    last_post = models.DateTimeField(null=True,blank=True)
+    last_search = models.DateTimeField(null=True,blank=True)
+    alerts = models.PositiveIntegerField(default=0)
+    alerts_new = models.PositiveIntegerField(default=0)
+    activation = models.IntegerField(default=0)
+    token = models.CharField(max_length=12,null=True,blank=True)
+    banned = models.BooleanField(default=False)
+    ban_reason_admin = models.TextField(null=True,blank=True)
+    ban_reason_user = models.TextField(null=True,blank=True)
+    ban_expires = models.DateTimeField(null=True,blank=True)
+    avatar_ban = models.BooleanField(default=False)
+    avatar_ban_reason_user = models.TextField(null=True,blank=True)
+    avatar_ban_reason_admin = models.TextField(null=True,blank=True)
+    avatar_ban_expires = models.DateTimeField(null=True,blank=True)
+    signature_ban = models.BooleanField(default=False)
+    signature_ban_reason_user = models.TextField(null=True,blank=True)
+    signature_ban_reason_admin = models.TextField(null=True,blank=True)
+    signature_ban_expires = models.DateTimeField(null=True,blank=True)
+    timezone = models.CharField(max_length=255,default='utc')
+    roles = models.CommaSeparatedIntegerField(max_length=255,null=True,blank=True)
+    group = models.ForeignKey('Group',on_delete=models.PROTECT)
+    groups = models.ManyToManyField('Group',related_name='groups')
+    acl_cache = models.TextField(null=True,blank=True)
+    
+    objects = UserManager()   
+    
+    ACTIVATION_NONE = 0
+    ACTIVATION_USER = 1
+    ACTIVATION_ADMIN = 2
+    ACTIVATION_PASSWORD = 3
+    
+    statistics_name = _('Users Registrations')
+        
+    def is_admin(self):
+        return 1 == self.group.pk
+    
+    def is_anonymous(self):
+        return False
+    
+    def is_authenticated(self):
+        return True
+    
+    def is_crawler(self):
+        return False
+
+    def is_banned(self):
+        """
+        Check if user is banned and handle eventual ban expiration.
+        """
+        banned = self.banned and (self.ban_expires == None or self.ban_expires > tz_util.now());
+        if not banned and self.banned:
+            self.banned = False
+            self.save(force_update=True)
+        return banned
+
+    def default_avatar(self, db_settings):
+        if db_settings['default_avatar'] == 'gallery':
+            try:
+                avatars_list = []
+                try:
+                    # First try, _default path
+                    galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_default')
+                    avatars_list += galleries.files('*.gif')
+                    avatars_list += galleries.files('*.jpg')
+                    avatars_list += galleries.files('*.jpeg')
+                    avatars_list += galleries.files('*.png')
+                except Exception as e:
+                    pass
+                # Second try, all paths
+                if not avatars_list:
+                    avatars_list = []
+                    for directory in path(settings.STATICFILES_DIRS[0]).joinpath('avatars').dirs():
+                        avatars_list += directory.files('*.gif')
+                        avatars_list += directory.files('*.jpg')
+                        avatars_list += directory.files('*.jpeg')
+                        avatars_list += directory.files('*.png')
+                if avatars_list:
+                    # Pick random avatar from list
+                    self.avatar_type = 'gallery'
+                    self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
+                    return True
+            except Exception as e:
+                pass
+        self.avatar_type = 'gravatar'
+        self.avatar_image = None
+        return True
+
+    def delete_avatar(self):
+        if self.avatar_type == 'upload':
+            # DELETE OUR AVATAR!!!
+            pass
+        
+    def delete(self, *args, **kwargs):
+        self.delete_avatar()
+        super(User, self).delete(*args, **kwargs)
+            
+    def set_username(self, username):
+        self.username = username.strip()
+        self.username_slug = slugify(username)
+        
+    def is_username_valid(self, e):
+        try:
+            raise ValidationError(e.message_dict['username'])
+        except KeyError:
+            pass
+        try:
+            raise ValidationError(e.message_dict['username_slug'])
+        except KeyError:
+            pass
+        
+    def is_email_valid(self, e):
+        try:
+            raise ValidationError(e.message_dict['email'])
+        except KeyError:
+            pass
+        try:
+            raise ValidationError(e.message_dict['email_hash'])
+        except KeyError:
+            pass
+        
+    def is_password_valid(self, e):
+        try:
+            raise ValidationError(e.message_dict['password'])
+        except KeyError:
+            pass
+        
+    def set_email(self, email):
+        self.email = email.strip().lower()
+        self.email_hash = hashlib.md5(self.email).hexdigest()
+        
+    def set_password(self, raw_password):
+        self.password_date = tz_util.now()
+        self.password = make_password(raw_password.strip())
+
+    def set_last_visit(self, ip, agent, hidden=False):
+        self.last_date = tz_util.now()
+        self.last_ip = ip
+        self.last_agent = agent
+        self.last_hide = hidden
+
+    def check_password(self, raw_password, mobile=False):
+        """
+        Returns a boolean of whether the raw_password was correct. Handles
+        hashing formats behind the scenes.
+        """
+        def setter(raw_password):
+            self.set_password(raw_password)
+            self.save()
+            
+        # Is standard password allright?
+        if check_password(raw_password, self.password, setter):
+            return True
+        
+        # Check mobile password?
+        if mobile:
+            raw_password = raw_password[:1].lower() + raw_password[1:]
+        else:
+            password_reversed = u''
+            for c in raw_password:
+                r = c.upper()
+                if r == c:
+                    r = c.lower()
+                password_reversed += r 
+            raw_password = password_reversed
+        return check_password(raw_password, self.password, setter)
+    
+    def get_avatar(self, size='normal'):
+        # Get uploaded avatar
+        if self.avatar_type == 'upload':
+            return settings.MEDIA_URL + 'avatars/' + self.avatar_image
+        
+        # Get gallery avatar
+        if self.avatar_type == 'gallery':
+            return settings.STATIC_URL + 'avatars/' + self.avatar_image
+        
+        # No avatar found, get gravatar
+        if size == 'big':
+            size = 150;
+        elif size == 'small':
+            size = 64;
+        elif size == 'tiny':
+            size = 46;
+        else:
+            size = 100
+        return 'http://www.gravatar.com/avatar/%s?s=%s' % (hashlib.md5(self.email).hexdigest(), size)
+    
+    def email_user(self, request, template, subject, context={}):
+        templates = request.theme.get_email_templates(template)
+        context = RequestContext(request, context)
+        context['author'] = context['user']
+        context['user'] = self
+        # Set message recipient
+        if settings.DEBUG:
+            recipient = settings.CATCH_ALL_EMAIL_ADDRESS
+        else:
+            recipient = self.email
+        # Build and send message
+        email = EmailMultiAlternatives(subject, templates[0].render(context), settings.EMAIL_HOST_USER, [recipient])
+        email.attach_alternative(templates[1].render(context), "text/html")
+        email.send()
+    
+    def get_date(self):
+        return self.join_date
+        
+        
+class Guest(object):
+    """
+    Misago Guest dummy
+    """
+    def is_admin(self):
+        return False
+    
+    def is_anonymous(self):
+        return True
+    
+    def is_authenticated(self):
+        return False
+    
+    def is_crawler(self):
+        return False
+        
+    def is_banned(self):
+        return False
+        
+        
+class Crawler(object): 
+    """
+    Misago Crawler dummy
+    """
+    def __init__(self, username):
+        self.username = username
+    
+    def is_admin(self):
+        return False
+    
+    def is_anonymous(self):
+        return True
+    
+    def is_authenticated(self):
+        return False
+    
+    def is_crawler(self):
+        return True
+        
+    def is_banned(self):
+        return False
+    
+    
+class Group(models.Model):
+    """
+    Misago Users Group model
+    """
+    name = models.CharField(max_length=255)
+    name_slug = models.SlugField(max_length=255)
+    hidden = models.BooleanField(default=False)
+    tab = models.CharField(max_length=255,null=True,blank=True)
+    position = models.IntegerField(default=0)
+    rank = models.ForeignKey('Rank',null=True,blank=True,db_index=True,on_delete=models.SET_NULL)
+    special = models.BooleanField(default=False)
+    
+    
+class Rank(models.Model):
+    """
+    Misago User Rank
+    Ranks are ready style/title pairs that are assigned to users either per group, admin action or user activity.
+    """
+    name = models.CharField(max_length=255,null=True,blank=True)
+    style = models.CharField(max_length=255,null=True,blank=True)
+    title = models.CharField(max_length=255,null=True,blank=True)
+    special = models.BooleanField(default=False)
+    order = models.IntegerField(default=0)
+    criteria = models.CharField(max_length=255,default='')
+    
+    
+class Follower(models.Model):
+    """
+    Misago Users follow model
+    """
+    user = models.ForeignKey('User',related_name='+')
+    target = models.ForeignKey('User')
+    since = models.DateTimeField()
+    
+    
+class Foe(models.Model):
+    """
+    Misago Users foes model
+    """
+    user = models.ForeignKey('User')
+    target = models.ForeignKey('User',related_name='+')
+    since = models.DateTimeField()
+    

+ 12 - 0
misago/users/urls.py

@@ -0,0 +1,12 @@
+from django.conf.urls import patterns, url, include
+
+urlpatterns = patterns('misago.users.views',
+    url(r'^users/$', 'users', name="users"),
+    url(r'^users/(?P<username>\w+)-(?P<user>\d+)/$', 'user_profile', name="user"),
+    url(r'^usercp/$', 'usercp_options', name="usercp"),
+    url(r'^usercp/credentials$', 'usercp_credentials', name="usercp_credentials"),
+    url(r'^usercp/username$', 'usercp_username', name="usercp_username"),
+    url(r'^usercp/avatar$', 'usercp_avatar', name="usercp_avatar"),
+    url(r'^usercp/signature$', 'usercp_signature', name="usercp_signature"),
+    url(r'^usercp/ignored$', 'usercp_ignored', name="usercp_ignored"),
+)

+ 48 - 0
misago/users/validators.py

@@ -0,0 +1,48 @@
+import re
+from django.core.exceptions import ValidationError
+from django.utils.translation import ungettext, ugettext_lazy as _
+from misago.banning.models import check_ban
+from misago.settings.settings import Settings
+
+def validate_username(value):
+    value = unicode(value).strip()
+    if len(value) < 3:
+        raise ValidationError(_("Username cannot be shorter than 3 characters."))
+    if len(value) > 12:
+        raise ValidationError(_("Username cannot be longer than 12 characters."))
+    if not re.search('^[0-9a-zA-Z]+$', value):
+        raise ValidationError(_("Username can only contain letters and digits."))
+    if check_ban(username=value):
+        raise ValidationError(_("This username is forbidden."))
+        
+
+def validate_password(value):
+    value = unicode(value).strip()
+    db_settings = Settings()
+    if len(value) < db_settings['password_length']:
+        raise ValidationError(ungettext(
+            'Correct password has to be at least one character long.',
+            'Correct password has to be at least %(count)d characters long.',
+            db_settings['password_length']
+        ) % {
+            'count': db_settings['password_length'],
+        })
+    for test in db_settings['password_complexity']:
+        if test in ('case', 'digits', 'special'):
+            if not re.search('[a-zA-Z]', value):
+                raise ValidationError(_("Password must contain alphabetical characters."))
+            if test == 'case':
+                if not (re.search('[a-z]', value) and re.search('[A-Z]', value)):
+                    raise ValidationError(_("Password must contain characters that have different case."))
+            if test == 'digits':
+                if not re.search('[0-9]', value):
+                    raise ValidationError(_("Password must contain digits in addition to characters."))
+            if test == 'special':
+                if not re.search('[^0-9a-zA-Z]', value):
+                    raise ValidationError(_("Password must contain special (non alphanumerical) characters."))
+
+
+def validate_email(value):
+    value = unicode(value).strip()
+    if check_ban(email=value):
+        raise ValidationError(_("This board forbids registrations using this e-mail address."))

+ 80 - 0
misago/users/views.py

@@ -0,0 +1,80 @@
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+from django.template import RequestContext
+from misago.security.decorators import *
+from misago.users.models import User, Group
+from misago.views import error403, error404
+
+
+def users(request):
+    pass
+
+
+def user_profile(request, user, username):
+    user = int(user)
+    try:
+        user = User.objects.get(pk=user)
+        if user.username_slug != username:
+            # Force crawlers to take notice of updated username
+            return redirect(reverse('user', args=(user.username_slug, user.pk)), permanent=True)
+        return request.theme.render_to_response('users/profile.html',
+                                            {
+                                             'profile': user,
+                                            },
+                                            context_instance=RequestContext(request));
+    except User.DoesNotExist:
+        return error404(request)
+
+
+@block_guest   
+def usercp_options(request):
+    return request.theme.render_to_response('users/usercp/options.html',
+                                            {
+                                             'tab': 'options',
+                                             },
+                                            context_instance=RequestContext(request));
+    
+ 
+@block_guest
+def usercp_credentials(request):
+    return request.theme.render_to_response('users/usercp/credentials.html',
+                                            {
+                                             'tab': 'credentials',
+                                             },
+                                            context_instance=RequestContext(request));
+    
+ 
+@block_guest
+def usercp_username(request):
+    return request.theme.render_to_response('users/usercp/username.html',
+                                            {
+                                             'tab': 'username',
+                                             },
+                                            context_instance=RequestContext(request));
+    
+ 
+@block_guest
+def usercp_avatar(request):
+    return request.theme.render_to_response('users/usercp/avatar.html',
+                                            {
+                                             'tab': 'avatar',
+                                             },
+                                            context_instance=RequestContext(request));
+    
+ 
+@block_guest
+def usercp_signature(request):
+    return request.theme.render_to_response('users/usercp/signature.html',
+                                            {
+                                             'tab': 'signature',
+                                             },
+                                            context_instance=RequestContext(request));
+    
+ 
+@block_guest
+def usercp_ignored(request):
+    return request.theme.render_to_response('users/usercp/ignored.html',
+                                            {
+                                             'tab': 'ignored',
+                                             },
+                                            context_instance=RequestContext(request));

+ 28 - 0
misago/utils/__init__.py

@@ -0,0 +1,28 @@
+"""
+Smart slugify
+"""
+import django.template.defaultfilters
+use_unidecode = True
+try:
+    from unidecode import unidecode
+except ImportError:
+    use_unidecode = False
+    
+def slugify(string):
+    if use_unidecode:
+        string = unidecode(string)
+    return django.template.defaultfilters.slugify(string)
+
+"""
+Lazy translate that allows us to access original message
+"""
+from django.utils import translation
+def ugettext_lazy(str):
+    t = translation.ugettext_lazy(str)
+    t.message = str
+    return t
+def get_msgid(gettext):
+    try:
+        return gettext.message
+    except AttributeError:
+        return None

+ 0 - 0
misago/utils/templatetags/__init__.py


+ 14 - 0
misago/utils/templatetags/misago_filters.py

@@ -0,0 +1,14 @@
+from django import template
+from django.template.defaultfilters import stringfilter
+from misago.utils import slugify
+
+register = template.Library()
+
+@register.filter(name="slugify", is_safe=True)
+@stringfilter
+def slugify_tag(format_string):
+    return slugify(format_string)
+
+@register.filter(name='klass', is_safe=True)
+def class_tag(obj):
+    return obj.__class__.__name__

+ 28 - 0
misago/views.py

@@ -0,0 +1,28 @@
+from django.template import RequestContext
+
+def home(request):
+    #if request.user.is_authenticated():
+    #    request.user.email_user('LOL Testowy e-mail z Django!', 'who', 'cares')
+    return request.theme.render_to_response('index.html',
+                                            {'page_title': 'Hello World!'},
+                                            context_instance=RequestContext(request));
+
+def error403(request, message=None, title=None):
+    return error_view(request, 403, message, title)
+                                            
+def error404(request, message=None, title=None):
+    return error_view(request, 404, message, title)
+
+def error_view(request, error, message, title):
+    if message:
+        message.single = True
+    response = request.theme.render_to_response(('error%s.html' % error),
+                                            {
+                                             'message': message,
+                                             'title': title,
+                                             'hide_signin': True,
+                                             'exception_response': True,
+                                             },
+                                            context_instance=RequestContext(request));
+    response.status_code = error
+    return response

+ 929 - 0
static/admin/css/admin.css

@@ -0,0 +1,929 @@
+@import "bootstrap-toggle-buttons.css";
+article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
+audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
+audio:not([controls]){display:none;}
+html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
+a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+a:hover,a:active{outline:0;}
+sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
+sup{top:-0.5em;}
+sub{bottom:-0.25em;}
+img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
+#map_canvas img{max-width:none;}
+button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
+button,input{*overflow:visible;line-height:normal;}
+button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
+button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
+input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;}
+input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
+textarea{overflow:auto;vertical-align:top;}
+.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0;}
+.clearfix:after{clear:both;}
+.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
+.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
+body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333333;background-color:#fcfcfc;}
+a{color:#0088cc;text-decoration:none;}
+a:hover{color:#00aaff;text-decoration:underline;}
+.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);}
+.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px;}
+.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;}
+.row:after{clear:both;}
+[class*="span"]{float:left;min-height:1px;margin-left:20px;}
+.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.span12{width:940px;}
+.span11{width:860px;}
+.span10{width:780px;}
+.span9{width:700px;}
+.span8{width:620px;}
+.span7{width:540px;}
+.span6{width:460px;}
+.span5{width:380px;}
+.span4{width:300px;}
+.span3{width:220px;}
+.span2{width:140px;}
+.span1{width:60px;}
+.offset12{margin-left:980px;}
+.offset11{margin-left:900px;}
+.offset10{margin-left:820px;}
+.offset9{margin-left:740px;}
+.offset8{margin-left:660px;}
+.offset7{margin-left:580px;}
+.offset6{margin-left:500px;}
+.offset5{margin-left:420px;}
+.offset4{margin-left:340px;}
+.offset3{margin-left:260px;}
+.offset2{margin-left:180px;}
+.offset1{margin-left:100px;}
+.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;}
+.row-fluid:after{clear:both;}
+.row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;}
+.row-fluid [class*="span"]:first-child{margin-left:0;}
+.row-fluid .span12{width:100%;*width:99.94680851063829%;}
+.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%;}
+.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%;}
+.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%;}
+.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%;}
+.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%;}
+.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%;}
+.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%;}
+.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%;}
+.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%;}
+.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%;}
+.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%;}
+.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%;}
+.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%;}
+.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%;}
+.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%;}
+.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%;}
+.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%;}
+.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%;}
+.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%;}
+.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%;}
+.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%;}
+.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%;}
+.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%;}
+.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%;}
+.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%;}
+.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%;}
+.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%;}
+.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%;}
+.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%;}
+.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%;}
+.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%;}
+.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%;}
+.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%;}
+.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%;}
+.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%;}
+[class*="span"].hide,.row-fluid [class*="span"].hide{display:none;}
+[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right;}
+.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";line-height:0;}
+.container:after{clear:both;}
+.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0;}
+.container-fluid:after{clear:both;}
+p{margin:0 0 10px;}
+.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px;}
+small{font-size:85%;}
+strong{font-weight:bold;}
+em{font-style:italic;}
+cite{font-style:normal;}
+.muted{color:#999999;}
+.text-warning{color:#c09853;}
+.text-error{color:#b94a48;}
+.text-info{color:#3a87ad;}
+.text-success{color:#468847;}
+h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:1;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999999;}
+h1{font-size:36px;line-height:40px;}
+h2{font-size:30px;line-height:40px;}
+h3{font-size:24px;line-height:40px;}
+h4{font-size:18px;line-height:20px;}
+h5{font-size:14px;line-height:20px;}
+h6{font-size:12px;line-height:20px;}
+h1 small{font-size:24px;}
+h2 small{font-size:18px;}
+h3 small{font-size:14px;}
+h4 small{font-size:14px;}
+.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eeeeee;}
+ul,ol{padding:0;margin:0 0 10px 25px;}
+ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
+li{line-height:20px;}
+ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
+dl{margin-bottom:20px;}
+dt,dd{line-height:20px;}
+dt{font-weight:bold;}
+dd{margin-left:10px;}
+.dl-horizontal{*zoom:1;}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0;}
+.dl-horizontal:after{clear:both;}
+.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
+.dl-horizontal dd{margin-left:180px;}
+hr{margin:20px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
+abbr[title]{cursor:help;border-bottom:1px dotted #999999;}
+abbr.initialism{font-size:90%;text-transform:uppercase;}
+blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px;}
+blockquote small{display:block;line-height:20px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
+blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
+blockquote.pull-right small:before{content:'';}
+blockquote.pull-right small:after{content:'\00A0 \2014';}
+q:before,q:after,blockquote:before,blockquote:after{content:"";}
+address{display:block;margin-bottom:20px;font-style:normal;line-height:20px;}
+code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
+pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;}
+pre code{padding:0;color:inherit;background-color:transparent;border:0;}
+.pre-scrollable{max-height:340px;overflow-y:scroll;}
+form{margin:0 0 20px;}
+fieldset{padding:0;margin:0;border:0;}
+legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;}
+label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px;}
+input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
+label{display:block;margin-bottom:5px;}
+select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:9px;font-size:14px;line-height:20px;color:#555555;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+input,textarea,.uneditable-input{width:206px;}
+textarea{height:auto;}
+textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear .2s, box-shadow linear .2s;-moz-transition:border linear .2s, box-shadow linear .2s;-o-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
+input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal;cursor:pointer;}
+input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto;}
+select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px;}
+select{width:220px;border:1px solid #cccccc;background-color:#ffffff;}
+select[multiple],select[size]{height:auto;}
+select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+.uneditable-input,.uneditable-textarea{color:#999999;background-color:#fcfcfc;border-color:#cccccc;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
+.uneditable-input{overflow:hidden;white-space:nowrap;}
+.uneditable-textarea{width:auto;height:auto;}
+input:-moz-placeholder,textarea:-moz-placeholder{color:#999999;}
+input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999999;}
+input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999999;}
+.radio,.checkbox{min-height:18px;padding-left:18px;}
+.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
+.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
+.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
+.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
+.input-mini{width:60px;}
+.input-small{width:90px;}
+.input-medium{width:150px;}
+.input-large{width:210px;}
+.input-xlarge{width:270px;}
+.input-xxlarge{width:530px;}
+input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;}
+.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block;}
+input,textarea,.uneditable-input{margin-left:0;}
+.controls-row [class*="span"]+[class*="span"]{margin-left:20px;}
+input.span12, textarea.span12, .uneditable-input.span12{width:926px;}
+input.span11, textarea.span11, .uneditable-input.span11{width:846px;}
+input.span10, textarea.span10, .uneditable-input.span10{width:766px;}
+input.span9, textarea.span9, .uneditable-input.span9{width:686px;}
+input.span8, textarea.span8, .uneditable-input.span8{width:606px;}
+input.span7, textarea.span7, .uneditable-input.span7{width:526px;}
+input.span6, textarea.span6, .uneditable-input.span6{width:446px;}
+input.span5, textarea.span5, .uneditable-input.span5{width:366px;}
+input.span4, textarea.span4, .uneditable-input.span4{width:286px;}
+input.span3, textarea.span3, .uneditable-input.span3{width:206px;}
+input.span2, textarea.span2, .uneditable-input.span2{width:126px;}
+input.span1, textarea.span1, .uneditable-input.span1{width:46px;}
+.controls-row{*zoom:1;}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0;}
+.controls-row:after{clear:both;}
+.controls-row [class*="span"]{float:left;}
+input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;}
+input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;}
+.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
+.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;}
+.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;}
+.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
+.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
+.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;}
+.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;}
+.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
+.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
+.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;}
+.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;}
+.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
+.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad;}
+.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad;}
+.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;}
+.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad;}
+input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
+.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";line-height:0;}
+.form-actions:after{clear:both;}
+.help-block,.help-inline{color:#595959;}
+.help-block{display:block;margin-bottom:10px;}
+.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
+.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap;}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;font-size:14px;vertical-align:top;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2;}
+.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#eeeeee;border:1px solid #ccc;}
+.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546;}
+.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
+.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-append .add-on,.input-append .btn{margin-left:-1px;}
+.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
+.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
+.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
+.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
+.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;vertical-align:middle;}
+.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
+.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block;}
+.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
+.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
+.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;}
+.control-group{margin-bottom:10px;}
+legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate;}
+.form-horizontal .control-group{margin-bottom:20px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";line-height:0;}
+.form-horizontal .control-group:after{clear:both;}
+.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right;}
+.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0;}.form-horizontal .controls:first-child{*padding-left:180px;}
+.form-horizontal .help-block{margin-bottom:0;}
+.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px;}
+.form-horizontal .form-actions{padding-left:180px;}
+table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}
+.table{width:100%;margin-bottom:20px;}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
+.table th{font-weight:bold;}
+.table thead th{vertical-align:bottom;}
+.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
+.table tbody+tbody{border-top:2px solid #dddddd;}
+.table-condensed th,.table-condensed td{padding:4px 5px;}
+.table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
+.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
+.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;}
+.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px;}
+.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;}
+.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;}
+.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;}
+.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topleft:4px;}
+.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f7f7f7;}
+.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f2f2f2;}
+table [class*=span],.row-fluid table [class*=span]{display:table-cell;float:none;margin-left:0;}
+.table .span1{float:none;width:44px;margin-left:0;}
+.table .span2{float:none;width:124px;margin-left:0;}
+.table .span3{float:none;width:204px;margin-left:0;}
+.table .span4{float:none;width:284px;margin-left:0;}
+.table .span5{float:none;width:364px;margin-left:0;}
+.table .span6{float:none;width:444px;margin-left:0;}
+.table .span7{float:none;width:524px;margin-left:0;}
+.table .span8{float:none;width:604px;margin-left:0;}
+.table .span9{float:none;width:684px;margin-left:0;}
+.table .span10{float:none;width:764px;margin-left:0;}
+.table .span11{float:none;width:844px;margin-left:0;}
+.table .span12{float:none;width:924px;margin-left:0;}
+.table .span13{float:none;width:1004px;margin-left:0;}
+.table .span14{float:none;width:1084px;margin-left:0;}
+.table .span15{float:none;width:1164px;margin-left:0;}
+.table .span16{float:none;width:1244px;margin-left:0;}
+.table .span17{float:none;width:1324px;margin-left:0;}
+.table .span18{float:none;width:1404px;margin-left:0;}
+.table .span19{float:none;width:1484px;margin-left:0;}
+.table .span20{float:none;width:1564px;margin-left:0;}
+.table .span21{float:none;width:1644px;margin-left:0;}
+.table .span22{float:none;width:1724px;margin-left:0;}
+.table .span23{float:none;width:1804px;margin-left:0;}
+.table .span24{float:none;width:1884px;margin-left:0;}
+.table tbody tr.success td{background-color:#dff0d8;}
+.table tbody tr.error td{background-color:#f2dede;}
+.table tbody tr.warning td{background-color:#fcf8e3;}
+.table tbody tr.info td{background-color:#d9edf7;}
+.table-hover tbody tr.success:hover td{background-color:#d0e9c6;}
+.table-hover tbody tr.error:hover td{background-color:#ebcccc;}
+.table-hover tbody tr.warning:hover td{background-color:#faf2cc;}
+.table-hover tbody tr.info:hover td{background-color:#c4e3f3;}
+[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px;}
+.icon-white,.nav-tabs>.active>a>[class^="icon-"],.nav-tabs>.active>a>[class*=" icon-"],.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png");}
+.icon-glass{background-position:0 0;}
+.icon-music{background-position:-24px 0;}
+.icon-search{background-position:-48px 0;}
+.icon-envelope{background-position:-72px 0;}
+.icon-heart{background-position:-96px 0;}
+.icon-star{background-position:-120px 0;}
+.icon-star-empty{background-position:-144px 0;}
+.icon-user{background-position:-168px 0;}
+.icon-film{background-position:-192px 0;}
+.icon-th-large{background-position:-216px 0;}
+.icon-th{background-position:-240px 0;}
+.icon-th-list{background-position:-264px 0;}
+.icon-ok{background-position:-288px 0;}
+.icon-remove{background-position:-312px 0;}
+.icon-zoom-in{background-position:-336px 0;}
+.icon-zoom-out{background-position:-360px 0;}
+.icon-off{background-position:-384px 0;}
+.icon-signal{background-position:-408px 0;}
+.icon-cog{background-position:-432px 0;}
+.icon-trash{background-position:-456px 0;}
+.icon-home{background-position:0 -24px;}
+.icon-file{background-position:-24px -24px;}
+.icon-time{background-position:-48px -24px;}
+.icon-road{background-position:-72px -24px;}
+.icon-download-alt{background-position:-96px -24px;}
+.icon-download{background-position:-120px -24px;}
+.icon-upload{background-position:-144px -24px;}
+.icon-inbox{background-position:-168px -24px;}
+.icon-play-circle{background-position:-192px -24px;}
+.icon-repeat{background-position:-216px -24px;}
+.icon-refresh{background-position:-240px -24px;}
+.icon-list-alt{background-position:-264px -24px;}
+.icon-lock{background-position:-287px -24px;}
+.icon-flag{background-position:-312px -24px;}
+.icon-headphones{background-position:-336px -24px;}
+.icon-volume-off{background-position:-360px -24px;}
+.icon-volume-down{background-position:-384px -24px;}
+.icon-volume-up{background-position:-408px -24px;}
+.icon-qrcode{background-position:-432px -24px;}
+.icon-barcode{background-position:-456px -24px;}
+.icon-tag{background-position:0 -48px;}
+.icon-tags{background-position:-25px -48px;}
+.icon-book{background-position:-48px -48px;}
+.icon-bookmark{background-position:-72px -48px;}
+.icon-print{background-position:-96px -48px;}
+.icon-camera{background-position:-120px -48px;}
+.icon-font{background-position:-144px -48px;}
+.icon-bold{background-position:-167px -48px;}
+.icon-italic{background-position:-192px -48px;}
+.icon-text-height{background-position:-216px -48px;}
+.icon-text-width{background-position:-240px -48px;}
+.icon-align-left{background-position:-264px -48px;}
+.icon-align-center{background-position:-288px -48px;}
+.icon-align-right{background-position:-312px -48px;}
+.icon-align-justify{background-position:-336px -48px;}
+.icon-list{background-position:-360px -48px;}
+.icon-indent-left{background-position:-384px -48px;}
+.icon-indent-right{background-position:-408px -48px;}
+.icon-facetime-video{background-position:-432px -48px;}
+.icon-picture{background-position:-456px -48px;}
+.icon-pencil{background-position:0 -72px;}
+.icon-map-marker{background-position:-24px -72px;}
+.icon-adjust{background-position:-48px -72px;}
+.icon-tint{background-position:-72px -72px;}
+.icon-edit{background-position:-96px -72px;}
+.icon-share{background-position:-120px -72px;}
+.icon-check{background-position:-144px -72px;}
+.icon-move{background-position:-168px -72px;}
+.icon-step-backward{background-position:-192px -72px;}
+.icon-fast-backward{background-position:-216px -72px;}
+.icon-backward{background-position:-240px -72px;}
+.icon-play{background-position:-264px -72px;}
+.icon-pause{background-position:-288px -72px;}
+.icon-stop{background-position:-312px -72px;}
+.icon-forward{background-position:-336px -72px;}
+.icon-fast-forward{background-position:-360px -72px;}
+.icon-step-forward{background-position:-384px -72px;}
+.icon-eject{background-position:-408px -72px;}
+.icon-chevron-left{background-position:-432px -72px;}
+.icon-chevron-right{background-position:-456px -72px;}
+.icon-plus-sign{background-position:0 -96px;}
+.icon-minus-sign{background-position:-24px -96px;}
+.icon-remove-sign{background-position:-48px -96px;}
+.icon-ok-sign{background-position:-72px -96px;}
+.icon-question-sign{background-position:-96px -96px;}
+.icon-info-sign{background-position:-120px -96px;}
+.icon-screenshot{background-position:-144px -96px;}
+.icon-remove-circle{background-position:-168px -96px;}
+.icon-ok-circle{background-position:-192px -96px;}
+.icon-ban-circle{background-position:-216px -96px;}
+.icon-arrow-left{background-position:-240px -96px;}
+.icon-arrow-right{background-position:-264px -96px;}
+.icon-arrow-up{background-position:-289px -96px;}
+.icon-arrow-down{background-position:-312px -96px;}
+.icon-share-alt{background-position:-336px -96px;}
+.icon-resize-full{background-position:-360px -96px;}
+.icon-resize-small{background-position:-384px -96px;}
+.icon-plus{background-position:-408px -96px;}
+.icon-minus{background-position:-433px -96px;}
+.icon-asterisk{background-position:-456px -96px;}
+.icon-exclamation-sign{background-position:0 -120px;}
+.icon-gift{background-position:-24px -120px;}
+.icon-leaf{background-position:-48px -120px;}
+.icon-fire{background-position:-72px -120px;}
+.icon-eye-open{background-position:-96px -120px;}
+.icon-eye-close{background-position:-120px -120px;}
+.icon-warning-sign{background-position:-144px -120px;}
+.icon-plane{background-position:-168px -120px;}
+.icon-calendar{background-position:-192px -120px;}
+.icon-random{background-position:-216px -120px;width:16px;}
+.icon-comment{background-position:-240px -120px;}
+.icon-magnet{background-position:-264px -120px;}
+.icon-chevron-up{background-position:-288px -120px;}
+.icon-chevron-down{background-position:-313px -119px;}
+.icon-retweet{background-position:-336px -120px;}
+.icon-shopping-cart{background-position:-360px -120px;}
+.icon-folder-close{background-position:-384px -120px;}
+.icon-folder-open{background-position:-408px -120px;width:16px;}
+.icon-resize-vertical{background-position:-432px -119px;}
+.icon-resize-horizontal{background-position:-456px -118px;}
+.icon-hdd{background-position:0 -144px;}
+.icon-bullhorn{background-position:-24px -144px;}
+.icon-bell{background-position:-48px -144px;}
+.icon-certificate{background-position:-72px -144px;}
+.icon-thumbs-up{background-position:-96px -144px;}
+.icon-thumbs-down{background-position:-120px -144px;}
+.icon-hand-right{background-position:-144px -144px;}
+.icon-hand-left{background-position:-168px -144px;}
+.icon-hand-up{background-position:-192px -144px;}
+.icon-hand-down{background-position:-216px -144px;}
+.icon-circle-arrow-right{background-position:-240px -144px;}
+.icon-circle-arrow-left{background-position:-264px -144px;}
+.icon-circle-arrow-up{background-position:-288px -144px;}
+.icon-circle-arrow-down{background-position:-312px -144px;}
+.icon-globe{background-position:-336px -144px;}
+.icon-wrench{background-position:-360px -144px;}
+.icon-tasks{background-position:-384px -144px;}
+.icon-filter{background-position:-408px -144px;}
+.icon-briefcase{background-position:-432px -144px;}
+.icon-fullscreen{background-position:-456px -144px;}
+.dropup,.dropdown{position:relative;}
+.dropdown-toggle{*margin-bottom:-3px;}
+.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
+.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";}
+.dropdown .caret{margin-top:8px;margin-left:2px;}
+.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.dropdown-menu.pull-right{right:0;left:auto;}
+.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
+.dropdown-menu a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333333;white-space:nowrap;}
+.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{text-decoration:none;color:#ffffff;background-color:#0088cc;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);}
+.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;outline:0;background-color:#0088cc;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);}
+.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999999;}
+.dropdown-menu .disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;}
+.open{*z-index:1000;}.open >.dropdown-menu{display:block;}
+.pull-right>.dropdown-menu{right:0;left:auto;}
+.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"";}
+.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
+.dropdown-submenu{position:relative;}
+.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
+.dropdown-submenu:hover>.dropdown-menu{display:block;}
+.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:#cccccc;margin-top:5px;margin-right:-10px;}
+.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
+.dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px;}
+.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
+.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}.fade.in{opacity:1;}
+.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}.collapse.in{height:auto;}
+.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);}
+button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;}
+.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 14px;margin-bottom:0;font-size:14px;line-height:20px;*line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333333;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#dedede;background-image:-moz-linear-gradient(top, #dcdcdc, #e1e1e1);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#dcdcdc), to(#e1e1e1));background-image:-webkit-linear-gradient(top, #dcdcdc, #e1e1e1);background-image:-o-linear-gradient(top, #dcdcdc, #e1e1e1);background-image:linear-gradient(to bottom, #dcdcdc, #e1e1e1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdcdcdc', endColorstr='#ffe1e1e1', GradientType=0);border-color:#e1e1e1 #e1e1e1 #bbbbbb;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e1e1e1;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #cccccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333333;background-color:#e1e1e1;*background-color:#d4d4d4;}
+.btn:active,.btn.active{background-color:#c8c8c8 \9;}
+.btn:first-child{*margin-left:0;}
+.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
+.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.btn-large{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.btn-large [class^="icon-"]{margin-top:2px;}
+.btn-small{padding:3px 9px;font-size:12px;line-height:18px;}
+.btn-small [class^="icon-"]{margin-top:0;}
+.btn-mini{padding:2px 6px;font-size:11px;line-height:17px;}
+.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
+.btn-block+.btn-block{margin-top:5px;}
+input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%;}
+.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
+.btn{border-color:#c5c5c5;border-color:rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);}
+.btn-primary{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0093dc;background-image:-moz-linear-gradient(top, #0088cc, #00a3f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#00a3f5));background-image:-webkit-linear-gradient(top, #0088cc, #00a3f5);background-image:-o-linear-gradient(top, #0088cc, #00a3f5);background-image:linear-gradient(to bottom, #0088cc, #00a3f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff00a3f5', GradientType=0);border-color:#00a3f5 #00a3f5 #0070a8;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#00a3f5;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#ffffff;background-color:#00a3f5;*background-color:#0092db;}
+.btn-primary:active,.btn-primary.active{background-color:#0081c2 \9;}
+.btn-warning{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#f39919;background-image:-moz-linear-gradient(top, #ee8e06, #faa937);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee8e06), to(#faa937));background-image:-webkit-linear-gradient(top, #ee8e06, #faa937);background-image:-o-linear-gradient(top, #ee8e06, #faa937);background-image:linear-gradient(to bottom, #ee8e06, #faa937);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee8e06', endColorstr='#fffaa937', GradientType=0);border-color:#faa937 #faa937 #df8505;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#faa937;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#ffffff;background-color:#faa937;*background-color:#fa9f1e;}
+.btn-warning:active,.btn-warning.active{background-color:#f89406 \9;}
+.btn-danger{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#d74439;background-image:-moz-linear-gradient(top, #d13327, #e05f55);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#d13327), to(#e05f55));background-image:-webkit-linear-gradient(top, #d13327, #e05f55);background-image:-o-linear-gradient(top, #d13327, #e05f55);background-image:linear-gradient(to bottom, #d13327, #e05f55);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd13327', endColorstr='#ffe05f55', GradientType=0);border-color:#e05f55 #e05f55 #c42f24;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e05f55;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#ffffff;background-color:#e05f55;*background-color:#dc4a3f;}
+.btn-danger:active,.btn-danger.active{background-color:#d8362a \9;}
+.btn-success{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#54b054;background-image:-moz-linear-gradient(top, #46a546, #69bf69);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#46a546), to(#69bf69));background-image:-webkit-linear-gradient(top, #46a546, #69bf69);background-image:-o-linear-gradient(top, #46a546, #69bf69);background-image:linear-gradient(to bottom, #46a546, #69bf69);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff46a546', endColorstr='#ff69bf69', GradientType=0);border-color:#69bf69 #69bf69 #419a41;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#69bf69;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#ffffff;background-color:#69bf69;*background-color:#57b857;}
+.btn-success:active,.btn-success.active{background-color:#49ac49 \9;}
+.btn-info{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#5d8aa1;background-image:-moz-linear-gradient(top, #507e95, #709cb2);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#507e95), to(#709cb2));background-image:-webkit-linear-gradient(top, #507e95, #709cb2);background-image:-o-linear-gradient(top, #507e95, #709cb2);background-image:linear-gradient(to bottom, #507e95, #709cb2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff507e95', endColorstr='#ff709cb2', GradientType=0);border-color:#709cb2 #709cb2 #4b768b;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#709cb2;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#ffffff;background-color:#709cb2;*background-color:#6091a9;}
+.btn-info:active,.btn-info.active{background-color:#54849c \9;}
+.btn-inverse{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#656565;background-image:-moz-linear-gradient(top, #555555, #7e7e7e);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#7e7e7e));background-image:-webkit-linear-gradient(top, #555555, #7e7e7e);background-image:-o-linear-gradient(top, #555555, #7e7e7e);background-image:linear-gradient(to bottom, #555555, #7e7e7e);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff555555', endColorstr='#ff7e7e7e', GradientType=0);border-color:#7e7e7e #7e7e7e #585858;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#7e7e7e;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#ffffff;background-color:#7e7e7e;*background-color:#717171;}
+.btn-inverse:active,.btn-inverse.active{background-color:#646464 \9;}
+button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
+button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
+button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
+button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
+.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.btn-link{border-color:transparent;cursor:pointer;color:#0088cc;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-link:hover{color:#00aaff;text-decoration:underline;background-color:transparent;}
+.btn-link[disabled]:hover{color:#333333;text-decoration:none;}
+.btn-group{position:relative;font-size:0;vertical-align:middle;white-space:nowrap;*margin-left:.3em;}.btn-group:first-child{*margin-left:0;}
+.btn-group+.btn-group{margin-left:5px;}
+.btn-toolbar{font-size:0;margin-top:10px;margin-bottom:10px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
+.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px;}
+.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-group>.btn+.btn{margin-left:-1px;}
+.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px;}
+.btn-group>.btn-mini{font-size:11px;}
+.btn-group>.btn-small{font-size:12px;}
+.btn-group>.btn-large{font-size:16px;}
+.btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
+.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
+.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
+.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
+.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;}
+.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
+.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 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 .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn-group.open .btn.dropdown-toggle{background-color:#e1e1e1;}
+.btn-group.open .btn-primary.dropdown-toggle{background-color:#00a3f5;}
+.btn-group.open .btn-warning.dropdown-toggle{background-color:#faa937;}
+.btn-group.open .btn-danger.dropdown-toggle{background-color:#e05f55;}
+.btn-group.open .btn-success.dropdown-toggle{background-color:#69bf69;}
+.btn-group.open .btn-info.dropdown-toggle{background-color:#709cb2;}
+.btn-group.open .btn-inverse.dropdown-toggle{background-color:#7e7e7e;}
+.btn .caret{margin-top:8px;margin-left:0;}
+.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px;}
+.btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px;}
+.dropup .btn-large .caret{border-bottom:5px solid #000000;border-top:0;}
+.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.btn-group-vertical{display:inline-block;*display:inline;*zoom:1;}
+.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-group-vertical .btn+.btn{margin-left:0;margin-top:-1px;}
+.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
+.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
+.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;}
+.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
+.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
+.alert h4{margin:0;}
+.alert .close{position:relative;top:-2px;right:-21px;line-height:20px;}
+.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
+.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
+.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
+.alert-block{padding-top:14px;padding-bottom:14px;}
+.alert-block>p,.alert-block>ul{margin-bottom:0;}
+.alert-block p+p{margin-top:5px;}
+.nav{margin-left:0;margin-bottom:20px;list-style:none;}
+.nav>li>a{display:block;}
+.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
+.nav>.pull-right{float:right;}
+.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
+.nav li+.nav-header{margin-top:9px;}
+.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
+.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
+.nav-list>li>a{padding:3px 15px;}
+.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
+.nav-list [class^="icon-"]{margin-right:2px;}
+.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
+.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0;}
+.nav-tabs:after,.nav-pills:after{clear:both;}
+.nav-tabs>li,.nav-pills>li{float:left;}
+.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
+.nav-tabs{border-bottom:1px solid #ddd;}
+.nav-tabs>li{margin-bottom:-1px;}
+.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
+.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
+.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
+.nav-stacked>li{float:none;}
+.nav-stacked>li>a{margin-right:0;}
+.nav-tabs.nav-stacked{border-bottom:0;}
+.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
+.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
+.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
+.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
+.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
+.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
+.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.nav .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
+.nav .dropdown-toggle:hover .caret{border-top-color:#00aaff;border-bottom-color:#00aaff;}
+.nav-tabs .dropdown-toggle .caret{margin-top:8px;}
+.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff;}
+.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;}
+.nav>.dropdown.active>a:hover{cursor:pointer;}
+.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
+.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
+.tabs-stacked .open>a:hover{border-color:#999999;}
+.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0;}
+.tabbable:after{clear:both;}
+.tab-content{overflow:auto;}
+.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;}
+.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
+.tab-content>.active,.pill-content>.active{display:block;}
+.tabs-below>.nav-tabs{border-top:1px solid #ddd;}
+.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;}
+.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below>.nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
+.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;}
+.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;}
+.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
+.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
+.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
+.tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
+.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
+.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
+.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
+.tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
+.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
+.nav>.disabled>a{color:#999999;}
+.nav>.disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;}
+.navbar{overflow:visible;margin-bottom:20px;color:#222222;*position:relative;*z-index:2;}
+.navbar-inner{min-height:64px;padding-left:20px;padding-right:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top, #ffffff, #f2f2f2);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));background-image:-webkit-linear-gradient(top, #ffffff, #f2f2f2);background-image:-o-linear-gradient(top, #ffffff, #f2f2f2);background-image:linear-gradient(to bottom, #ffffff, #f2f2f2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);border:1px solid #ffffff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;}
+.navbar-inner:after{clear:both;}
+.navbar .container{width:auto;}
+.nav-collapse.collapse{height:auto;}
+.navbar .brand{float:left;display:block;padding:22px 20px 22px;margin-left:-20px;font-size:20px;font-weight:200;color:#222222;text-shadow:0 1px 0 #ffffff;}.navbar .brand:hover{text-decoration:none;}
+.navbar-text{margin-bottom:0;line-height:64px;}
+.navbar-link{color:#222222;}.navbar-link:hover{color:#333333;}
+.navbar .divider-vertical{height:64px;margin:0 9px;border-left:1px solid #f2f2f2;border-right:1px solid #ffffff;}
+.navbar .btn,.navbar .btn-group{margin-top:17px;}
+.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0;}
+.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0;}
+.navbar-form:after{clear:both;}
+.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:17px;}
+.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0;}
+.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
+.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
+.navbar-search{position:relative;float:left;margin-top:17px;margin-bottom:0;}.navbar-search .search-query{margin-bottom:0;padding:4px 14px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.navbar-static-top{position:static;width:100%;margin-bottom:0;}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
+.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px;}
+.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0;}
+.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.navbar-fixed-top{top:0;}
+.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);}
+.navbar-fixed-bottom{bottom:0;}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);}
+.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
+.navbar .nav.pull-right{float:right;margin-right:0;}
+.navbar .nav>li{float:left;}
+.navbar .nav>li>a{float:none;padding:22px 15px 22px;color:#222222;text-decoration:none;text-shadow:0 1px 0 #ffffff;}
+.navbar .nav .dropdown-toggle .caret{margin-top:8px;}
+.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:#e8e8e8;color:#333333;text-decoration:none;}
+.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#0088cc;-webkit-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);-moz-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);}
+.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#ededed;background-image:-moz-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));background-image:-webkit-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-o-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:linear-gradient(to bottom, #f2f2f2, #e5e5e5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e5e5e5;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#ffffff;background-color:#e5e5e5;*background-color:#d9d9d9;}
+.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#cccccc \9;}
+.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
+.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
+.navbar .nav>li>.dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
+.navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
+.navbar-fixed-bottom .nav>li>.dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
+.navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
+.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:#0088cc;color:#ffffff;}
+.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#222222;border-bottom-color:#222222;}
+.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{left:auto;right:0;}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{left:auto;right:12px;}
+.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px;}
+.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{left:auto;right:100%;margin-left:0;margin-right:-1px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
+.navbar-inverse{color:#333333;}.navbar-inverse .navbar-inner{background-color:#ededed;background-image:-moz-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));background-image:-webkit-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-o-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:linear-gradient(to bottom, #f2f2f2, #e5e5e5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);border-color:#cccccc;}
+.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999999;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#ffffff;}
+.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:#ebebeb;color:#ffffff;}
+.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#ffffff;background-color:#f2f2f2;}
+.navbar-inverse .navbar-link{color:#999999;}.navbar-inverse .navbar-link:hover{color:#ffffff;}
+.navbar-inverse .divider-vertical{border-left-color:#e5e5e5;border-right-color:#f2f2f2;}
+.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{background-color:#f2f2f2;color:#ffffff;}
+.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999999;border-bottom-color:#999999;}
+.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.navbar-inverse .navbar-search .search-query{color:#ffffff;background-color:#ffffff;border-color:#e5e5e5;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none;}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#cccccc;}
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#cccccc;}
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
+.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
+.navbar-inverse .btn-navbar{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#e0e0e0;background-image:-moz-linear-gradient(top, #e5e5e5, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e5e5e5), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #e5e5e5, #d9d9d9);background-image:-o-linear-gradient(top, #e5e5e5, #d9d9d9);background-image:linear-gradient(to bottom, #e5e5e5, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffd9d9d9', GradientType=0);border-color:#d9d9d9 #d9d9d9 #b2b2b2;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#d9d9d9;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#ffffff;background-color:#d9d9d9;*background-color:#cccccc;}
+.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#bfbfbf \9;}
+.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}
+.breadcrumb .divider{padding:0 5px;color:#ccc;}
+.breadcrumb .active{color:#999999;}
+.pagination{height:40px;margin:20px 0;}
+.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
+.pagination ul>li{display:inline;}
+.pagination ul>li>a,.pagination ul>li>span{float:left;padding:0 14px;line-height:38px;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0;}
+.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5;}
+.pagination ul>.active>a,.pagination ul>.active>span{color:#999999;cursor:default;}
+.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999999;background-color:transparent;cursor:default;}
+.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.pagination-centered{text-align:center;}
+.pagination-right{text-align:right;}
+.pager{margin:20px 0;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";line-height:0;}
+.pager:after{clear:both;}
+.pager li{display:inline;}
+.pager a,.pager span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
+.pager .next a,.pager .next span{float:right;}
+.pager .previous a{float:left;}
+.pager .disabled a,.pager .disabled a:hover,.pager .disabled span{color:#999999;background-color:#fff;cursor:default;}
+.modal-open .modal .dropdown-menu{z-index:2050;}
+.modal-open .modal .dropdown.open{*z-index:2050;}
+.modal-open .modal .popover{z-index:2060;}
+.modal-open .modal .tooltip{z-index:2080;}
+.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
+.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
+.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
+.modal.fade.in{top:50%;}
+.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
+.modal-header h3{margin:0;line-height:30px;}
+.modal-body{overflow-y:auto;max-height:400px;padding:15px;}
+.modal-form{margin-bottom:0;}
+.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0;}
+.modal-footer:after{clear:both;}
+.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
+.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
+.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
+.tooltip.top{margin-top:-3px;}
+.tooltip.right{margin-left:3px;}
+.tooltip.bottom{margin-top:3px;}
+.tooltip.left{margin-left:-3px;}
+.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid;}
+.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000;}
+.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000;}
+.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000;}
+.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000;}
+.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#ffffff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);}.popover.top{margin-bottom:10px;}
+.popover.right{margin-left:10px;}
+.popover.bottom{margin-top:10px;}
+.popover.left{margin-right:10px;}
+.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;}
+.popover-content{padding:9px 14px;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
+.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid;}
+.popover .arrow:after{content:"";z-index:-1;}
+.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-width:10px 10px 0;border-top-color:#ffffff;}.popover.top .arrow:after{border-width:11px 11px 0;border-top-color:rgba(0, 0, 0, 0.25);bottom:-1px;left:-11px;}
+.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-width:10px 10px 10px 0;border-right-color:#ffffff;}.popover.right .arrow:after{border-width:11px 11px 11px 0;border-right-color:rgba(0, 0, 0, 0.25);bottom:-11px;left:-1px;}
+.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-width:0 10px 10px;border-bottom-color:#ffffff;}.popover.bottom .arrow:after{border-width:0 11px 11px;border-bottom-color:rgba(0, 0, 0, 0.25);top:-1px;left:-11px;}
+.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-width:10px 0 10px 10px;border-left-color:#ffffff;}.popover.left .arrow:after{border-width:11px 0 11px 11px;border-left-color:rgba(0, 0, 0, 0.25);bottom:-11px;right:-1px;}
+.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0;}
+.thumbnails:after{clear:both;}
+.row-fluid .thumbnails{margin-left:0;}
+.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px;}
+.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;}
+a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
+.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
+.thumbnail .caption{padding:9px;color:#555555;}
+.label,.badge{font-size:11.844px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;}
+.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
+a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
+.label-important,.badge-important{background-color:#b94a48;}
+.label-important[href],.badge-important[href]{background-color:#953b39;}
+.label-warning,.badge-warning{background-color:#f89406;}
+.label-warning[href],.badge-warning[href]{background-color:#c67605;}
+.label-success,.badge-success{background-color:#468847;}
+.label-success[href],.badge-success[href]{background-color:#356635;}
+.label-info,.badge-info{background-color:#3a87ad;}
+.label-info[href],.badge-info[href]{background-color:#2d6987;}
+.label-inverse,.badge-inverse{background-color:#333333;}
+.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}
+.btn .label,.btn .badge{position:relative;top:-1px;}
+.btn-mini .label,.btn-mini .badge{top:0;}
+@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(to bottom, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.progress .bar{width:0%;height:100%;color:#ffffff;float:left;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(to bottom, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
+.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);}
+.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
+.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
+.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(to bottom, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);}
+.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(to bottom, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);}
+.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(to bottom, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);}
+.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);}
+.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.accordion{margin-bottom:20px;}
+.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.accordion-heading{border-bottom:0;}
+.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
+.accordion-toggle{cursor:pointer;}
+.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
+.carousel{position:relative;margin-bottom:20px;line-height:1;}
+.carousel-inner{overflow:hidden;width:100%;position:relative;}
+.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
+.carousel .item>img{display:block;line-height:1;}
+.carousel .active,.carousel .next,.carousel .prev{display:block;}
+.carousel .active{left:0;}
+.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
+.carousel .next{left:100%;}
+.carousel .prev{left:-100%;}
+.carousel .next.left,.carousel .prev.right{left:0;}
+.carousel .active.left{left:-100%;}
+.carousel .active.right{left:100%;}
+.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:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
+.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
+.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333333;background:rgba(0, 0, 0, 0.75);}
+.carousel-caption h4,.carousel-caption p{color:#ffffff;line-height:20px;}
+.carousel-caption h4{margin:0 0 5px;}
+.carousel-caption p{margin-bottom:0;}
+.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
+.hero-unit p{font-size:18px;font-weight:200;line-height:30px;color:inherit;}
+.pull-right{float:right;}
+.pull-left{float:left;}
+.hide{display:none;}
+.show{display:block;}
+.invisible{visibility:hidden;}
+.affix{position:fixed;}
+@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:20px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%;} .row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%;} .row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%;} .row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%;} .row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%;} .row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%;} .row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%;} .row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%;} .row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%;} .row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%;} .row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%;} .row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%;} .row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%;} .row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%;} .row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%;} .row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%;} .row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%;} .row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%;} .row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%;} .row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%;} .row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%;} .row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%;} .row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%;} .row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%;} .row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%;} .row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%;} .row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%;} .row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%;} .row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%;} .row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%;} .row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%;} .row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%;} .row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%;} .row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%;} .row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:20px;} input.span12, textarea.span12, .uneditable-input.span12{width:710px;} input.span11, textarea.span11, .uneditable-input.span11{width:648px;} input.span10, textarea.span10, .uneditable-input.span10{width:586px;} input.span9, textarea.span9, .uneditable-input.span9{width:524px;} input.span8, textarea.span8, .uneditable-input.span8{width:462px;} input.span7, textarea.span7, .uneditable-input.span7{width:400px;} input.span6, textarea.span6, .uneditable-input.span6{width:338px;} input.span5, textarea.span5, .uneditable-input.span5{width:276px;} input.span4, textarea.span4, .uneditable-input.span4{width:214px;} input.span3, textarea.span3, .uneditable-input.span3{width:152px;} input.span2, textarea.span2, .uneditable-input.span2{width:90px;} input.span1, textarea.span1, .uneditable-input.span1{width:28px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:30px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%;} .row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%;} .row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%;} .row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%;} .row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%;} .row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%;} .row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%;} .row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%;} .row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%;} .row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%;} .row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%;} .row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%;} .row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%;} .row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%;} .row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%;} .row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%;} .row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%;} .row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%;} .row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%;} .row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%;} .row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%;} .row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%;} .row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%;} .row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%;} .row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%;} .row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%;} .row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%;} .row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%;} .row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%;} .row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%;} .row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%;} .row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%;} .row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%;} .row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%;} .row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:30px;} input.span12, textarea.span12, .uneditable-input.span12{width:1156px;} input.span11, textarea.span11, .uneditable-input.span11{width:1056px;} input.span10, textarea.span10, .uneditable-input.span10{width:956px;} input.span9, textarea.span9, .uneditable-input.span9{width:856px;} input.span8, textarea.span8, .uneditable-input.span8{width:756px;} input.span7, textarea.span7, .uneditable-input.span7{width:656px;} input.span6, textarea.span6, .uneditable-input.span6{width:556px;} input.span5, textarea.span5, .uneditable-input.span5{width:456px;} input.span4, textarea.span4, .uneditable-input.span4{width:356px;} input.span3, textarea.span3, .uneditable-input.span3{width:256px;} input.span2, textarea.span2, .uneditable-input.span2{width:156px;} input.span1, textarea.span1, .uneditable-input.span1{width:56px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}body.layout-compact{background-color:#fafafa;margin-top:100px;}body.layout-compact h1{padding:0px;color:#999999;font-size:180%;}body.layout-compact h1 strong{color:#333333;}
+body.layout-compact .block-bold{background:#ffffff;border:1px solid #c9c9c9;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0px 0px 0px 3px #e3e3e3;-moz-box-shadow:0px 0px 0px 3px #e3e3e3;box-shadow:0px 0px 0px 3px #e3e3e3;margin:0px -19px;}body.layout-compact .block-bold .alert{border-width:0px 0px 1px 0px;-webkit-border-radius:2px 2px 0px 0px;-moz-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px;margin:0px;padding:12px;font-weight:bold;}
+body.layout-compact .block-bold form{margin:0px;padding:0px;}body.layout-compact .block-bold form .form-container{padding:20px 18px;padding-bottom:12px;}
+body.layout-compact .block-bold form .form-actions{-webkit-border-radius:0px 0px 2px 2px;-moz-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px;margin:0px;}
+footer{padding-top:16px;padding-bottom:32px;color:#b0b0b0;}footer a,footer a:link,footer a:active,footer a:visited{color:#b0b0b0;text-decoration:underline;}
+footer a:hover{color:#7d7d7d;}
+footer .go-to-top{float:right;}footer .go-to-top,footer .go-to-top:link,footer .go-to-top:active,footer .go-to-top:visited{text-decoration:none;}footer .go-to-top i,footer .go-to-top:link i,footer .go-to-top:active i,footer .go-to-top:visited i{opacity:0.4;filter:alpha(opacity=40);}
+footer .go-to-top:hover i{opacity:0.65;filter:alpha(opacity=65);}
+.side-panel{padding-right:30px;border-right:1px solid #d6d6d6;}
+.sidepanel-header{margin-top:0px;}
+form label{color:#555555;font-weight:bold;cursor:pointer;}
+form fieldset{border-top:1px solid #e8e8e8;margin:0px;padding:0px;padding-top:16px;padding-bottom:8px;}form fieldset legend{margin:0px;margin-bottom:-8px;padding:0px;padding-top:8px;}
+form fieldset .control-group{padding-bottom:4px;}
+form fieldset .control-group:last-child{padding-bottom:0px;}
+form fieldset:first-child{border-top:none;padding-top:0px;}
+form fieldset:last-child{padding-bottom:0px;}
+textarea{resize:vertical;}
+.radio-group,.select-multiple,.yes-no-switch{margin-bottom:8px;}.radio-group label,.select-multiple label,.yes-no-switch label{color:#000000;font-weight:normal;}
+.side-search{background:#ffffff;border:1px solid #d6d6d6;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0px 0px 0px 3px #f0f0f0;-moz-box-shadow:0px 0px 0px 3px #f0f0f0;box-shadow:0px 0px 0px 3px #f0f0f0;padding:8px;margin-left:14px;margin-right:-14px;}.side-search h4{border-bottom:1px solid #d6d6d6;padding-top:0px;padding-bottom:8px;margin-top:0px;}
+.side-search hr{border-top:1px solid #ebebeb;margin-bottom:16px;}
+.side-search label.checkbox,.side-search label.radio{font-weight:normal;}
+.side-search .form-actions{background:#f5f5f5;-webkit-border-radius:0px 0px 3px 3px;-moz-border-radius:0px 0px 3px 3px;border-radius:0px 0px 3px 3px;padding:12px 8px;margin:-8px;margin-top:0px;}
+.table-footer{background:none;margin-bottom:0px;padding:0px 8px;position:relative;bottom:20px;}.table-footer .pager{margin:0px 0px;margin-top:9px;padding:0px;margin-right:6px;}.table-footer .pager>li{margin-right:6px;}.table-footer .pager>li>a:link,.table-footer .pager>li>a:active,.table-footer .pager>li>a:visited{background:#e8e8e8;border:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:2px 5px;}
+.table-footer .pager>li>a:hover{background-color:#0088cc;}.table-footer .pager>li>a:hover i{background-image:url("../img/glyphicons-halflings-white.png");}
+.table-footer .table-count{padding:11px 0px;color:#555555;}
+.table-footer .form-inline{margin:0px;padding:6px 0px;}
+td.check-cell,th.check-cell{width:32px;}
+td .checkbox,th .checkbox{margin-bottom:0px;position:relative;bottom:1px;}td .checkbox input,th .checkbox input{position:relative;left:9px;}
+td.lead-cell{font-size:120%;}
+.table td{vertical-align:middle;}
+th.table-sort{padding:0px;}th.table-sort a:link,th.table-sort a:active,th.table-sort a:visited a:hover{display:block;padding:8px;}
+th.table-sort.sort-active-asc a:link,th.table-sort.sort-active-asc a:active,th.table-sort.sort-active-asc a:visited{border-bottom:3px solid #049cdb;padding-bottom:5px;}
+th.table-sort.sort-active-asc a:hover{border-bottom:3px solid #e4776f;padding-bottom:5px;text-decoration:none;}
+th.table-sort.sort-active-desc a:link,th.table-sort.sort-active-desc a:active,th.table-sort.sort-active-desc a:visited{border-bottom:3px solid #dc4e44;padding-bottom:5px;}
+th.table-sort.sort-active-desc a:hover{border-bottom:3px solid #17b8fb;padding-bottom:5px;text-decoration:none;}
+th.table-sort.sort-asc a:hover{border-bottom:3px solid #ade6fe;padding-bottom:5px;text-decoration:none;}
+th.table-sort.sort-desc a:hover{border-bottom:3px solid #eca09a;padding-bottom:5px;text-decoration:none;}
+.well{background:#fafafa;border:1px solid #ffffff;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0px 0px 3px #c9c9c9;-moz-box-shadow:0px 0px 3px #c9c9c9;box-shadow:0px 0px 3px #c9c9c9;}
+.strong-well{background:#ffffff;border:1px solid #c9c9c9;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0px 0px 0px 3px #e3e3e3;-moz-box-shadow:0px 0px 0px 3px #e3e3e3;box-shadow:0px 0px 0px 3px #e3e3e3;}
+.list-tiny{display:block;margin:0px -6px;margin-bottom:16px;padding:4px 0px;overflow:auto;}.list-tiny>li{background:#ededed;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin:3px 6px;padding:4px 7px;padding-bottom:6px;float:left;list-style:none;font-weight:bold;}
+.btn{background:#dcdcdc;border:1px solid #dcdcdc;*border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;padding:4px 10px;color:#6f6f6f;font-weight:bold;text-shadow:none;}.btn:hover,.btn:active{background:#e1e1e1;border:1px solid #e1e1e1;*border:0;box-shadow:none;color:#0088cc;}
+.btn i{opacity:0.7;filter:alpha(opacity=70);}
+.btn:hover i,.btn:active i{opacity:1;filter:alpha(opacity=100);}
+.btn.btn-primary,.btn.btn-info,.btn.btn-success,.btn.btn-warning,.btn.btn-danger,.btn.btn-inverse{color:#f2f2f2;}.btn.btn-primary i,.btn.btn-info i,.btn.btn-success i,.btn.btn-warning i,.btn.btn-danger i,.btn.btn-inverse i{opacity:0.9;filter:alpha(opacity=90);}
+.btn.btn-primary:hover i,.btn.btn-info:hover i,.btn.btn-success:hover i,.btn.btn-warning:hover i,.btn.btn-danger:hover i,.btn.btn-inverse:hover i,.btn.btn-primary:active i,.btn.btn-info:active i,.btn.btn-success:active i,.btn.btn-warning:active i,.btn.btn-danger:active i,.btn.btn-inverse:active i{opacity:1;filter:alpha(opacity=100);}
+.btn.btn-primary:hover,.btn.btn-info:hover,.btn.btn-success:hover,.btn.btn-warning:hover,.btn.btn-danger:hover,.btn.btn-inverse:hover,.btn.btn-primary:active,.btn.btn-info:active,.btn.btn-success:active,.btn.btn-warning:active,.btn.btn-danger:active,.btn.btn-inverse:active{color:#ffffff;}
+.btn.btn-primary{background:#0088cc;border:1px solid #0088cc;*border:0;background:#0088cc;border:1px solid #0088cc;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #0077b3;}.btn.btn-primary:hover,.btn.btn-primary:active{background:#00a3f5;border:1px solid #00a3f5;*border:0;}
+.btn.btn-info{background:#507e95;border:1px solid #507e95;*border:0;background:#507e95;border:1px solid #507e95;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #477085;}.btn.btn-info:hover,.btn.btn-info:active{background:#709cb2;border:1px solid #709cb2;*border:0;}
+.btn.btn-success{background:#46a546;border:1px solid #46a546;*border:0;background:#46a546;border:1px solid #46a546;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #3e933e;}.btn.btn-success:hover,.btn.btn-success:active{background:#69bf69;border:1px solid #69bf69;*border:0;}
+.btn.btn-warning{background:#ee8e06;border:1px solid #ee8e06;*border:0;background:#ee8e06;border:1px solid #ee8e06;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #d57f05;}.btn.btn-warning:hover,.btn.btn-warning:active{background:#faa937;border:1px solid #faa937;*border:0;}
+.btn.btn-danger{background:#d13327;border:1px solid #d13327;*border:0;background:#d13327;border:1px solid #d13327;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #bb2d23;}.btn.btn-danger:hover,.btn.btn-danger:active{background:#e05f55;border:1px solid #e05f55;*border:0;}
+.btn.btn-inverse{background:#555555;border:1px solid #555555;*border:0;background:#555555;border:1px solid #555555;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #484848;}.btn.btn-inverse:hover,.btn.btn-inverse:active{background:#7e7e7e;border:1px solid #7e7e7e;*border:0;}
+.btn.btn-link{background:none;border:none;}.btn.btn-link:hover,.btn.btn-link:active{color:#00aaff;text-decoration:none;}
+.alerts-global{margin-top:16px;}
+.alert-form{margin:0px;margin-bottom:16px;font-weight:bold;}
+.alert-inline{margin:0px;padding:0px;}
+.alert-icon{float:left;}.alert-icon span{-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;padding:1px 2px;}
+.alert .alert-icon span{background:#f89406;}
+.alert-error .alert-icon span{background:#9d261d;}
+.alert-success .alert-icon span{background:#46a546;}
+.alert-info .alert-icon span{background:#049cdb;}
+.alert{padding:10px 11px;}.alert p{margin-left:28px;}
+.alert p:last-child{margin-bottom:0px;}
+.alert p.protip{font-size:100%;}
+.avatar-big{border-radius:3px;width:180px;height:180px;}
+.avatar-normal{border-radius:3px;width:64px;height:64px;}
+.avatar-small{border-radius:3px;width:28px;height:28px;}
+.avatar-tiny{border-radius:3px;width:16px;height:16px;}
+.navbar .avatar-small{margin:-10px 0px;position:relative;bottom:1px;}
+.page-header .avatar-normal{width:42px;height:42px;margin:-10px 0px;position:relative;top:-3px;}
+.tabs-header{border-bottom:none;padding-bottom:0px;}.tabs-header .nav-tabs{margin-bottom:0px;}
+.nav-tabs li a:link,.nav-tabs li a:active,.nav-tabs li a:visited{opacity:0.6;filter:alpha(opacity=60);color:#333333;font-weight:bold;}
+.nav-tabs li i{margin-right:4px;}
+.nav-tabs li.active a:link,.nav-tabs li.active a:active,.nav-tabs li.active a:visited,.nav-tabs li.active a:hover{background-color:#fcfcfc;}.nav-tabs li.active a:link i,.nav-tabs li.active a:active i,.nav-tabs li.active a:visited i,.nav-tabs li.active a:hover i{background-image:url("../img/glyphicons-halflings.png");}
+.nav-tabs li.active a:link,.nav-tabs li.active a:active,.nav-tabs li.active a:visited,.nav-tabs li.active a:hover,.nav-tabs li a:hover{opacity:1;filter:alpha(opacity=100);}
+.nav-tabs li.fallback{float:right;}.nav-tabs li.fallback a:link,.nav-tabs li.fallback a:active,.nav-tabs li.fallback a:visited,.nav-tabs li.fallback a:hover{border-bottom:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin-top:4px;padding:4px 12px;}
+td>ul.list-actions{list-style:none;margin:0px;overflow:auto;padding:0px;}td>ul.list-actions>li{float:left;margin:0px;padding:0px;}td>ul.list-actions>li>form{margin:0px;padding:0px;}
+td>ul.list-actions>li>a,td>ul.list-actions>li>form>button{background:none;border:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;display:block;margin:0px 3px;padding:2px 5px;}td>ul.list-actions>li>a:hover,td>ul.list-actions>li>form>button:hover{background-color:#0088cc;}td>ul.list-actions>li>a:hover i,td>ul.list-actions>li>form>button:hover i{background-image:url("../img/glyphicons-halflings-white.png");}
+.navbar-sections .brand{margin-right:-10px;color:#333333;font-size:200%;}.navbar-sections .brand span{color:#999999;font-size:50%;line-height:50%;}
+.navbar-sections .user-profile{padding:7px 10px 7px 3px;margin:15px 5px;color:#222222;font-weight:bold;}
+.navbar-sections .nav{margin-left:0px;margin-right:0px;}.navbar-sections .nav li a,.navbar-sections .nav li a:link,.navbar-sections .nav li a:active,.navbar-sections .nav li a:visited{opacity:0.5;filter:alpha(opacity=50);padding:22px 10px 22px;margin:0px 5px;font-weight:bold;text-shadow:0px 1px 0px #ffffff;}
+.navbar-sections .nav li a:hover{background:#e8e8e8;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;padding:7px 10px 7px;margin:15px 5px;opacity:1;filter:alpha(opacity=100);}
+.navbar-sections .nav li form{margin:0px;padding:0px;}
+.navbar-sections .nav li.active a,.navbar-sections .nav li.active a:link,.navbar-sections .nav li.active a:active,.navbar-sections .nav li.active a:visited,.navbar-sections .nav li.active a:hover{background-color:#0088cc;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;padding:7px 10px 7px;margin:15px 0px;opacity:1;filter:alpha(opacity=100);text-shadow:0px 1px 0px #0077b3;}.navbar-sections .nav li.active a i,.navbar-sections .nav li.active a:link i,.navbar-sections .nav li.active a:active i,.navbar-sections .nav li.active a:visited i,.navbar-sections .nav li.active a:hover i{background-image:url("../img/glyphicons-halflings-white.png");opacity:1;filter:alpha(opacity=100);}
+.navbar-sections .btn-link,.navbar-sections .btn-link:link,.navbar-sections .btn-link:visited{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;padding:7px 8px 7px;margin:15px 0px;opacity:0.6;filter:alpha(opacity=60);color:#333333;font-weight:bold;font-size:100%;}.navbar-sections .btn-link i,.navbar-sections .btn-link:link i,.navbar-sections .btn-link:visited i{opacity:1;filter:alpha(opacity=100);}
+.navbar-sections .btn-link:active,.navbar-sections .btn-link:hover{background:#e8e8e8;opacity:1;filter:alpha(opacity=100);color:#333333;}
+.navbar-actions{border-bottom:1px solid #cccccc;}.navbar-actions .navbar-inner{min-height:40px;}
+.navbar-actions .nav li a,.navbar-actions .nav li a:link,.navbar-actions .nav li a:active,.navbar-actions .nav li a:visited{opacity:0.5;filter:alpha(opacity=50);padding:10px 15px 10px;color:#000000;font-weight:bold;text-shadow:0px 1px 0px #eeeeee;}
+.navbar-actions .nav li a:hover{opacity:1;filter:alpha(opacity=100);}
+.navbar-actions .nav li form{margin:0px;padding:0px;}
+.navbar-actions .nav li.active a,.navbar-actions .nav li.active a:link,.navbar-actions .nav li.active a:active,.navbar-actions .nav li.active a:visited,.navbar-actions .nav li.active a:hover{border-bottom:4px solid #0088cc;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:1;filter:alpha(opacity=100);padding-bottom:7px;margin-bottom:-1px;color:#333333;text-shadow:0px 1px 0px #ffffff;}.navbar-actions .nav li.active a i,.navbar-actions .nav li.active a:link i,.navbar-actions .nav li.active a:active i,.navbar-actions .nav li.active a:visited i,.navbar-actions .nav li.active a:hover i{background-image:url("../img/glyphicons-halflings.png");opacity:1;filter:alpha(opacity=100);}
+.navbar .navbar-inner{border:none;background:none;background-color:#f2f2f2;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.navbar.navbar-inverse{font-size:90%;}.navbar.navbar-inverse .navbar-inner{background:none;background-color:#e5e5e5;}
+.popover{border:1px solid #black;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0px 0px 0px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:0px 0px 0px 3px rgba(0, 0, 0, 0.1);box-shadow:0px 0px 0px 3px rgba(0, 0, 0, 0.1);}
+.popover-title .user-card{margin-top:2px;margin-bottom:-2px;font-weight:bold;font-size:160%;}.popover-title .user-card .avatar-small{position:relative;bottom:2px;}
+.user-infocard{overflow:auto;}.user-infocard .avatar-normal{float:left;}
+.graph{background:#FFF;border:1px solid #DDD;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:5px;}.graph .timeline{overflow:auto;margin-top:-12px;padding:0px 12px;}
+.graph .peak{background:#FFF;border:4px solid #F00;-webkit-border-radius:12px;-moz-border-radius:12px;border-radius:12px;position:absolute;cursor:pointer;width:7px;height:7px;}
+.sub-graph{margin-top:8px;}
+.clickable{cursor:pointer;}
+.stat-title{font-weight:bold;text-align:right;}
+.quick-action{margin:0px -8px;}

+ 92 - 0
static/admin/css/admin.less

@@ -0,0 +1,92 @@
+/*!
+ * Bootstrap v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+// CSS Reset
+@import "bootstrap/reset.less";
+
+// Core variables and mixins
+@import "variables.less";
+@import "bootstrap/mixins.less";
+
+// Grid system and page structure
+@import "bootstrap/scaffolding.less";
+@import "bootstrap/grid.less";
+@import "bootstrap/layouts.less";
+
+// Base CSS
+@import "bootstrap/type.less";
+@import "bootstrap/code.less";
+@import "bootstrap/forms.less";
+@import "bootstrap/tables.less";
+
+// Components: common
+@import "bootstrap/sprites.less";
+@import "bootstrap/dropdowns.less";
+@import "bootstrap/wells.less";
+@import "bootstrap/component-animations.less";
+@import "bootstrap/close.less";
+
+// Components: Buttons & Alerts
+@import "bootstrap/buttons.less";
+@import "bootstrap/button-groups.less";
+@import "bootstrap/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
+
+// Components: Nav
+@import "bootstrap/navs.less";
+@import "bootstrap/navbar.less";
+@import "bootstrap/breadcrumbs.less";
+@import "bootstrap/pagination.less";
+@import "bootstrap/pager.less";
+
+// Components: Popovers
+@import "bootstrap/modals.less";
+@import "bootstrap/tooltip.less";
+@import "bootstrap/popovers.less";
+
+// Components: Misc
+@import "bootstrap/thumbnails.less";
+@import "bootstrap/labels-badges.less";
+@import "bootstrap/progress-bars.less";
+@import "bootstrap/accordion.less";
+@import "bootstrap/carousel.less";
+@import "bootstrap/hero-unit.less";
+
+// Utility classes
+@import "bootstrap/utilities.less"; // Has to be last to override when necessary
+
+// Responsiveness
+@import "bootstrap/responsive-768px-979px.less";
+@import "bootstrap/responsive-1200px-min.less";
+
+
+// Yes-no Switch
+@import "bootstrap-toggle-buttons.css";
+
+
+// Admin theme
+@import "admin/scaffolding.less";
+
+@import "admin/forms.less";
+@import "admin/tables.less";
+
+@import "admin/wells.less";
+@import "admin/users-lists.less";
+
+@import "admin/buttons.less";
+@import "admin/alerts.less";
+
+@import "admin/avatars.less";
+@import "admin/navs.less";
+@import "admin/navbar.less";
+
+@import "admin/popovers.less";
+@import "admin/graphs.less";
+
+@import "admin/utilities.less";

+ 62 - 0
static/admin/css/admin/alerts.less

@@ -0,0 +1,62 @@
+// Misago alerts
+// -------------------------
+.alerts-global {
+  margin-top: 16px;
+}
+
+.alert-form {
+  margin: 0px;
+  margin-bottom: 16px;
+  
+  font-weight: bold;
+}
+
+.alert-inline {
+  margin: 0px;
+  padding: 0px;
+}
+
+// Alerts icons
+// -------------------------
+.alert-icon {
+  float: left;
+  
+  span {
+    .border-radius(2px);
+    padding: 1px 2px; 
+  }
+}
+
+.alert .alert-icon span {
+  background: @orange;
+}
+
+.alert-error .alert-icon span {
+  background: @red;  
+}
+
+.alert-success .alert-icon span {
+  background: @green;  
+}
+
+.alert-info .alert-icon span {
+  background: @blue;  
+}
+
+// Alerts paddings
+// -------------------------
+.alert {
+  padding: 10px 11px;
+  
+  p {
+    margin-left: 28px;
+  }
+  
+  p:last-child {
+    margin-bottom: 0px;
+  }
+  
+  p.protip {
+    font-size: 100%;
+  }
+}

+ 45 - 0
static/admin/css/admin/avatars.less

@@ -0,0 +1,45 @@
+// Misago avatars sizes
+// -------------------------
+.avatar-big {
+  border-radius: 3px;
+  width: 180px;
+  height: 180px;
+}
+
+.avatar-normal {
+  border-radius: 3px;
+  width: 64px;
+  height: 64px;
+}
+
+.avatar-small {
+  border-radius: 3px;
+  width: 28px;
+  height: 28px;
+}
+
+.avatar-tiny {
+  border-radius: 3px;
+  width: 16px;
+  height: 16px;
+}
+
+// Handle small avatar within different places
+// -------------------------
+.navbar {
+  .avatar-small {
+    margin: -(@baseLineHeight / 2) 0px;
+    position: relative;
+    bottom: 1px;
+  }
+}
+
+.page-header {
+  .avatar-normal {
+    width: 42px;
+    height: 42px;
+    margin: -(@baseLineHeight / 2) 0px;
+    position: relative;
+    top: (@baseLineHeight / 2) - 13;
+  }
+}

+ 103 - 0
static/admin/css/admin/buttons.less

@@ -0,0 +1,103 @@
+.button-colors (@btnColor) {
+  background: @btnColor;
+  border: 1px solid @btnColor;
+  *border: 0;
+}
+
+.button-icon (@default, @hover: 100) {
+  i {
+    .opacity(@default);
+  }
+  
+  &:hover, &:active {
+    i {
+      .opacity(@hover);
+    }
+  }
+}
+
+.button-style (@background, @highlight) {
+  .button-colors(@background);
+
+  color: darken(@white, 3%);
+  text-shadow: 0px 1px 0px darken(@background, 5%);
+
+  &:hover, &:active {
+    .button-colors(@highlight);
+  }
+}
+
+.btn {
+  .button-colors(@btnBackground);
+  .box-shadow(none);
+  padding: 4px 10px;
+  
+  color: lighten(@gray, 10%);
+  font-weight: bold;
+  text-shadow: none;
+  
+  &:hover, &:active {
+    .button-colors(@btnBackgroundHighlight);
+    box-shadow: none;
+  
+    color: @linkColor;
+  }
+  
+  .button-icon(70);
+  
+  &.btn-primary,
+  &.btn-info,
+  &.btn-success,
+  &.btn-warning,
+  &.btn-danger,
+  &.btn-inverse {
+    .button-icon(90);
+  
+    color: darken(@white, 5%);
+  
+    &:hover, &:active {  
+      color: @white;
+    }
+  }
+  
+  &.btn-primary {
+    .button-colors(@btnPrimaryBackground);
+    .button-style(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
+  }
+  
+  &.btn-info {
+    .button-colors(@btnInfoBackground);
+    .button-style(@btnInfoBackground, @btnInfoBackgroundHighlight);
+  }
+  
+  &.btn-success {
+    .button-colors(@btnSuccessBackground);
+    .button-style(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
+  }
+  
+  &.btn-warning {
+    .button-colors(@btnWarningBackground);
+    .button-style(@btnWarningBackground, @btnWarningBackgroundHighlight);
+  }
+  
+  &.btn-danger {
+    .button-colors(@btnDangerBackground);
+    .button-style(@btnDangerBackground, @btnDangerBackgroundHighlight);
+  }
+  
+  &.btn-inverse {
+    .button-colors(@btnInverseBackground);
+    .button-style(@btnInverseBackground, @btnInverseBackgroundHighlight);
+  }
+  
+  &.btn-link {
+    background: none;
+    border: none;
+  
+    &:hover, &:active {
+      color: @linkColorHover;
+      
+      text-decoration: none;
+    }
+  }
+}

+ 95 - 0
static/admin/css/admin/forms.less

@@ -0,0 +1,95 @@
+// Misago forms
+// -------------------------
+form {
+  label {
+    color: @gray;
+    font-weight: bold;
+    cursor: pointer;
+  }
+  
+  fieldset {
+    border-top: 1px solid darken(@bodyBackground, 8%);
+    margin: 0px;
+    padding: 0px;
+    padding-top: 16px;
+    padding-bottom: 8px;
+  
+    legend {
+      margin: 0px;
+      margin-bottom: -8px;
+      padding: 0px;
+      padding-top: 8px;
+    }
+        
+    .control-group {
+      padding-bottom: 4px;
+    }
+    
+    .control-group:last-child {
+      padding-bottom: 0px;
+    }
+  }
+  
+  fieldset:first-child {
+    border-top: none;
+    padding-top: 0px;
+  }
+  
+  fieldset:last-child {
+    padding-bottom: 0px;
+  }
+}
+
+// Make textarea resizeable vertically only
+// -------------------------
+textarea {
+  resize: vertical;
+}
+
+
+// Lists styling
+// -------------------------
+.radio-group, .select-multiple, .yes-no-switch {
+  label {
+    color: @black;
+    font-weight: normal;
+  }
+  
+  margin-bottom: 8px;
+}
+
+// Side-search form
+// -------------------------
+.side-search {
+  background: @white;
+  border: 1px solid darken(@bodyBackground, 15%);
+  .border-radius(4px);
+  .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 5%));
+  padding: 8px;
+  margin-left: 14px;
+  margin-right: -14px;
+  
+  h4 {
+    border-bottom: 1px solid darken(@bodyBackground, 15%);
+    padding-top: 0px;
+    padding-bottom: 8px;
+    margin-top: 0px;
+  }
+  
+  hr {
+    border-top: 1px solid darken(@bodyBackground, 7%);
+    margin-bottom: 16px;
+  }
+    
+  label.checkbox, label.radio {
+    font-weight: normal;
+  }
+    
+  .form-actions {
+    background: darken(@bodyBackground, 3%);
+    .border-radius(0px 0px 3px 3px);
+    padding: 12px 8px;
+    margin: -8px;
+    margin-top: 0px;
+  }
+}

+ 28 - 0
static/admin/css/admin/graphs.less

@@ -0,0 +1,28 @@
+// Graphs
+// --------------------------------------------------
+.graph {
+  background: #FFF;
+  border: 1px solid #DDD;
+  .border-radius(3px);
+  padding: 5px;
+
+  .timeline {
+    overflow: auto;
+    margin-top: -12px;
+    padding: 0px 12px;
+  }
+
+  .peak {
+    background: #FFF;
+    border: 4px solid #F00;
+    .border-radius(12px);
+	position: absolute;
+    cursor: pointer;
+    width: 7px;
+    height: 7px;
+  }
+}
+
+.sub-graph {
+  margin-top: 8px;
+}

+ 172 - 0
static/admin/css/admin/navbar.less

@@ -0,0 +1,172 @@
+// Admin sections and user navbar
+// -------------------------
+.navbar-sections {    
+  // Style brand
+  .brand {
+    margin-right: -10px;
+    
+    color: @grayDark;
+    font-size: 200%;
+    
+    span {
+      color: @grayLight;
+      font-size: 50%;
+      line-height: 50%;
+    }
+  }
+  
+  // Signed-in administrator profile 
+  .user-profile {
+    padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px 3px;
+    margin: 15px 5px;
+    
+    color: @grayDarker;
+    font-weight: bold;
+  }
+      
+  // Make links look better
+  .nav {
+    // Drop side margins so we fit 1024px clients
+    margin-left: 0px;
+    margin-right: 0px;
+    
+    li {
+      a, a:link, a:active, a:visited{
+        .opacity(50);
+        padding: ((@navbarHeight - @baseLineHeight) / 2) 10px ((@navbarHeight - @baseLineHeight) / 2);
+        margin: 0px 5px;
+        
+        font-weight: bold;
+        text-shadow: 0px 1px 0px @white;
+      }
+      
+      a:hover  {
+        background: @navbarLinkBackgroundHover;
+        .border-radius(4px);
+        padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px;
+        margin: 15px 5px;
+        .opacity(100);
+      }
+      
+      form {
+        margin: 0px;
+        padding: 0px;
+      }
+    }
+    
+    li.active {
+      a, a:link, a:active, a:visited, a:hover {
+        background-color: @navbarLinkBackgroundActive;
+        .border-radius(4px);
+        .box-shadow(none);
+        padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px;
+        margin: 15px 0px;
+        .opacity(100);
+          
+        text-shadow: 0px 1px 0px darken(@navbarLinkBackgroundActive, 5%);
+        
+        i {
+          background-image: url("@{iconWhiteSpritePath}");
+          .opacity(100);
+        }
+      }
+    }
+  }  
+    
+  // Sign-out button
+  .btn-link, .btn-link:link, .btn-link:visited {
+    .border-radius(4px);
+    padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 8px ((@navbarHeight - @baseLineHeight) / 2)-15px;
+    margin: 15px 0px;
+    .opacity(60);
+    
+    color: @textColor;
+    font-weight: bold;
+    font-size: 100%;
+    
+    i {
+      .opacity(100);
+    }
+  }
+  
+  .btn-link:active, .btn-link:hover {
+    background: @navbarLinkBackgroundHover;
+    .opacity(100);
+    
+    color: @textColor;
+  }
+}
+
+// Admin section actions navbar
+// -------------------------
+.navbar-actions {  
+  border-bottom: 1px solid @navbarInverseBorder;
+  
+  // Fix inverse navbar height
+  .navbar-inner {
+    min-height: @navbarInverseHeight;
+  }
+  
+  .nav {
+    li {
+      a, a:link, a:active, a:visited {
+        .opacity(50);
+        padding: ((@navbarInverseHeight - @baseLineHeight) / 2) 15px ((@navbarInverseHeight - @baseLineHeight) / 2);
+        
+        color: @black;
+        font-weight: bold;
+        text-shadow: 0px 1px 0px @grayLighter;
+      }
+      
+      a:hover {        
+        .opacity(100);
+        
+      }
+      
+      form {
+        margin: 0px;
+        padding: 0px;
+      }
+    }
+    
+    li.active {      
+      a, a:link, a:active, a:visited, a:hover {
+        border-bottom: 4px solid @linkColor;
+        .box-shadow(none);
+        .opacity(100);
+        padding-bottom: ((@navbarInverseHeight - @baseLineHeight) / 2) - 3px;
+        margin-bottom: -1px;
+        
+        color: @grayDark;
+        text-shadow: 0px 1px 0px @white;
+        
+        i {
+          background-image: url("@{iconSpritePath}");
+          .opacity(100);
+        }
+      }
+    }
+  }
+}
+
+// Both Navbars
+// -------------------------
+.navbar {
+  // Inner style
+  .navbar-inner {
+    border: none;
+    background: none;
+    background-color: @navbarBackground;
+    .box-shadow(none);
+  }
+  // And black navbar style
+  &.navbar-inverse {  
+    font-size: 90%;
+      
+    // Inner style
+    .navbar-inner {
+      background: none;
+      background-color: @navbarInverseBackground;
+    }
+  }
+}

+ 85 - 0
static/admin/css/admin/navs.less

@@ -0,0 +1,85 @@
+// Admin navs
+// -------------------------
+.tabs-header {
+  border-bottom: none;
+  padding-bottom: 0px;
+  
+  .nav-tabs {
+    margin-bottom: 0px;
+  }
+}
+
+.nav-tabs li {
+  a:link, a:active, a:visited {
+    .opacity(60);
+    
+    color: @textColor;
+    font-weight: bold;
+  }
+  
+  i {
+    margin-right: 4px;
+  }
+  
+  &.active a:link, &.active a:active,
+  &.active a:visited, &.active a:hover {
+    background-color: @bodyBackground;
+        
+    i {
+      background-image: url("@{iconSpritePath}");
+    }
+  }
+  
+  &.active a:link, &.active a:active,
+  &.active a:visited, &.active a:hover, a:hover {
+    .opacity(100);
+  }
+  
+  &.fallback {
+    float:right;
+    
+    a:link, a:active,
+    a:visited, a:hover {
+      border-bottom: none;
+      .border-radius(3px);
+      margin-top: 4px;
+      padding: 4px 12px;
+    }
+  }
+}
+
+// Tables lists actions
+td>ul.list-actions {
+  list-style: none;
+  margin: 0px;
+  overflow: auto;
+  padding: 0px;
+  
+  &>li {
+    float: left;
+    margin: 0px;
+    padding: 0px;
+    
+    &>form {
+      margin: 0px;
+      padding: 0px;
+    }
+    
+    &>a, &>form>button {
+      background: none;
+      border: none;
+      .border-radius(3px);
+      display: block;
+      margin: 0px 3px;
+      padding: 2px 5px;
+      
+      &:hover {
+        background-color: @linkColor;
+        
+        i {
+          background-image: url("@{iconWhiteSpritePath}");          
+        }
+      }
+    }
+  }
+}

+ 32 - 0
static/admin/css/admin/popovers.less

@@ -0,0 +1,32 @@
+// Popovers
+// --------------------------------------------------
+.popover {
+  border: 1px solid #black;
+  .border-radius(3px);
+  .box-shadow(0px 0px 0px 3px rgba(0,0,0,.1));
+}
+
+.popover-title {
+  .user-card {
+    margin-top: 2px;
+    margin-bottom: -2px;
+    
+    font-weight: bold;
+    font-size: 160%;
+    
+    .avatar-small {
+      position: relative;
+      bottom: 2px;
+    }
+  }
+}
+
+// Popover infocard
+// --------------------------------------------------
+.user-infocard {
+  overflow: auto;
+  
+  .avatar-normal {
+    float: left;
+  }
+}

+ 94 - 0
static/admin/css/admin/scaffolding.less

@@ -0,0 +1,94 @@
+// Compact layout, used for sign-in and "processing" pages
+// -------------------------
+body.layout-compact {
+  background-color: darken(@bodyBackground, 1%);
+  margin-top: 100px;
+  
+  h1 {
+    padding: 0px;
+    
+    color: @grayLight;
+    font-size: 180%;
+    
+    strong {
+      color: @grayDark;
+    }
+  }
+  
+  .block-bold {
+    background: @white;
+    border: 1px solid darken(@bodyBackground, 20%);
+    .border-radius(3px);
+    .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 10%));
+    margin: 0px -19px;
+    
+    .alert{
+      border-width: 0px 0px 1px 0px;
+      .border-radius(2px 2px 0px 0px);
+      
+      margin: 0px;
+      padding: 12px;
+      font-weight: bold;
+    }
+    
+    form {
+      margin: 0px;
+      padding: 0px;
+      
+      .form-container {
+        padding: 20px 18px;
+        padding-bottom: 12px;
+      }
+      
+      .form-actions {
+        .border-radius(0px 0px 2px 2px);
+        margin: 0px;
+      }
+    }
+  }
+}
+
+// Footer
+footer {
+  padding-top: 16px;
+  padding-bottom: 32px;
+  
+  color: darken(@bodyBackground, 30%);
+  
+  a, a:link, a:active, a:visited {
+    color: darken(@bodyBackground, 30%);
+    text-decoration: underline;
+  }
+  
+  a:hover {
+    color: darken(@bodyBackground, 50%);
+  }
+  
+  .go-to-top {
+    float: right;
+    
+    &, &:link, &:active, &:visited {
+      text-decoration: none;
+      
+      i {
+        .opacity(40);
+      }
+    }
+    
+    &:hover {
+      i {
+        .opacity(65);
+      }
+    }
+  }
+}
+
+// Side-panel
+.side-panel {
+  padding-right: 30px;
+  border-right: 1px solid darken(@bodyBackground, 15%);
+}
+  
+.sidepanel-header {
+  margin-top: 0px;
+}

+ 134 - 0
static/admin/css/admin/tables.less

@@ -0,0 +1,134 @@
+// Tables
+// --------------------------------------------------
+.table-footer {
+  background: none;
+  margin-bottom: 0px;
+  padding: 0px 8px;
+  position: relative;
+  bottom: 20px;
+  
+  .pager {
+    margin: 0px 0px;
+    margin-top: 9px;
+    padding: 0px;
+    margin-right: 6px;
+    
+    &>li {
+      margin-right: 6px;
+    
+      &>a {
+        &:link, &:active, &:visited {
+          background: darken(@bodyBackground, 8%);
+          border: none;
+          .border-radius(3px);
+          padding: 2px 5px;
+        }
+      
+        &:hover {
+          background-color: @linkColor;
+          
+          i {
+            background-image: url("@{iconWhiteSpritePath}");          
+          }
+        }
+      }
+    }
+  }
+  
+  .table-count {
+    padding: 11px 0px;
+    
+    color: @gray;
+  }
+  
+  .form-inline {
+    margin: 0px;
+    padding: 6px 0px;
+  }
+}
+
+// Checkbox cell
+td, th {
+  &.check-cell {
+    width: 32px;
+  }
+    
+  .checkbox {
+    margin-bottom: 0px;
+    position: relative;
+    bottom: 1px;
+    
+    input {
+      position: relative;
+      left: 9px;
+    }
+  }
+}
+
+// Lead cell
+td.lead-cell {
+  font-size: 120%;
+}
+
+// Vertically centered table
+.table {
+  td {
+    vertical-align: middle;
+  }
+}
+
+// Table sorting styles
+th.table-sort {
+  padding: 0px;
+  
+  a:link, a:active, a:visited a:hover{
+    display: block;
+    padding: 8px;
+  }
+  
+  &.sort-active-asc {
+    a:link, a:active, a:visited {
+      border-bottom: 3px solid @blue;
+      padding-bottom: 5px;
+    }
+    
+    a:hover {
+      border-bottom: 3px solid lighten(@red, 30%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-active-desc {
+    a:link, a:active, a:visited {
+      border-bottom: 3px solid lighten(@red, 20%);
+      padding-bottom: 5px;
+    }
+    
+    a:hover {
+      border-bottom: 3px solid lighten(@blue, 10%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-asc {
+    a:hover {
+      border-bottom: 3px solid lighten(@blue, 40%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-desc {    
+    a:hover {
+      border-bottom: 3px solid lighten(@red, 40%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }    
+  }
+}

+ 21 - 0
static/admin/css/admin/users-lists.less

@@ -0,0 +1,21 @@
+// Users list
+// --------------------------------------------------
+.list-tiny {
+  display: block;
+  margin: 0px -6px;
+  margin-bottom: 16px;
+  padding: 4px 0px;
+  overflow: auto;
+  
+  &>li {
+    background: darken(@bodyBackground, 6%);
+    .border-radius(3px);
+    margin: 3px 6px;
+    padding: 4px 7px;
+    padding-bottom: 6px;
+    float: left;
+    list-style: none;
+    
+    font-weight: bold;
+  }
+}

+ 16 - 0
static/admin/css/admin/utilities.less

@@ -0,0 +1,16 @@
+// Utility classes
+// --------------------------------------------------
+.clickable {
+  cursor: pointer;
+}
+
+// Admin Overview
+// --------------------------------------------------
+.stat-title {
+  font-weight: bold;
+  text-align: right;
+}
+
+.quick-action {
+  margin: 0px -8px;
+}

+ 15 - 0
static/admin/css/admin/wells.less

@@ -0,0 +1,15 @@
+// Wells
+// --------------------------------------------------
+.well {
+  background: darken(@bodyBackground, 1%);
+  border: 1px solid @white;
+  .border-radius(3px);
+  .box-shadow(0px 0px 3px darken(@bodyBackground, 20%));
+}
+
+.strong-well {
+  background: @white;
+  border: 1px solid darken(@bodyBackground, 20%);
+  .border-radius(3px);
+  .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 10%));
+}

+ 148 - 0
static/admin/css/bootstrap-toggle-buttons.css

@@ -0,0 +1,148 @@
+/* line 7, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button {
+  display: inline-block;
+  cursor: pointer;
+  background: #0088CC;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #0088cc), color-stop(100%, #0055cc));
+  background-image: -webkit-linear-gradient(top, #0088cc, #0055cc);
+  background-image: -moz-linear-gradient(top, #0088cc, #0055cc);
+  background-image: -o-linear-gradient(top, #0088cc, #0055cc);
+  background-image: linear-gradient(top, #0088cc, #0055cc);
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  -ms-border-radius: 4px;
+  -o-border-radius: 4px;
+  border-radius: 4px;
+  border: 1px solid;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  position: relative;
+  bottom: -2px;
+  text-align: left;
+  min-height: 25px;
+  max-height: 25px;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+/* line 29, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button:before, .toggle-button:after {
+  line-height: 25px;
+  font-weight: bold;
+  letter-spacing: .4px;
+}
+/* line 35, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button:before {
+  color: #fefefe;
+  padding-left: 0%;
+  margin-left: 10px;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);
+  content: attr(data-enabled);
+}
+/* line 43, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.disabled {
+  background: #fefefe;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #fefefe), color-stop(100%, #e6e6e6));
+  background-image: -webkit-linear-gradient(top, #fefefe, #e6e6e6);
+  background-image: -moz-linear-gradient(top, #fefefe, #e6e6e6);
+  background-image: -o-linear-gradient(top, #fefefe, #e6e6e6);
+  background-image: linear-gradient(top, #fefefe, #e6e6e6);
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+}
+/* line 47, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.disabled:before {
+  color: #555555;
+  padding-left: 50%;
+  margin-left: 10px;
+  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
+  content: attr(data-disabled);
+}
+/* line 54, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.disabled label {
+  margin-left: -1px;
+  left: 0%;
+}
+/* line 60, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.deactivate {
+  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
+  opacity: 0.5;
+}
+/* line 64, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button label {
+  cursor: pointer;
+  position: absolute;
+  width: 50%;
+  height: 25px;
+  background: #fefefe;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #fefefe), color-stop(100%, #e6e6e6));
+  background-image: -webkit-linear-gradient(top, #fefefe, #e6e6e6);
+  background-image: -moz-linear-gradient(top, #fefefe, #e6e6e6);
+  background-image: -o-linear-gradient(top, #fefefe, #e6e6e6);
+  background-image: linear-gradient(top, #fefefe, #e6e6e6);
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  -ms-border-radius: 4px;
+  -o-border-radius: 4px;
+  border-radius: 4px;
+  border: 1px solid;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  top: -1px;
+  margin-left: 0;
+  left: 50%;
+}
+/* line 79, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button input[type=checkbox] {
+  display: none;
+}
+/* line 88, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.primary:before, .toggle-button.info:before, .toggle-button.success:before, .toggle-button.warning:before, .toggle-button.danger:before {
+  color: white;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);
+}
+/* line 94, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.primary {
+  background: #0088cc;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #0088cc), color-stop(100%, #0055cc));
+  background-image: -webkit-linear-gradient(top, #0088cc, #0055cc);
+  background-image: -moz-linear-gradient(top, #0088cc, #0055cc);
+  background-image: -o-linear-gradient(top, #0088cc, #0055cc);
+  background-image: linear-gradient(top, #0088cc, #0055cc);
+}
+/* line 98, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.info {
+  background: #5bc0de;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5bc0de), color-stop(100%, #2f96b4));
+  background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
+  background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
+  background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
+  background-image: linear-gradient(top, #5bc0de, #2f96b4);
+}
+/* line 103, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.success {
+  background: #62c462;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #62c462), color-stop(100%, #51a351));
+  background-image: -webkit-linear-gradient(top, #62c462, #51a351);
+  background-image: -moz-linear-gradient(top, #62c462, #51a351);
+  background-image: -o-linear-gradient(top, #62c462, #51a351);
+  background-image: linear-gradient(top, #62c462, #51a351);
+}
+/* line 108, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.warning {
+  background: #dbb450;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dbb450), color-stop(100%, #f89406));
+  background-image: -webkit-linear-gradient(top, #dbb450, #f89406);
+  background-image: -moz-linear-gradient(top, #dbb450, #f89406);
+  background-image: -o-linear-gradient(top, #dbb450, #f89406);
+  background-image: linear-gradient(top, #dbb450, #f89406);
+}
+/* line 113, ../sass/bootstrap-toggle-buttons.scss */
+.toggle-button.danger {
+  background: #ee5f5b;
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ee5f5b), color-stop(100%, #bd362f));
+  background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
+  background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
+  background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
+  background-image: linear-gradient(top, #ee5f5b, #bd362f);
+}

+ 34 - 0
static/admin/css/bootstrap/accordion.less

@@ -0,0 +1,34 @@
+//
+// Accordion
+// --------------------------------------------------
+
+
+// Parent container
+.accordion {
+  margin-bottom: @baseLineHeight;
+}
+
+// Group == heading + body
+.accordion-group {
+  margin-bottom: 2px;
+  border: 1px solid #e5e5e5;
+  .border-radius(4px);
+}
+.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;
+}

+ 65 - 0
static/admin/css/bootstrap/alerts.less

@@ -0,0 +1,65 @@
+//
+// 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(4px);
+  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-danger,
+.alert-error {
+  background-color: @errorBackground;
+  border-color: @errorBorder;
+  color: @errorText;
+}
+.alert-info {
+  background-color: @infoBackground;
+  border-color: @infoBorder;
+  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;
+}

+ 62 - 0
static/admin/css/bootstrap/bootstrap.less

@@ -0,0 +1,62 @@
+/*!
+ * Bootstrap v2.1.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+// CSS Reset
+@import "reset.less";
+
+// Core variables and mixins
+@import "variables.less"; // Modify this for custom colors, font-sizes, etc
+@import "mixins.less";
+
+// Grid system and page structure
+@import "scaffolding.less";
+@import "grid.less";
+@import "layouts.less";
+
+// Base CSS
+@import "type.less";
+@import "code.less";
+@import "forms.less";
+@import "tables.less";
+
+// Components: common
+@import "sprites.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 "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

+ 24 - 0
static/admin/css/bootstrap/breadcrumbs.less

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

+ 245 - 0
static/admin/css/bootstrap/button-groups.less

@@ -0,0 +1,245 @@
+//
+// Button groups
+// --------------------------------------------------
+
+
+// Make the div behave like a button
+.btn-group {
+  position: relative;
+  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-group {
+    display: inline-block;
+    .ie7-inline-block();
+  }
+  .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 {
+  font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack
+}
+
+// Reset fonts for other sizes
+.btn-group > .btn-mini {
+  font-size: 11px;
+}
+.btn-group > .btn-small {
+  font-size: 12px;
+}
+.btn-group > .btn-large {
+  font-size: 16px;
+}
+
+// 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;
+     -webkit-border-top-left-radius: 4px;
+         -moz-border-radius-topleft: 4px;
+             border-top-left-radius: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+      -moz-border-radius-bottomleft: 4px;
+          border-bottom-left-radius: 4px;
+}
+// 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 {
+     -webkit-border-top-right-radius: 4px;
+         -moz-border-radius-topright: 4px;
+             border-top-right-radius: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+      -moz-border-radius-bottomright: 4px;
+          border-bottom-right-radius: 4px;
+}
+// Reset corners for large buttons
+.btn-group > .btn.large:first-child {
+  margin-left: 0;
+     -webkit-border-top-left-radius: 6px;
+         -moz-border-radius-topleft: 6px;
+             border-top-left-radius: 6px;
+  -webkit-border-bottom-left-radius: 6px;
+      -moz-border-radius-bottomleft: 6px;
+          border-bottom-left-radius: 6px;
+}
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+     -webkit-border-top-right-radius: 6px;
+         -moz-border-radius-topright: 6px;
+             border-top-right-radius: 6px;
+  -webkit-border-bottom-right-radius: 6px;
+      -moz-border-radius-bottomright: 6px;
+          border-bottom-right-radius: 6px;
+}
+
+// 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-mini .caret,
+.btn-small .caret,
+.btn-large .caret {
+  margin-top: 6px;
+}
+.btn-large .caret {
+  border-left-width:  5px;
+  border-right-width: 5px;
+  border-top-width:   5px;
+}
+// Upside down carets for .dropup
+.dropup .btn-large .caret {
+  border-bottom: 5px solid @black;
+  border-top: 0;
+}
+
+
+
+// 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;
+  width: 100%;
+  .border-radius(0);
+}
+.btn-group-vertical .btn + .btn {
+  margin-left: 0;
+  margin-top: -1px;
+}
+.btn-group-vertical .btn:first-child {
+  .border-radius(4px 4px 0 0);
+}
+.btn-group-vertical .btn:last-child {
+  .border-radius(0 0 4px 4px);
+}
+.btn-group-vertical .btn-large:first-child {
+  .border-radius(6px 6px 0 0);
+}
+.btn-group-vertical .btn-large:last-child {
+  .border-radius(0 0 6px 6px);
+}

+ 231 - 0
static/admin/css/bootstrap/buttons.less

@@ -0,0 +1,231 @@
+//
+// Buttons
+// --------------------------------------------------
+
+
+// Base styles
+// --------------------------------------------------
+
+// Core
+.btn {
+  display: inline-block;
+  .ie7-inline-block();
+  padding: 4px 14px;
+  margin-bottom: 0; // For input.btn
+  font-size: @baseFontSize;
+  line-height: @baseLineHeight;
+  *line-height: @baseLineHeight;
+  text-align: center;
+  vertical-align: middle;
+  cursor: pointer;
+  .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75));
+  border: 1px solid @btnBorder;
+  *border: 0; // Remove the border to prevent IE7's black border on input:focus
+  border-bottom-color: darken(@btnBorder, 10%);
+  .border-radius(4px);
+  .ie7-restore-left-whitespace(); // Give IE7 some love
+  .box-shadow(inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05));
+
+  // Hover state
+  &:hover {
+    color: @grayDark;
+    text-decoration: none;
+    background-color: darken(@white, 10%);
+    *background-color: darken(@white, 15%); /* Buttons in IE7 don't get borders, so darken on hover */
+    background-position: 0 -15px;
+
+    // transition is only when going to hover, otherwise the background
+    // behind the gradient (there for IE<=9 fallback) gets mismatched
+    .transition(background-position .1s linear);
+  }
+
+  // Focus state for keyboard and accessibility
+  &:focus {
+    .tab-focus();
+  }
+
+  // Active state
+  &.active,
+  &:active {
+    background-color: darken(@white, 10%);
+    background-color: darken(@white, 15%) e("\9");
+    background-image: none;
+    outline: 0;
+    .box-shadow(inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05));
+  }
+
+  // Disabled state
+  &.disabled,
+  &[disabled] {
+    cursor: default;
+    background-color: darken(@white, 10%);
+    background-image: none;
+    .opacity(65);
+    .box-shadow(none);
+  }
+
+}
+
+
+
+// Button Sizes
+// --------------------------------------------------
+
+// Large
+.btn-large {
+  padding: 9px 14px;
+  font-size: @baseFontSize + 2px;
+  line-height: normal;
+  .border-radius(5px);
+}
+.btn-large [class^="icon-"] {
+  margin-top: 2px;
+}
+
+// Small
+.btn-small {
+  padding: 3px 9px;
+  font-size: @baseFontSize - 2px;
+  line-height: @baseLineHeight - 2px;
+}
+.btn-small [class^="icon-"] {
+  margin-top: 0;
+}
+
+// Mini
+.btn-mini {
+  padding: 2px 6px;
+  font-size: @baseFontSize - 3px;
+  line-height: @baseLineHeight - 3px;
+}
+
+
+// Block button
+// -------------------------
+
+.btn-block {
+  display: block;
+  width: 100%;
+  padding-left: 0;
+  padding-right: 0;
+  .box-sizing(border-box);
+}
+
+// Vertically space out multiple block buttons
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+
+// Specificity overrides
+input[type="submit"],
+input[type="reset"],
+input[type="button"] {
+  &.btn-block {
+    width: 100%;
+  }
+}
+
+
+
+// Alternate buttons
+// --------------------------------------------------
+
+// Provide *some* extra contrast for those who can get it
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+  color: rgba(255,255,255,.75);
+}
+
+// Set the backgrounds
+// -------------------------
+.btn {
+  // reset here as of 2.0.3 due to Recess property order
+  border-color: #c5c5c5;
+  border-color: rgba(0,0,0,.15) rgba(0,0,0,.15) rgba(0,0,0,.25);
+}
+.btn-primary {
+  .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
+}
+// Warning appears are orange
+.btn-warning {
+  .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight);
+}
+// Danger and error appear as red
+.btn-danger {
+  .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight);
+}
+// Success appears as green
+.btn-success {
+  .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
+}
+// Info appears as a neutral blue
+.btn-info {
+  .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight);
+}
+// Inverse appears as dark gray
+.btn-inverse {
+  .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight);
+}
+
+
+// Cross-browser Jank
+// --------------------------------------------------
+
+button.btn,
+input[type="submit"].btn {
+
+  // Firefox 3.6 only I believe
+  &::-moz-focus-inner {
+    padding: 0;
+    border: 0;
+  }
+
+  // IE7 has some default padding on button controls
+  *padding-top: 3px;
+  *padding-bottom: 3px;
+
+  &.btn-large {
+    *padding-top: 7px;
+    *padding-bottom: 7px;
+  }
+  &.btn-small {
+    *padding-top: 3px;
+    *padding-bottom: 3px;
+  }
+  &.btn-mini {
+    *padding-top: 1px;
+    *padding-bottom: 1px;
+  }
+}
+
+
+// Link buttons
+// --------------------------------------------------
+
+// Make a button look and behave like a link
+.btn-link,
+.btn-link:active,
+.btn-link[disabled] {
+  background-color: transparent;
+  background-image: none;
+  .box-shadow(none);
+}
+.btn-link {
+  border-color: transparent;
+  cursor: pointer;
+  color: @linkColor;
+  .border-radius(0);
+}
+.btn-link:hover {
+  color: @linkColorHover;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover {
+  color: @grayDark;
+  text-decoration: none;
+}

+ 131 - 0
static/admin/css/bootstrap/carousel.less

@@ -0,0 +1,131 @@
+//
+// Carousel
+// --------------------------------------------------
+
+
+.carousel {
+  position: relative;
+  margin-bottom: @baseLineHeight;
+  line-height: 1;
+}
+
+.carousel-inner {
+  overflow: hidden;
+  width: 100%;
+  position: relative;
+}
+
+.carousel {
+
+  .item {
+    display: none;
+    position: relative;
+    .transition(.6s ease-in-out left);
+  }
+
+  // Account for jankitude on images
+  .item > 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 state
+  &:hover {
+    color: @white;
+    text-decoration: none;
+    .opacity(90);
+  }
+}
+
+
+// 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;
+}

+ 31 - 0
static/admin/css/bootstrap/close.less

@@ -0,0 +1,31 @@
+//
+// 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 {
+    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;
+}

+ 58 - 0
static/admin/css/bootstrap/code.less

@@ -0,0 +1,58 @@
+//
+// 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;
+}
+
+// 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(4px);
+
+  // 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;
+    background-color: transparent;
+    border: 0;
+  }
+}
+
+// Enable scrollable blocks of code
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}

+ 22 - 0
static/admin/css/bootstrap/component-animations.less

@@ -0,0 +1,22 @@
+//
+// 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;
+  }
+}

+ 210 - 0
static/admin/css/bootstrap/dropdowns.less

@@ -0,0 +1,210 @@
+//
+// 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
+  a {
+    display: block;
+    padding: 3px 20px;
+    clear: both;
+    font-weight: normal;
+    line-height: @baseLineHeight;
+    color: @dropdownLinkColor;
+    white-space: nowrap;
+  }
+}
+
+// Hover state
+// -----------
+.dropdown-menu li > a:hover,
+.dropdown-menu li > a:focus,
+.dropdown-submenu:hover > a {
+  text-decoration: none;
+  color: @dropdownLinkColorHover;
+  background-color: @dropdownLinkBackgroundHover;
+  #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%));
+}
+
+// Active state
+// ------------
+.dropdown-menu .active > a,
+.dropdown-menu .active > a:hover {
+  color: @dropdownLinkColorHover;
+  text-decoration: none;
+  outline: 0;
+  background-color: @dropdownLinkBackgroundActive;
+  #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%));
+}
+
+// Disabled state
+// --------------
+// Gray out text and ensure the hover state remains gray
+.dropdown-menu .disabled > a,
+.dropdown-menu .disabled > a:hover {
+  color: @grayLight;
+}
+// Nuke hover effects
+.dropdown-menu .disabled > a:hover {
+  text-decoration: none;
+  background-color: transparent;
+  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;
+  }
+}
+
+// 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;
+}
+.dropdown-submenu > .dropdown-menu {
+  top: 0;
+  left: 100%;
+  margin-top: -6px;
+  margin-left: -1px;
+  -webkit-border-radius: 0 6px 6px 6px;
+     -moz-border-radius: 0 6px 6px 6px;
+          border-radius: 0 6px 6px 6px;
+}
+.dropdown-submenu:hover > .dropdown-menu {
+  display: block;
+}
+
+.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;
+}
+
+
+// Tweak nav headers
+// -----------------
+// Increase padding from 15px to 20px on sides
+.dropdown .dropdown-menu .nav-header {
+  padding-left: 20px;
+  padding-right: 20px;
+}
+
+// Typeahead
+// ---------
+.typeahead {
+  margin-top: 2px; // give it some space to breathe
+  .border-radius(4px);
+}

+ 650 - 0
static/admin/css/bootstrap/forms.less

@@ -0,0 +1,650 @@
+//
+// Forms
+// --------------------------------------------------
+
+
+// GENERAL STYLES
+// --------------
+
+// Make all forms have space below them
+form {
+  margin: 0 0 @baseLineHeight;
+}
+
+fieldset {
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+
+// Groups of fields with labels on top (legends)
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: @baseLineHeight;
+  font-size: @baseFontSize * 1.5;
+  line-height: @baseLineHeight * 2;
+  color: @grayDark;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+
+  // Small
+  small {
+    font-size: @baseLineHeight * .75;
+    color: @grayLight;
+  }
+}
+
+// Set font for forms
+label,
+input,
+button,
+select,
+textarea {
+  #font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here
+}
+input,
+button,
+select,
+textarea {
+  font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element)
+}
+
+// Identify controls by their labels
+label {
+  display: block;
+  margin-bottom: 5px;
+}
+
+// Form controls
+// -------------------------
+
+// Shared size and type resets
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+  display: inline-block;
+  height: @baseLineHeight;
+  padding: 4px 6px;
+  margin-bottom: 9px;
+  font-size: @baseFontSize;
+  line-height: @baseLineHeight;
+  color: @gray;
+  .border-radius(@inputBorderRadius);
+}
+
+// Reset appearance properties for textual inputs and textarea
+// Declare width for legacy (can't be on input[type=*] selectors or it's too specific)
+input,
+textarea,
+.uneditable-input {
+  width: 206px; // plus 12px padding and 2px border
+}
+// Reset height since textareas have rows
+textarea {
+  height: auto;
+}
+// Everything else
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+  background-color: @inputBackground;
+  border: 1px solid @inputBorder;
+  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));
+  .transition(~"border linear .2s, box-shadow linear .2s");
+
+  // Focus state
+  &:focus {
+    border-color: rgba(82,168,236,.8);
+    outline: 0;
+    outline: thin dotted \9; /* IE6-9 */
+    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6));
+  }
+}
+
+// Position radios and checkboxes better
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  *margin-top: 0; /* IE7 */
+  margin-top: 1px \9; /* IE8-9 */
+  line-height: normal;
+  cursor: pointer;
+}
+
+// Reset width of input images, buttons, radios, checkboxes
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+  width: auto; // Override of generic input selector
+}
+
+// Set the height of select and file controls to match text inputs
+select,
+input[type="file"] {
+  height: 30px; /* In IE7, the height of the select element cannot be changed by height, only font-size */
+  *margin-top: 4px; /* For IE7, add top margin to align select with labels */
+  line-height: 30px;
+}
+
+// Make select elements obey height by applying a border
+select {
+  width: 220px; // default input width + 10px of padding that doesn't get applied
+  border: 1px solid @inputBorder;
+  background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color
+}
+
+// Make multiple select elements height not fixed
+select[multiple],
+select[size] {
+  height: auto;
+}
+
+// Focus for select, file, radio, and checkbox
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  .tab-focus();
+}
+
+
+// Uneditable inputs
+// -------------------------
+
+// Make uneditable inputs look inactive
+.uneditable-input,
+.uneditable-textarea {
+  color: @grayLight;
+  background-color: darken(@inputBackground, 1%);
+  border-color: @inputBorder;
+  .box-shadow(inset 0 1px 2px rgba(0,0,0,.025));
+  cursor: not-allowed;
+}
+
+// For text that needs to appear as an input but should not be an input
+.uneditable-input {
+  overflow: hidden; // prevent text from wrapping, but still cut it off like an input does
+  white-space: nowrap;
+}
+
+// Make uneditable textareas behave like a textarea
+.uneditable-textarea {
+  width: auto;
+  height: auto;
+}
+
+
+// Placeholder
+// -------------------------
+
+// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn’t understand a selector
+input,
+textarea {
+  .placeholder();
+}
+
+
+// CHECKBOXES & RADIOS
+// -------------------
+
+// Indent the labels to position radios/checkboxes as hanging
+.radio,
+.checkbox {
+  min-height: 18px; // clear the floating input if there is no label text
+  padding-left: 18px;
+}
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+  float: left;
+  margin-left: -18px;
+}
+
+// Move the options list down to align with labels
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+  padding-top: 5px; // has to be padding because margin collaspes
+}
+
+// Radios and checkboxes on same line
+// TODO v3: Convert .inline to .control-inline
+.radio.inline,
+.checkbox.inline {
+  display: inline-block;
+  padding-top: 5px;
+  margin-bottom: 0;
+  vertical-align: middle;
+}
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+  margin-left: 10px; // space out consecutive inline controls
+}
+
+
+
+// INPUT SIZES
+// -----------
+
+// General classes for quick sizes
+.input-mini       { width: 60px; }
+.input-small      { width: 90px; }
+.input-medium     { width: 150px; }
+.input-large      { width: 210px; }
+.input-xlarge     { width: 270px; }
+.input-xxlarge    { width: 530px; }
+
+// Grid style input sizes
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+// Redeclare since the fluid row class is more specific
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+  float: none;
+  margin-left: 0;
+}
+// Ensure input-prepend/append never wraps
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+  display: inline-block;
+}
+
+
+
+// GRID SIZING FOR INPUTS
+// ----------------------
+
+// Grid sizes
+#grid > .input(@gridColumnWidth, @gridGutterWidth);
+
+// Control row for multiple inputs per line
+.controls-row {
+  .clearfix(); // Clear the float from controls
+}
+.controls-row [class*="span"] {
+  float: left; // Float to collapse white-space for proper grid alignment
+}
+
+
+
+
+// DISABLED STATE
+// --------------
+
+// Disabled and read-only inputs
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+  cursor: not-allowed;
+  background-color: @inputDisabledBackground;
+}
+// Explicitly reset the colors here
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+  background-color: transparent;
+}
+
+
+
+
+// FORM FIELD FEEDBACK STATES
+// --------------------------
+
+// Warning
+.control-group.warning {
+  .formFieldState(@warningText, @warningText, @warningBackground);
+}
+// Error
+.control-group.error {
+  .formFieldState(@errorText, @errorText, @errorBackground);
+}
+// Success
+.control-group.success {
+  .formFieldState(@successText, @successText, @successBackground);
+}
+// Success
+.control-group.info {
+  .formFieldState(@infoText, @infoText, @infoBackground);
+}
+
+// HTML5 invalid states
+// Shares styles with the .control-group.error above
+input:focus:required:invalid,
+textarea:focus:required:invalid,
+select:focus:required:invalid {
+  color: #b94a48;
+  border-color: #ee5f5b;
+  &:focus {
+    border-color: darken(#ee5f5b, 10%);
+    .box-shadow(0 0 6px lighten(#ee5f5b, 20%));
+  }
+}
+
+
+
+// FORM ACTIONS
+// ------------
+
+.form-actions {
+  padding: (@baseLineHeight - 1) 20px @baseLineHeight;
+  margin-top: @baseLineHeight;
+  margin-bottom: @baseLineHeight;
+  background-color: @formActionsBackground;
+  border-top: 1px solid #e5e5e5;
+  .clearfix(); // Adding clearfix to allow for .pull-right button containers
+}
+
+
+
+// HELP TEXT
+// ---------
+
+.help-block,
+.help-inline {
+  color: lighten(@textColor, 15%); // lighten the text some for contrast
+}
+
+.help-block {
+  display: block; // account for any element using help-block
+  margin-bottom: @baseLineHeight / 2;
+}
+
+.help-inline {
+  display: inline-block;
+  .ie7-inline-block();
+  vertical-align: middle;
+  padding-left: 5px;
+}
+
+
+
+// INPUT GROUPS
+// ------------
+
+// Allow us to put symbols and text within the input field for a cleaner look
+.input-append,
+.input-prepend {
+  margin-bottom: 5px;
+  font-size: 0;
+  white-space: nowrap; // Prevent span and input from separating
+
+  input,
+  select,
+  .uneditable-input {
+    position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness
+    margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms
+    *margin-left: 0;
+    font-size: @baseFontSize;
+    vertical-align: top;
+    .border-radius(0 @inputBorderRadius @inputBorderRadius 0);
+    // Make input on top when focused so blue border and shadow always show
+    &:focus {
+      z-index: 2;
+    }
+  }
+  .add-on {
+    display: inline-block;
+    width: auto;
+    height: @baseLineHeight;
+    min-width: 16px;
+    padding: 4px 5px;
+    font-size: @baseFontSize;
+    font-weight: normal;
+    line-height: @baseLineHeight;
+    text-align: center;
+    text-shadow: 0 1px 0 @white;
+    background-color: @grayLighter;
+    border: 1px solid #ccc;
+  }
+  .add-on,
+  .btn {
+    vertical-align: top;
+    .border-radius(0);
+  }
+  .active {
+    background-color: lighten(@green, 30);
+    border-color: @green;
+  }
+}
+.input-prepend {
+  .add-on,
+  .btn {
+    margin-right: -1px;
+  }
+  .add-on:first-child,
+  .btn:first-child {
+    .border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
+  }
+}
+.input-append {
+  input,
+  select,
+  .uneditable-input {
+    .border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
+  }
+  .add-on,
+  .btn {
+    margin-left: -1px;
+  }
+  .add-on:last-child,
+  .btn:last-child {
+    .border-radius(0 @inputBorderRadius @inputBorderRadius 0);
+  }
+}
+// Remove all border-radius for inputs with both prepend and append
+.input-prepend.input-append {
+  input,
+  select,
+  .uneditable-input {
+    .border-radius(0);
+  }
+  .add-on:first-child,
+  .btn:first-child {
+    margin-right: -1px;
+    .border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
+  }
+  .add-on:last-child,
+  .btn:last-child {
+    margin-left: -1px;
+    .border-radius(0 @inputBorderRadius @inputBorderRadius 0);
+  }
+}
+
+
+
+// SEARCH FORM
+// -----------
+
+input.search-query {
+  padding-right: 14px;
+  padding-right: 4px \9;
+  padding-left: 14px;
+  padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */
+  margin-bottom: 0; // Remove the default margin on all inputs
+  .border-radius(15px);
+}
+
+/* Allow for input prepend/append in search forms */
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+  .border-radius(0); // Override due to specificity
+}
+.form-search .input-append .search-query {
+  .border-radius(14px 0 0 14px);
+}
+.form-search .input-append .btn {
+  .border-radius(0 14px 14px 0);
+}
+.form-search .input-prepend .search-query {
+  .border-radius(0 14px 14px 0);
+}
+.form-search .input-prepend .btn {
+  .border-radius(14px 0 0 14px);
+}
+
+
+
+
+// HORIZONTAL & VERTICAL FORMS
+// ---------------------------
+
+// Common properties
+// -----------------
+
+.form-search,
+.form-inline,
+.form-horizontal {
+  input,
+  textarea,
+  select,
+  .help-inline,
+  .uneditable-input,
+  .input-prepend,
+  .input-append {
+    display: inline-block;
+    .ie7-inline-block();
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  // Re-hide hidden elements due to specifity
+  .hide {
+    display: none;
+  }
+}
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+  display: inline-block;
+}
+// Remove margin for input-prepend/-append
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+  margin-bottom: 0;
+}
+// Inline checkbox/radio labels (remove padding on left)
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+  padding-left: 0;
+  margin-bottom: 0;
+  vertical-align: middle;
+}
+// Remove float and margin, set to inline-block
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+  float: left;
+  margin-right: 3px;
+  margin-left: 0;
+}
+
+
+// Margin to space out fieldsets
+.control-group {
+  margin-bottom: @baseLineHeight / 2;
+}
+
+// Legend collapses margin, so next element is responsible for spacing
+legend + .control-group {
+  margin-top: @baseLineHeight;
+  -webkit-margin-top-collapse: separate;
+}
+
+// Horizontal-specific styles
+// --------------------------
+
+.form-horizontal {
+  // Increase spacing between groups
+  .control-group {
+    margin-bottom: @baseLineHeight;
+    .clearfix();
+  }
+  // Float the labels left
+  .control-label {
+    float: left;
+    width: @horizontalComponentOffset - 20;
+    padding-top: 5px;
+    text-align: right;
+  }
+  // Move over all input controls and content
+  .controls {
+    // Super jank IE7 fix to ensure the inputs in .input-append and input-prepend
+    // don't inherit the margin of the parent, in this case .controls
+    *display: inline-block;
+    *padding-left: 20px;
+    margin-left: @horizontalComponentOffset;
+    *margin-left: 0;
+    &:first-child {
+      *padding-left: @horizontalComponentOffset;
+    }
+  }
+  // Remove bottom margin on block level help text since that's accounted for on .control-group
+  .help-block {
+    margin-bottom: 0;
+  }
+  // And apply it only to .help-block instances that follow a form control
+  input,
+  select,
+  textarea {
+    + .help-block {
+      margin-top: @baseLineHeight / 2;
+    }
+  }
+  // Move over buttons in .form-actions to align with .controls
+  .form-actions {
+    padding-left: @horizontalComponentOffset;
+  }
+}

+ 21 - 0
static/admin/css/bootstrap/grid.less

@@ -0,0 +1,21 @@
+//
+// Grid system
+// --------------------------------------------------
+
+
+// Fixed (940px)
+#grid > .core(@gridColumnWidth, @gridGutterWidth);
+
+// Fluid (940px)
+#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
+
+// Reset utility classes due to specificity
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+  display: none;
+}
+
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+  float: right;
+}

+ 24 - 0
static/admin/css/bootstrap/hero-unit.less

@@ -0,0 +1,24 @@
+//
+// Hero unit
+// --------------------------------------------------
+
+
+.hero-unit {
+  padding: 60px;
+  margin-bottom: 30px;
+  background-color: @heroUnitBackground;
+  .border-radius(6px);
+  h1 {
+    margin-bottom: 0;
+    font-size: 60px;
+    line-height: 1;
+    color: @heroUnitHeadingColor;
+    letter-spacing: -1px;
+  }
+  p {
+    font-size: 18px;
+    font-weight: 200;
+    line-height: @baseLineHeight * 1.5;
+    color: @heroUnitLeadColor;
+  }
+}

+ 72 - 0
static/admin/css/bootstrap/labels-badges.less

@@ -0,0 +1,72 @@
+//
+// Labels and badges
+// --------------------------------------------------
+
+
+// Base classes
+.label,
+.badge {
+  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 {
+  padding: 1px 4px 2px;
+  .border-radius(3px);
+}
+.badge {
+  padding: 1px 9px 2px;
+  .border-radius(9px);
+}
+
+// Hover state, but only for links
+a {
+  &.label:hover,
+  &.badge:hover {
+    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;
+  }
+}

+ 16 - 0
static/admin/css/bootstrap/layouts.less

@@ -0,0 +1,16 @@
+//
+// 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();
+}

+ 681 - 0
static/admin/css/bootstrap/mixins.less

@@ -0,0 +1,681 @@
+//
+// Mixins
+// --------------------------------------------------
+
+
+// UTILITY MIXINS
+// --------------------------------------------------
+
+// Clearfix
+// --------
+// For clearing floats like a boss h5bp.com/q
+.clearfix {
+  *zoom: 1;
+  &:before,
+  &:after {
+    display: table;
+    content: "";
+    // Fixes Opera/contenteditable bug:
+    // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952
+    line-height: 0;
+  }
+  &:after {
+    clear: both;
+  }
+}
+
+// Webkit-style focus
+// ------------------
+.tab-focus() {
+  // Default
+  outline: thin dotted #333;
+  // Webkit
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+// Center-align a block level element
+// ----------------------------------
+.center-block() {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+// IE7 inline-block
+// ----------------
+.ie7-inline-block() {
+  *display: inline; /* IE7 inline-block hack */
+  *zoom: 1;
+}
+
+// IE7 likes to collapse whitespace on either side of the inline-block elements.
+// Ems because we're attempting to match the width of a space character. Left
+// version is for form buttons, which typically come after other elements, and
+// right version is for icons, which come before. Applying both is ok, but it will
+// mean that space between those elements will be .6em (~2 space characters) in IE7,
+// instead of the 1 space in other browsers.
+.ie7-restore-left-whitespace() {
+  *margin-left: .3em;
+
+  &:first-child {
+    *margin-left: 0;
+  }
+}
+
+.ie7-restore-right-whitespace() {
+  *margin-right: .3em;
+}
+
+// Sizing shortcuts
+// -------------------------
+.size(@height, @width) {
+  width: @width;
+  height: @height;
+}
+.square(@size) {
+  .size(@size, @size);
+}
+
+// Placeholder text
+// -------------------------
+.placeholder(@color: @placeholderText) {
+  &:-moz-placeholder {
+    color: @color;
+  }
+  &:-ms-input-placeholder {
+    color: @color;
+  }
+  &::-webkit-input-placeholder {
+    color: @color;
+  }
+}
+
+// Text overflow
+// -------------------------
+// Requires inline-block or block for proper styling
+.text-overflow() {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+// CSS image replacement
+// -------------------------
+// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
+.hide-text {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+
+
+// FONTS
+// --------------------------------------------------
+
+#font {
+  #family {
+    .serif() {
+      font-family: @serifFontFamily;
+    }
+    .sans-serif() {
+      font-family: @sansFontFamily;
+    }
+    .monospace() {
+      font-family: @monoFontFamily;
+    }
+  }
+  .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    font-size: @size;
+    font-weight: @weight;
+    line-height: @lineHeight;
+  }
+  .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    #font > #family > .serif;
+    #font > .shorthand(@size, @weight, @lineHeight);
+  }
+  .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    #font > #family > .sans-serif;
+    #font > .shorthand(@size, @weight, @lineHeight);
+  }
+  .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    #font > #family > .monospace;
+    #font > .shorthand(@size, @weight, @lineHeight);
+  }
+}
+
+
+// FORMS
+// --------------------------------------------------
+
+// Block level inputs
+.input-block-level {
+  display: block;
+  width: 100%;
+  min-height: 30px;        // Make inputs at least the height of their button counterpart
+  .box-sizing(border-box); // Makes inputs behave like true block-level elements
+}
+
+
+
+// Mixin for form field states
+.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) {
+  // Set the text color
+  > label,
+  .help-block,
+  .help-inline {
+    color: @textColor;
+  }
+  // Style inputs accordingly
+  .checkbox,
+  .radio,
+  input,
+  select,
+  textarea {
+    color: @textColor;
+  }
+  input,
+  select,
+  textarea {
+    border-color: @borderColor;
+    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
+    &:focus {
+      border-color: darken(@borderColor, 10%);
+      .box-shadow(inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%));
+    }
+  }
+  // Give a small background color for input-prepend/-append
+  .input-prepend .add-on,
+  .input-append .add-on {
+    color: @textColor;
+    background-color: @backgroundColor;
+    border-color: @textColor;
+  }
+}
+
+
+
+// CSS3 PROPERTIES
+// --------------------------------------------------
+
+// Border Radius
+.border-radius(@radius) {
+  -webkit-border-radius: @radius;
+     -moz-border-radius: @radius;
+          border-radius: @radius;
+}
+
+// Single Corner Border Radius
+.border-top-left-radius(@radius) {
+  -webkit-border-top-left-radius: @radius;
+      -moz-border-radius-topleft: @radius;
+          border-top-left-radius: @radius;
+}
+.border-top-right-radius(@radius) {
+  -webkit-border-top-right-radius: @radius;
+      -moz-border-radius-topright: @radius;
+          border-top-right-radius: @radius;
+}
+.border-bottom-right-radius(@radius) {
+  -webkit-border-bottom-right-radius: @radius;
+      -moz-border-radius-bottomright: @radius;
+          border-bottom-right-radius: @radius;
+}
+.border-bottom-left-radius(@radius) {
+  -webkit-border-bottom-left-radius: @radius;
+      -moz-border-radius-bottomleft: @radius;
+          border-bottom-left-radius: @radius;
+}
+
+// Single Side Border Radius
+.border-top-radius(@radius) {
+  .border-top-right-radius(@radius);
+  .border-top-left-radius(@radius);
+}
+.border-right-radius(@radius) {
+  .border-top-right-radius(@radius);
+  .border-bottom-right-radius(@radius);
+}
+.border-bottom-radius(@radius) {
+  .border-bottom-right-radius(@radius);
+  .border-bottom-left-radius(@radius);
+}
+.border-left-radius(@radius) {
+  .border-top-left-radius(@radius);
+  .border-bottom-left-radius(@radius);
+}
+
+// Drop shadows
+.box-shadow(@shadowA, @shadowB:X, ...){
+  // Multiple shadow solution from http://toekneestuck.com/blog/2012/05/15/less-css-arguments-variable/
+  @props: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
+  -webkit-box-shadow: @props;
+     -moz-box-shadow: @props;
+          box-shadow: @props;
+}
+
+// Transitions
+.transition(@transition) {
+  -webkit-transition: @transition;
+     -moz-transition: @transition;
+       -o-transition: @transition;
+          transition: @transition;
+}
+.transition-delay(@transition-delay) {
+  -webkit-transition-delay: @transition-delay;
+     -moz-transition-delay: @transition-delay;
+       -o-transition-delay: @transition-delay;
+          transition-delay: @transition-delay;
+}
+
+// Transformations
+.rotate(@degrees) {
+  -webkit-transform: rotate(@degrees);
+     -moz-transform: rotate(@degrees);
+      -ms-transform: rotate(@degrees);
+       -o-transform: rotate(@degrees);
+          transform: rotate(@degrees);
+}
+.scale(@ratio) {
+  -webkit-transform: scale(@ratio);
+     -moz-transform: scale(@ratio);
+      -ms-transform: scale(@ratio);
+       -o-transform: scale(@ratio);
+          transform: scale(@ratio);
+}
+.translate(@x, @y) {
+  -webkit-transform: translate(@x, @y);
+     -moz-transform: translate(@x, @y);
+      -ms-transform: translate(@x, @y);
+       -o-transform: translate(@x, @y);
+          transform: translate(@x, @y);
+}
+.skew(@x, @y) {
+  -webkit-transform: skew(@x, @y);
+     -moz-transform: skew(@x, @y);
+      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twitter/bootstrap/issues/4885
+       -o-transform: skew(@x, @y);
+          transform: skew(@x, @y);
+}
+.translate3d(@x, @y, @z) {
+  -webkit-transform: translate3d(@x, @y, @z);
+     -moz-transform: translate3d(@x, @y, @z);
+       -o-transform: translate3d(@x, @y, @z);
+          transform: translate3d(@x, @y, @z);
+}
+
+// Backface visibility
+// Prevent browsers from flickering when using CSS 3D transforms.
+// Default value is `visible`, but can be changed to `hidden
+// See git pull https://github.com/dannykeane/bootstrap.git backface-visibility for examples
+.backface-visibility(@visibility){
+	-webkit-backface-visibility: @visibility;
+	   -moz-backface-visibility: @visibility;
+	        backface-visibility: @visibility;
+}
+
+// Background clipping
+// Heads up: FF 3.6 and under need "padding" instead of "padding-box"
+.background-clip(@clip) {
+  -webkit-background-clip: @clip;
+     -moz-background-clip: @clip;
+          background-clip: @clip;
+}
+
+// Background sizing
+.background-size(@size){
+  -webkit-background-size: @size;
+     -moz-background-size: @size;
+       -o-background-size: @size;
+          background-size: @size;
+}
+
+
+// Box sizing
+.box-sizing(@boxmodel) {
+  -webkit-box-sizing: @boxmodel;
+     -moz-box-sizing: @boxmodel;
+          box-sizing: @boxmodel;
+}
+
+// User select
+// For selecting text on the page
+.user-select(@select) {
+  -webkit-user-select: @select;
+     -moz-user-select: @select;
+      -ms-user-select: @select;
+       -o-user-select: @select;
+          user-select: @select;
+}
+
+// Resize anything
+.resizable(@direction) {
+  resize: @direction; // Options: horizontal, vertical, both
+  overflow: auto; // Safari fix
+}
+
+// CSS3 Content Columns
+.content-columns(@columnCount, @columnGap: @gridGutterWidth) {
+  -webkit-column-count: @columnCount;
+     -moz-column-count: @columnCount;
+          column-count: @columnCount;
+  -webkit-column-gap: @columnGap;
+     -moz-column-gap: @columnGap;
+          column-gap: @columnGap;
+}
+
+// Optional hyphenation
+.hyphens(@mode: auto) {
+  word-wrap: break-word;
+  -webkit-hyphens: @mode;
+     -moz-hyphens: @mode;
+      -ms-hyphens: @mode;
+       -o-hyphens: @mode;
+          hyphens: @mode;
+}
+
+// Opacity
+.opacity(@opacity) {
+  opacity: @opacity / 100;
+  filter: ~"alpha(opacity=@{opacity})";
+}
+
+
+
+// BACKGROUNDS
+// --------------------------------------------------
+
+// Add an alphatransparency value to any background or border color (via Elyse Holladay)
+#translucent {
+  .background(@color: @white, @alpha: 1) {
+    background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
+  }
+  .border(@color: @white, @alpha: 1) {
+    border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
+    .background-clip(padding-box);
+  }
+}
+
+// Gradient Bar Colors for buttons and alerts
+.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) {
+  color: @textColor;
+  text-shadow: @textShadow;
+  #gradient > .vertical(@primaryColor, @secondaryColor);
+  border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%);
+  border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%);
+}
+
+// Gradients
+#gradient {
+  .horizontal(@startColor: #555, @endColor: #333) {
+    background-color: @endColor;
+    background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+
+    background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+
+    background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+
+    background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10
+    background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10
+    background-repeat: repeat-x;
+    filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@startColor),argb(@endColor))); // IE9 and down
+  }
+  .vertical(@startColor: #555, @endColor: #333) {
+    background-color: mix(@startColor, @endColor, 60%);
+    background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+
+    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+
+    background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+
+    background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10
+    background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10
+    background-repeat: repeat-x;
+    filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down
+  }
+  .directional(@startColor: #555, @endColor: #333, @deg: 45deg) {
+    background-color: @endColor;
+    background-repeat: repeat-x;
+    background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+
+    background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+
+    background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10
+    background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10
+  }
+  .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) {
+    background-color: mix(@midColor, @endColor, 80%);
+    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor));
+    background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor);
+    background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor);
+    background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor);
+    background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor);
+    background-repeat: no-repeat;
+    filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback
+  }
+  .radial(@innerColor: #555, @outerColor: #333)  {
+    background-color: @outerColor;
+    background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor));
+    background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor);
+    background-image: -moz-radial-gradient(circle, @innerColor, @outerColor);
+    background-image: -o-radial-gradient(circle, @innerColor, @outerColor);
+    background-repeat: no-repeat;
+  }
+  .striped(@color: #555, @angle: 45deg) {
+    background-color: @color;
+    background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent));
+    background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+    background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+    background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+    background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+  }
+}
+// Reset filters for IE
+.reset-filter() {
+  filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
+}
+
+
+
+// COMPONENT MIXINS
+// --------------------------------------------------
+
+// Horizontal dividers
+// -------------------------
+// Dividers (basically an hr) within dropdowns and nav lists
+.nav-divider(@top: #e5e5e5, @bottom: @white) {
+  // IE7 needs a set width since we gave a height. Restricting just
+  // to IE7 to keep the 1px left/right space in other browsers.
+  // It is unclear where IE is getting the extra space that we need
+  // to negative-margin away, but so it goes.
+  *width: 100%;
+  height: 1px;
+  margin: ((@baseLineHeight / 2) - 1) 1px; // 8px 1px
+  *margin: -5px 0 5px;
+  overflow: hidden;
+  background-color: @top;
+  border-bottom: 1px solid @bottom;
+}
+
+// Button backgrounds
+// ------------------
+.buttonBackground(@startColor, @endColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) {
+  // gradientBar will set the background to a pleasing blend of these, to support IE<=9
+  .gradientBar(@startColor, @endColor, @textColor, @textShadow);
+  *background-color: @endColor; /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+  .reset-filter();
+
+  // in these cases the gradient won't cover the background, so we override
+  &:hover, &:active, &.active, &.disabled, &[disabled] {
+    color: @textColor;
+    background-color: @endColor;
+    *background-color: darken(@endColor, 5%);
+  }
+
+  // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves
+  &:active,
+  &.active {
+    background-color: darken(@endColor, 10%) e("\9");
+  }
+}
+
+// Navbar vertical align
+// -------------------------
+// Vertically center elements in the navbar.
+// Example: an element has a height of 30px, so write out `.navbarVerticalAlign(30px);` to calculate the appropriate top margin.
+.navbarVerticalAlign(@elementHeight) {
+  margin-top: (@navbarHeight - @elementHeight) / 2;
+}
+
+
+
+// Grid System
+// -----------
+
+// Centered container element
+.container-fixed() {
+  margin-right: auto;
+  margin-left: auto;
+  .clearfix();
+}
+
+// Table columns
+.tableColumns(@columnSpan: 1) {
+  float: none; // undo default grid column styles
+  width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; // 16 is total padding on left and right of table cells
+  margin-left: 0; // undo default grid column styles
+}
+
+// Make a Grid
+// Use .makeRow and .makeColumn to assign semantic layouts grid system behavior
+.makeRow() {
+  margin-left: @gridGutterWidth * -1;
+  .clearfix();
+}
+.makeColumn(@columns: 1, @offset: 0) {
+  float: left;
+  margin-left: (@gridColumnWidth * @offset) + (@gridGutterWidth * (@offset - 1)) + (@gridGutterWidth * 2);
+  width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1));
+}
+
+// The Grid
+#grid {
+
+  .core (@gridColumnWidth, @gridGutterWidth) {
+
+    .spanX (@index) when (@index > 0) {
+      (~".span@{index}") { .span(@index); }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    .offsetX (@index) when (@index > 0) {
+      (~".offset@{index}") { .offset(@index); }
+      .offsetX(@index - 1);
+    }
+    .offsetX (0) {}
+
+    .offset (@columns) {
+      margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1));
+    }
+
+    .span (@columns) {
+      width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1));
+    }
+
+    .row {
+      margin-left: @gridGutterWidth * -1;
+      .clearfix();
+    }
+
+    [class*="span"] {
+      float: left;
+      min-height: 1px; // prevent collapsing columns
+      margin-left: @gridGutterWidth;
+    }
+
+    // Set the container width, and override it for fixed navbars in media queries
+    .container,
+    .navbar-static-top .container,
+    .navbar-fixed-top .container,
+    .navbar-fixed-bottom .container { .span(@gridColumns); }
+
+    // generate .spanX and .offsetX
+    .spanX (@gridColumns);
+    .offsetX (@gridColumns);
+
+  }
+
+  .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) {
+
+    .spanX (@index) when (@index > 0) {
+      (~".span@{index}") { .span(@index); }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    .offsetX (@index) when (@index > 0) {
+      (~'.offset@{index}') { .offset(@index); }
+      (~'.offset@{index}:first-child') { .offsetFirstChild(@index); }
+      .offsetX(@index - 1);
+    }
+    .offsetX (0) {}
+
+    .offset (@columns) {
+      margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2);
+  	  *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%);
+    }
+
+    .offsetFirstChild (@columns) {
+      margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth);
+      *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%);
+    }
+
+    .span (@columns) {
+      width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1));
+      *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%);
+    }
+
+    .row-fluid {
+      width: 100%;
+      .clearfix();
+      [class*="span"] {
+        .input-block-level();
+        float: left;
+        margin-left: @fluidGridGutterWidth;
+        *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%);
+      }
+      [class*="span"]:first-child {
+        margin-left: 0;
+      }
+
+      // generate .spanX and .offsetX
+      .spanX (@gridColumns);
+      .offsetX (@gridColumns);
+    }
+
+  }
+
+  .input(@gridColumnWidth, @gridGutterWidth) {
+
+    .spanX (@index) when (@index > 0) {
+      (~"input.span@{index}, textarea.span@{index}, .uneditable-input.span@{index}") { .span(@index); }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    .span(@columns) {
+      width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14;
+    }
+
+    input,
+    textarea,
+    .uneditable-input {
+      margin-left: 0; // override margin-left from core grid system
+    }
+
+    // Space grid-sized controls properly if multiple per line
+    .controls-row [class*="span"] + [class*="span"] {
+      margin-left: @gridGutterWidth;
+    }
+
+    // generate .spanX
+    .spanX (@gridColumns);
+
+  }
+
+}

+ 98 - 0
static/admin/css/bootstrap/modals.less

@@ -0,0 +1,98 @@
+//
+// Modals
+// --------------------------------------------------
+
+
+// Recalculate z-index where appropriate,
+// but only apply to elements within modal
+.modal-open .modal {
+  .dropdown-menu {  z-index: @zindexDropdown + @zindexModal; }
+  .dropdown.open { *z-index: @zindexDropdown + @zindexModal; }
+  .popover       {  z-index: @zindexPopover  + @zindexModal; }
+  .tooltip       {  z-index: @zindexTooltip  + @zindexModal; }
+}
+
+// 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: 50%;
+  left: 50%;
+  z-index: @zindexModal;
+  overflow: auto;
+  width: 560px;
+  margin: -250px 0 0 -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);
+  &.fade {
+    .transition(e('opacity .3s linear, top .3s ease-out'));
+    top: -25%;
+  }
+  &.fade.in { top: 50%; }
+}
+.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 {
+  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;
+  }
+}

+ 475 - 0
static/admin/css/bootstrap/navbar.less

@@ -0,0 +1,475 @@
+//
+// Navbars (Redux)
+// --------------------------------------------------
+
+
+// COMMON STYLES
+// -------------
+
+// Base class and wrapper
+.navbar {
+  overflow: visible;
+  margin-bottom: @baseLineHeight;
+  color: @navbarText;
+
+  // 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(4px);
+  .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;
+}
+
+
+// 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 {
+    text-decoration: none;
+  }
+}
+
+// Plain text in topbar
+// -------------------------
+.navbar-text {
+  margin-bottom: 0;
+  line-height: @navbarHeight;
+}
+
+// Janky solution for now to account for links outside the .nav
+// -------------------------
+.navbar-link {
+  color: @navbarLinkColor;
+  &:hover {
+    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 {
+  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: 6px;
+    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
+  }
+}
+
+
+
+// Static navbar
+// -------------------------
+
+.navbar-static-top {
+  position: static;
+  width: 100%;
+  margin-bottom: 0; // remove 18px margin for default navbar
+  .navbar-inner {
+    .border-radius(0);
+  }
+}
+
+
+
+// Fixed navbar
+// -------------------------
+
+// Shared (top/bottom) styles
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: @zindexFixedNavbar;
+  margin-bottom: 0; // remove 18px margin for default navbar
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+  border-width: 0 0 1px;
+}
+.navbar-fixed-bottom .navbar-inner {
+  border-width: 1px 0 0;
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+  padding-left:  0;
+  padding-right: 0;
+  .border-radius(0);
+}
+
+// Reset container width
+// Required here as we reset the width earlier on and the grid mixins don't override early enough
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+  #grid > .core > .span(@gridColumns);
+}
+
+// Fixed to top
+.navbar-fixed-top {
+  top: 0;
+}
+.navbar-fixed-top,
+.navbar-static-top {
+  .navbar-inner {
+    .box-shadow(inset 0 -1px 0 rgba(0,0,0,.1), 0 1px 10px rgba(0,0,0,.1));
+  }
+}
+
+// Fixed to bottom
+.navbar-fixed-bottom {
+  bottom: 0;
+  .navbar-inner {
+    .box-shadow(inset 0 1px 0 rgba(0,0,0,.1), 0 -1px 10px rgba(0,0,0,.1));
+  }
+}
+
+
+
+// NAVIGATION
+// ----------
+
+.navbar .nav {
+  position: relative;
+  left: 0;
+  display: block;
+  float: left;
+  margin: 0 10px 0 0;
+}
+.navbar .nav.pull-right {
+  float: right; // redeclare due to specificity
+  margin-right: 0; // remove margin on float right nav
+}
+.navbar .nav > li {
+  float: left;
+}
+
+// Links
+.navbar .nav > li > a {
+  float: none;
+  // Vertically center the text given @navbarHeight
+  padding: ((@navbarHeight - @baseLineHeight) / 2) 15px ((@navbarHeight - @baseLineHeight) / 2);
+  color: @navbarLinkColor;
+  text-decoration: none;
+  text-shadow: 0 1px 0 @navbarBackgroundHighlight;
+}
+.navbar .nav .dropdown-toggle .caret {
+  margin-top: 8px;
+}
+
+// Hover
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+  background-color: @navbarLinkBackgroundHover; // "transparent" is default to differentiate :hover from .active
+  color: @navbarLinkColorHover;
+  text-decoration: none;
+}
+
+// Active nav items
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+  color: @navbarLinkColorActive;
+  text-decoration: none;
+  background-color: @navbarLinkBackgroundActive;
+  .box-shadow(inset 0 3px 8px rgba(0,0,0,.125));
+}
+
+// Navbar button for toggling navbar items in responsive layouts
+// These definitions need to come after '.navbar .btn'
+.navbar .btn-navbar {
+  display: none;
+  float: right;
+  padding: 7px 10px;
+  margin-left: 5px;
+  margin-right: 5px;
+  .buttonBackground(darken(@navbarBackgroundHighlight, 5%), darken(@navbarBackground, 5%));
+  .box-shadow(inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075));
+}
+.navbar .btn-navbar .icon-bar {
+  display: block;
+  width: 18px;
+  height: 2px;
+  background-color: #f5f5f5;
+  .border-radius(1px);
+  .box-shadow(0 1px 0 rgba(0,0,0,.25));
+}
+.btn-navbar .icon-bar + .icon-bar {
+  margin-top: 3px;
+}
+
+
+
+// Dropdown menus
+// --------------
+
+// Menu position and menu carets
+.navbar .nav > li > .dropdown-menu {
+  &:before {
+    content: '';
+    display: inline-block;
+    border-left:   7px solid transparent;
+    border-right:  7px solid transparent;
+    border-bottom: 7px solid #ccc;
+    border-bottom-color: @dropdownBorder;
+    position: absolute;
+    top: -7px;
+    left: 9px;
+  }
+  &:after {
+    content: '';
+    display: inline-block;
+    border-left:   6px solid transparent;
+    border-right:  6px solid transparent;
+    border-bottom: 6px solid @dropdownBackground;
+    position: absolute;
+    top: -6px;
+    left: 10px;
+  }
+}
+// Menu position and menu caret support for dropups via extra dropup class
+.navbar-fixed-bottom .nav > li > .dropdown-menu {
+  &:before {
+    border-top: 7px solid #ccc;
+    border-top-color: @dropdownBorder;
+    border-bottom: 0;
+    bottom: -7px;
+    top: auto;
+  }
+  &:after {
+    border-top: 6px solid @dropdownBackground;
+    border-bottom: 0;
+    bottom: -6px;
+    top: auto;
+  }
+}
+
+// Remove background color from open dropdown
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+  background-color: @navbarLinkBackgroundActive;
+  color: @navbarLinkColorActive;
+}
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+  border-top-color: @navbarLinkColor;
+  border-bottom-color: @navbarLinkColor;
+}
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+  border-top-color: @navbarLinkColorActive;
+  border-bottom-color: @navbarLinkColorActive;
+}
+
+// Right aligned menus need alt position
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+  left: auto;
+  right: 0;
+  &:before {
+    left: auto;
+    right: 12px;
+  }
+  &:after {
+    left: auto;
+    right: 13px;
+  }
+  .dropdown-menu {
+    left: auto;
+    right: 100%;
+    margin-left: 0;
+    margin-right: -1px;
+    .border-radius(6px 0 6px 6px);
+  }
+}
+
+
+// Inverted navbar
+// -------------------------
+
+.navbar-inverse {
+  color: @navbarInverseText;
+
+  .navbar-inner {
+    #gradient > .vertical(@navbarInverseBackgroundHighlight, @navbarInverseBackground);
+    border-color: @navbarInverseBorder;
+  }
+
+  .brand,
+  .nav > li > a {
+    color: @navbarInverseLinkColor;
+    text-shadow: 0 -1px 0 rgba(0,0,0,.25);
+    &:hover {
+      color: @navbarInverseLinkColorHover;
+    }
+  }
+
+  .nav > li > a:focus,
+  .nav > li > a:hover {
+    background-color: @navbarInverseLinkBackgroundHover;
+    color: @navbarInverseLinkColorHover;
+  }
+
+  .nav .active > a,
+  .nav .active > a:hover,
+  .nav .active > a:focus {
+    color: @navbarInverseLinkColorActive;
+    background-color: @navbarInverseLinkBackgroundActive;
+  }
+
+  // Inline text links
+  .navbar-link {
+    color: @navbarInverseLinkColor;
+    &:hover {
+      color: @navbarInverseLinkColorHover;
+    }
+  }
+
+  // Dividers in navbar
+  .divider-vertical {
+    border-left-color: @navbarInverseBackground;
+    border-right-color: @navbarInverseBackgroundHighlight;
+  }
+
+  // Dropdowns
+  .nav li.dropdown.open > .dropdown-toggle,
+  .nav li.dropdown.active > .dropdown-toggle,
+  .nav li.dropdown.open.active > .dropdown-toggle {
+    background-color: @navbarInverseLinkBackgroundActive;
+    color: @navbarInverseLinkColorActive;
+  }
+  .nav li.dropdown > .dropdown-toggle .caret {
+    border-top-color: @navbarInverseLinkColor;
+    border-bottom-color: @navbarInverseLinkColor;
+  }
+  .nav li.dropdown.open > .dropdown-toggle .caret,
+  .nav li.dropdown.active > .dropdown-toggle .caret,
+  .nav li.dropdown.open.active > .dropdown-toggle .caret {
+    border-top-color: @navbarInverseLinkColorActive;
+    border-bottom-color: @navbarInverseLinkColorActive;
+  }
+
+  // Navbar search
+  .navbar-search {
+    .search-query {
+      color: @white;
+      background-color: @navbarInverseSearchBackground;
+      border-color: @navbarInverseSearchBorder;
+      .box-shadow(inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15));
+      .transition(none);
+      .placeholder(@navbarInverseSearchPlaceholderColor);
+
+      // Focus states (we use .focused since IE7-8 and down doesn't support :focus)
+      &:focus,
+      &.focused {
+        padding: 5px 15px;
+        color: @grayDark;
+        text-shadow: 0 1px 0 @white;
+        background-color: @navbarInverseSearchBackgroundFocus;
+        border: 0;
+        .box-shadow(0 0 3px rgba(0,0,0,.15));
+        outline: 0;
+      }
+    }
+  }
+
+  // Navbar collapse button
+  .btn-navbar {
+    .buttonBackground(darken(@navbarInverseBackgroundHighlight, 5%), darken(@navbarInverseBackground, 5%));
+  }
+
+}
+
+
+

+ 384 - 0
static/admin/css/bootstrap/navs.less

@@ -0,0 +1,384 @@
+//
+// Navs
+// --------------------------------------------------
+
+
+// BASE CLASS
+// ----------
+
+.nav {
+  margin-left: 0;
+  margin-bottom: @baseLineHeight;
+  list-style: none;
+}
+
+// Make links block level
+.nav > li > a {
+  display: block;
+}
+.nav > li > a:hover {
+  text-decoration: none;
+  background-color: @grayLighter;
+}
+
+// Redeclare pull classes because of specifity
+.nav > .pull-right {
+  float: right;
+}
+
+// Nav headers (for dropdowns and lists)
+.nav-header {
+  display: block;
+  padding: 3px 15px;
+  font-size: 11px;
+  font-weight: bold;
+  line-height: @baseLineHeight;
+  color: @grayLight;
+  text-shadow: 0 1px 0 rgba(255,255,255,.5);
+  text-transform: uppercase;
+}
+// Space them out when they follow another list item (link)
+.nav li + .nav-header {
+  margin-top: 9px;
+}
+
+
+
+// NAV LIST
+// --------
+
+.nav-list {
+  padding-left: 15px;
+  padding-right: 15px;
+  margin-bottom: 0;
+}
+.nav-list > li > a,
+.nav-list .nav-header {
+  margin-left:  -15px;
+  margin-right: -15px;
+  text-shadow: 0 1px 0 rgba(255,255,255,.5);
+}
+.nav-list > li > a {
+  padding: 3px 15px;
+}
+.nav-list > .active > a,
+.nav-list > .active > a:hover {
+  color: @white;
+  text-shadow: 0 -1px 0 rgba(0,0,0,.2);
+  background-color: @linkColor;
+}
+.nav-list [class^="icon-"] {
+  margin-right: 2px;
+}
+// Dividers (basically an hr) within the dropdown
+.nav-list .divider {
+  .nav-divider();
+}
+
+
+
+// TABS AND PILLS
+// -------------
+
+// Common styles
+.nav-tabs,
+.nav-pills {
+  .clearfix();
+}
+.nav-tabs > li,
+.nav-pills > li {
+  float: left;
+}
+.nav-tabs > li > a,
+.nav-pills > li > a {
+  padding-right: 12px;
+  padding-left: 12px;
+  margin-right: 2px;
+  line-height: 14px; // keeps the overall height an even number
+}
+
+// TABS
+// ----
+
+// Give the tabs something to sit on
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+// Make the list-items overlay the bottom border
+.nav-tabs > li {
+  margin-bottom: -1px;
+}
+// Actual tabs (as links)
+.nav-tabs > li > a {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  line-height: @baseLineHeight;
+  border: 1px solid transparent;
+  .border-radius(4px 4px 0 0);
+  &:hover {
+    border-color: @grayLighter @grayLighter #ddd;
+  }
+}
+// Active state, and it's :hover to override normal :hover
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover {
+  color: @gray;
+  background-color: @white;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+  cursor: default;
+}
+
+
+// PILLS
+// -----
+
+// Links rendered as pills
+.nav-pills > li > a {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  .border-radius(5px);
+}
+
+// Active state
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover {
+  color: @white;
+  background-color: @linkColor;
+}
+
+
+
+// STACKED NAV
+// -----------
+
+// Stacked tabs and pills
+.nav-stacked > li {
+  float: none;
+}
+.nav-stacked > li > a {
+  margin-right: 0; // no need for the gap between nav items
+}
+
+// Tabs
+.nav-tabs.nav-stacked {
+  border-bottom: 0;
+}
+.nav-tabs.nav-stacked > li > a {
+  border: 1px solid #ddd;
+  .border-radius(0);
+}
+.nav-tabs.nav-stacked > li:first-child > a {
+  .border-top-radius(4px);
+}
+.nav-tabs.nav-stacked > li:last-child > a {
+  .border-bottom-radius(4px);
+}
+.nav-tabs.nav-stacked > li > a:hover {
+  border-color: #ddd;
+  z-index: 2;
+}
+
+// Pills
+.nav-pills.nav-stacked > li > a {
+  margin-bottom: 3px;
+}
+.nav-pills.nav-stacked > li:last-child > a {
+  margin-bottom: 1px; // decrease margin to match sizing of stacked tabs
+}
+
+
+
+// DROPDOWNS
+// ---------
+
+.nav-tabs .dropdown-menu {
+  .border-radius(0 0 6px 6px); // remove the top rounded corners here since there is a hard edge above the menu
+}
+.nav-pills .dropdown-menu {
+  .border-radius(6px); // make rounded corners match the pills
+}
+
+// Default dropdown links
+// -------------------------
+// Make carets use linkColor to start
+.nav .dropdown-toggle .caret {
+  border-top-color: @linkColor;
+  border-bottom-color: @linkColor;
+  margin-top: 6px;
+}
+.nav .dropdown-toggle:hover .caret {
+  border-top-color: @linkColorHover;
+  border-bottom-color: @linkColorHover;
+}
+/* move down carets for tabs */
+.nav-tabs .dropdown-toggle .caret {
+  margin-top: 8px;
+}
+
+// Active dropdown links
+// -------------------------
+.nav .active .dropdown-toggle .caret {
+  border-top-color: #fff;
+  border-bottom-color: #fff;
+}
+.nav-tabs .active .dropdown-toggle .caret {
+  border-top-color: @gray;
+  border-bottom-color: @gray;
+}
+
+// Active:hover dropdown links
+// -------------------------
+.nav > .dropdown.active > a:hover {
+  cursor: pointer;
+}
+
+// Open dropdowns
+// -------------------------
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+  color: @white;
+  background-color: @grayLight;
+  border-color: @grayLight;
+}
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret {
+  border-top-color: @white;
+  border-bottom-color: @white;
+  .opacity(100);
+}
+
+// Dropdowns in stacked tabs
+.tabs-stacked .open > a:hover {
+  border-color: @grayLight;
+}
+
+
+
+// TABBABLE
+// --------
+
+
+// COMMON STYLES
+// -------------
+
+// Clear any floats
+.tabbable {
+  .clearfix();
+}
+.tab-content {
+  overflow: auto; // prevent content from running below tabs
+}
+
+// Remove border on bottom, left, right
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+  border-bottom: 0;
+}
+
+// Show/hide tabbable areas
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+  display: none;
+}
+.tab-content > .active,
+.pill-content > .active {
+  display: block;
+}
+
+
+// BOTTOM
+// ------
+
+.tabs-below > .nav-tabs {
+  border-top: 1px solid #ddd;
+}
+.tabs-below > .nav-tabs > li {
+  margin-top: -1px;
+  margin-bottom: 0;
+}
+.tabs-below > .nav-tabs > li > a {
+  .border-radius(0 0 4px 4px);
+  &:hover {
+    border-bottom-color: transparent;
+    border-top-color: #ddd;
+  }
+}
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover {
+  border-color: transparent #ddd #ddd #ddd;
+}
+
+// LEFT & RIGHT
+// ------------
+
+// Common styles
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+  float: none;
+}
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+  min-width: 74px;
+  margin-right: 0;
+  margin-bottom: 3px;
+}
+
+// Tabs on the left
+.tabs-left > .nav-tabs {
+  float: left;
+  margin-right: 19px;
+  border-right: 1px solid #ddd;
+}
+.tabs-left > .nav-tabs > li > a {
+  margin-right: -1px;
+  .border-radius(4px 0 0 4px);
+}
+.tabs-left > .nav-tabs > li > a:hover {
+  border-color: @grayLighter #ddd @grayLighter @grayLighter;
+}
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover {
+  border-color: #ddd transparent #ddd #ddd;
+  *border-right-color: @white;
+}
+
+// Tabs on the right
+.tabs-right > .nav-tabs {
+  float: right;
+  margin-left: 19px;
+  border-left: 1px solid #ddd;
+}
+.tabs-right > .nav-tabs > li > a {
+  margin-left: -1px;
+  .border-radius(0 4px 4px 0);
+}
+.tabs-right > .nav-tabs > li > a:hover {
+  border-color: @grayLighter @grayLighter @grayLighter #ddd;
+}
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover {
+  border-color: #ddd #ddd #ddd transparent;
+  *border-left-color: @white;
+}
+
+
+
+// DISABLED STATES
+// ---------------
+
+// Gray out text
+.nav > .disabled > a {
+  color: @grayLight;
+}
+// Nuke hover effects
+.nav > .disabled > a:hover {
+  text-decoration: none;
+  background-color: transparent;
+  cursor: default;
+}

+ 40 - 0
static/admin/css/bootstrap/pager.less

@@ -0,0 +1,40 @@
+//
+// Pager pagination
+// --------------------------------------------------
+
+
+.pager {
+  margin: @baseLineHeight 0;
+  list-style: none;
+  text-align: center;
+  .clearfix();
+}
+.pager li {
+  display: inline;
+}
+.pager a,
+.pager span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  .border-radius(15px);
+}
+.pager a:hover {
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.pager .next a,
+.pager .next span {
+  float: right;
+}
+.pager .previous a {
+  float: left;
+}
+.pager .disabled a,
+.pager .disabled a:hover,
+.pager .disabled span {
+  color: @grayLight;
+  background-color: #fff;
+  cursor: default;
+}

+ 64 - 0
static/admin/css/bootstrap/pagination.less

@@ -0,0 +1,64 @@
+//
+// Pagination (multiple pages)
+// --------------------------------------------------
+
+
+.pagination {
+  height: @baseLineHeight * 2;
+  margin: @baseLineHeight 0;
+ }
+.pagination ul {
+  display: inline-block;
+  .ie7-inline-block();
+  margin-left: 0;
+  margin-bottom: 0;
+  .border-radius(3px);
+  .box-shadow(0 1px 2px rgba(0,0,0,.05));
+}
+.pagination ul > li {
+  display: inline;
+}
+.pagination ul > li > a,
+.pagination ul > li > span {
+  float: left;
+  padding: 0 14px;
+  line-height: (@baseLineHeight * 2) - 2;
+  text-decoration: none;
+  background-color: @paginationBackground;
+  border: 1px solid @paginationBorder;
+  border-left-width: 0;
+}
+.pagination ul > li > a:hover,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  background-color: #f5f5f5;
+}
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  color: @grayLight;
+  cursor: default;
+}
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover {
+  color: @grayLight;
+  background-color: transparent;
+  cursor: default;
+}
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
+  border-left-width: 1px;
+  .border-radius(3px 0 0 3px);
+}
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+  .border-radius(0 3px 3px 0);
+}
+
+// Centered
+.pagination-centered {
+  text-align: center;
+}
+.pagination-right {
+  text-align: right;
+}

+ 117 - 0
static/admin/css/bootstrap/popovers.less

@@ -0,0 +1,117 @@
+//
+// Popovers
+// --------------------------------------------------
+
+
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: @zindexPopover;
+  display: none;
+  width: 236px;
+  padding: 1px;
+  background-color: @popoverBackground;
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0,0,0,.2);
+  .border-radius(6px);
+  .box-shadow(0 5px 10px rgba(0,0,0,.2));
+
+  // Offset the popover to account for the popover arrow
+  &.top     { margin-bottom: 10px; }
+  &.right   { margin-left: 10px; }
+  &.bottom  { margin-top: 10px; }
+  &.left    { margin-right: 10px; }
+
+}
+
+.popover-title {
+  margin: 0; // reset heading margin
+  padding: 8px 14px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 18px;
+  background-color: @popoverTitleBackground;
+  border-bottom: 1px solid darken(@popoverTitleBackground, 5%);
+  .border-radius(5px 5px 0 0);
+}
+
+.popover-content {
+  padding: 9px 14px;
+  p, ul, ol {
+    margin-bottom: 0;
+  }
+}
+
+// Arrows
+.popover .arrow,
+.popover .arrow:after {
+  position: absolute;
+  display: inline-block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.popover .arrow:after {
+  content: "";
+  z-index: -1;
+}
+
+.popover {
+  &.top .arrow {
+    bottom: -@popoverArrowWidth;
+    left: 50%;
+    margin-left: -@popoverArrowWidth;
+    border-width: @popoverArrowWidth @popoverArrowWidth 0;
+    border-top-color: @popoverArrowColor;
+    &:after {
+      border-width: @popoverArrowOuterWidth @popoverArrowOuterWidth 0;
+      border-top-color: @popoverArrowOuterColor;
+      bottom: -1px;
+      left: -@popoverArrowOuterWidth;
+    }
+  }
+  &.right .arrow {
+    top: 50%;
+    left: -@popoverArrowWidth;
+    margin-top: -@popoverArrowWidth;
+    border-width: @popoverArrowWidth @popoverArrowWidth @popoverArrowWidth 0;
+    border-right-color: @popoverArrowColor;
+    &:after {
+      border-width: @popoverArrowOuterWidth @popoverArrowOuterWidth @popoverArrowOuterWidth 0;
+      border-right-color: @popoverArrowOuterColor;
+      bottom: -@popoverArrowOuterWidth;
+      left: -1px;
+    }
+  }
+  &.bottom .arrow {
+    top: -@popoverArrowWidth;
+    left: 50%;
+    margin-left: -@popoverArrowWidth;
+    border-width: 0 @popoverArrowWidth @popoverArrowWidth;
+    border-bottom-color: @popoverArrowColor;
+    &:after {
+      border-width: 0 @popoverArrowOuterWidth @popoverArrowOuterWidth;
+      border-bottom-color: @popoverArrowOuterColor;
+      top: -1px;
+      left: -@popoverArrowOuterWidth;
+    }
+  }
+  &.left .arrow {
+    top: 50%;
+    right: -@popoverArrowWidth;
+    margin-top: -@popoverArrowWidth;
+    border-width: @popoverArrowWidth 0 @popoverArrowWidth @popoverArrowWidth;
+    border-left-color: @popoverArrowColor;
+    &:after {
+      border-width: @popoverArrowOuterWidth 0 @popoverArrowOuterWidth @popoverArrowOuterWidth;
+      border-left-color: @popoverArrowOuterColor;
+      bottom: -@popoverArrowOuterWidth;
+      right: -1px;
+    }
+  }
+}

+ 122 - 0
static/admin/css/bootstrap/progress-bars.less

@@ -0,0 +1,122 @@
+//
+// Progress bars
+// --------------------------------------------------
+
+
+// ANIMATIONS
+// ----------
+
+// Webkit
+@-webkit-keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+// Firefox
+@-moz-keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+// IE9
+@-ms-keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+// Opera
+@-o-keyframes progress-bar-stripes {
+  from  { background-position: 0 0; }
+  to    { background-position: 40px 0; }
+}
+
+// Spec
+@keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+
+
+// THE BARS
+// --------
+
+// Outer container
+.progress {
+  overflow: hidden;
+  height: @baseLineHeight;
+  margin-bottom: @baseLineHeight;
+  #gradient > .vertical(#f5f5f5, #f9f9f9);
+  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
+  .border-radius(4px);
+}
+
+// Bar of progress
+.progress .bar {
+  width: 0%;
+  height: 100%;
+  color: @white;
+  float: left;
+  font-size: 12px;
+  text-align: center;
+  text-shadow: 0 -1px 0 rgba(0,0,0,.25);
+  #gradient > .vertical(#149bdf, #0480be);
+  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
+  .box-sizing(border-box);
+  .transition(width .6s ease);
+}
+.progress .bar + .bar {
+  .box-shadow(inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15));
+}
+
+// Striped bars
+.progress-striped .bar {
+  #gradient > .striped(#149bdf);
+  .background-size(40px 40px);
+}
+
+// Call animation for the active one
+.progress.active .bar {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+     -moz-animation: progress-bar-stripes 2s linear infinite;
+      -ms-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+
+
+
+// COLORS
+// ------
+
+// Danger (red)
+.progress-danger .bar, .progress .bar-danger {
+  #gradient > .vertical(#ee5f5b, #c43c35);
+}
+.progress-danger.progress-striped .bar, .progress-striped .bar-danger {
+  #gradient > .striped(#ee5f5b);
+}
+
+// Success (green)
+.progress-success .bar, .progress .bar-success {
+  #gradient > .vertical(#62c462, #57a957);
+}
+.progress-success.progress-striped .bar, .progress-striped .bar-success {
+  #gradient > .striped(#62c462);
+}
+
+// Info (teal)
+.progress-info .bar, .progress .bar-info {
+  #gradient > .vertical(#5bc0de, #339bb9);
+}
+.progress-info.progress-striped .bar, .progress-striped .bar-info {
+  #gradient > .striped(#5bc0de);
+}
+
+// Warning (orange)
+.progress-warning .bar, .progress .bar-warning {
+  #gradient > .vertical(lighten(@orange, 15%), @orange);
+}
+.progress-warning.progress-striped .bar, .progress-striped .bar-warning {
+  #gradient > .striped(lighten(@orange, 15%));
+}

+ 137 - 0
static/admin/css/bootstrap/reset.less

@@ -0,0 +1,137 @@
+//
+// Modals
+// Adapted from http://github.com/necolas/normalize.css
+// --------------------------------------------------
+
+
+// Display in IE6-9 and FF3
+// -------------------------
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+  display: block;
+}
+
+// Display block in IE6-9 and FF3
+// -------------------------
+
+audio,
+canvas,
+video {
+  display: inline-block;
+  *display: inline;
+  *zoom: 1;
+}
+
+// Prevents modern browsers from displaying 'audio' without controls
+// -------------------------
+
+audio:not([controls]) {
+    display: none;
+}
+
+// Base settings
+// -------------------------
+
+html {
+  font-size: 100%;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+// Focus states
+a:focus {
+  .tab-focus();
+}
+// Hover & Active
+a:hover,
+a:active {
+  outline: 0;
+}
+
+// Prevents sub and sup affecting line-height in all browsers
+// -------------------------
+
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+sup {
+  top: -0.5em;
+}
+sub {
+  bottom: -0.25em;
+}
+
+// Img border in a's and image quality
+// -------------------------
+
+img {
+  /* Responsive images (ensure images don't scale beyond their parents) */
+  max-width: 100%; /* Part 1: Set a maxium relative to the parent */
+  width: auto\9; /* IE7-8 need help adjusting responsive images */
+  height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */
+
+  vertical-align: middle;
+  border: 0;
+  -ms-interpolation-mode: bicubic;
+}
+
+// Prevent max-width from affecting Google Maps
+#map_canvas img {
+  max-width: none;
+}
+
+// Forms
+// -------------------------
+
+// Font size in all browsers, margin changes, misc consistency
+button,
+input,
+select,
+textarea {
+  margin: 0;
+  font-size: 100%;
+  vertical-align: middle;
+}
+button,
+input {
+  *overflow: visible; // Inner spacing ie IE6/7
+  line-height: normal; // FF3/4 have !important on line-height in UA stylesheet
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
+  padding: 0;
+  border: 0;
+}
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  cursor: pointer; // Cursors on all buttons applied consistently
+  -webkit-appearance: button; // Style clickable inputs in iOS
+}
+input[type="search"] { // Appearance in Safari/Chrome
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+  -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
+}
+textarea {
+  overflow: auto; // Remove vertical scrollbar in IE6-9
+  vertical-align: top; // Readability and alignment cross-browser
+}

+ 28 - 0
static/admin/css/bootstrap/responsive-1200px-min.less

@@ -0,0 +1,28 @@
+//
+// Responsive: Large desktop and up
+// --------------------------------------------------
+
+
+@media (min-width: 1200px) {
+
+  // Fixed grid
+  #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200);
+
+  // Fluid grid
+  #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200);
+
+  // Input grid
+  #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200);
+
+  // Thumbnails
+  .thumbnails {
+    margin-left: -@gridGutterWidth1200;
+  }
+  .thumbnails > li {
+    margin-left: @gridGutterWidth1200;
+  }
+  .row-fluid .thumbnails {
+    margin-left: 0;
+  }
+
+}

+ 174 - 0
static/admin/css/bootstrap/responsive-767px-max.less

@@ -0,0 +1,174 @@
+//
+// Responsive: Landscape phone to desktop/tablet
+// --------------------------------------------------
+
+
+@media (max-width: 767px) {
+
+  // Padding to set content in a bit
+  body {
+    padding-left: 20px;
+    padding-right: 20px;
+  }
+  // Negative indent the now static "fixed" navbar
+  .navbar-fixed-top,
+  .navbar-fixed-bottom,
+  .navbar-static-top {
+    margin-left: -20px;
+    margin-right: -20px;
+  }
+  // Remove padding on container given explicit padding set on body
+  .container-fluid {
+    padding: 0;
+  }
+
+  // TYPOGRAPHY
+  // ----------
+  // Reset horizontal dl
+  .dl-horizontal {
+    dt {
+      float: none;
+      clear: none;
+      width: auto;
+      text-align: left;
+    }
+    dd {
+      margin-left: 0;
+    }
+  }
+
+  // GRID & CONTAINERS
+  // -----------------
+  // Remove width from containers
+  .container {
+    width: auto;
+  }
+  // Fluid rows
+  .row-fluid {
+    width: 100%;
+  }
+  // Undo negative margin on rows and thumbnails
+  .row,
+  .thumbnails {
+    margin-left: 0;
+  }
+  .thumbnails > li {
+    float: none;
+    margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present
+  }
+  // Make all grid-sized elements block level again
+  [class*="span"],
+  .row-fluid [class*="span"] {
+    float: none;
+    display: block;
+    width: 100%;
+    margin-left: 0;
+    .box-sizing(border-box);
+  }
+  .span12,
+  .row-fluid .span12 {
+    width: 100%;
+    .box-sizing(border-box);
+  }
+
+  // FORM FIELDS
+  // -----------
+  // Make span* classes full width
+  .input-large,
+  .input-xlarge,
+  .input-xxlarge,
+  input[class*="span"],
+  select[class*="span"],
+  textarea[class*="span"],
+  .uneditable-input {
+    .input-block-level();
+  }
+  // But don't let it screw up prepend/append inputs
+  .input-prepend input,
+  .input-append input,
+  .input-prepend input[class*="span"],
+  .input-append input[class*="span"] {
+    display: inline-block; // redeclare so they don't wrap to new lines
+    width: auto;
+  }
+  .controls-row [class*="span"] + [class*="span"] {
+    margin-left: 0;
+  }
+
+  // Modals
+  .modal {
+    position: fixed;
+    top:   20px;
+    left:  20px;
+    right: 20px;
+    width: auto;
+    margin: 0;
+    &.fade.in { top: auto; }
+  }
+
+}
+
+
+
+// UP TO LANDSCAPE PHONE
+// ---------------------
+
+@media (max-width: 480px) {
+
+  // Smooth out the collapsing/expanding nav
+  .nav-collapse {
+    -webkit-transform: translate3d(0, 0, 0); // activate the GPU
+  }
+
+  // Block level the page header small tag for readability
+  .page-header h1 small {
+    display: block;
+    line-height: @baseLineHeight;
+  }
+
+  // Update checkboxes for iOS
+  input[type="checkbox"],
+  input[type="radio"] {
+    border: 1px solid #ccc;
+  }
+
+  // Remove the horizontal form styles
+  .form-horizontal {
+    .control-label {
+      float: none;
+      width: auto;
+      padding-top: 0;
+      text-align: left;
+    }
+    // Move over all input controls and content
+    .controls {
+      margin-left: 0;
+    }
+    // Move the options list down to align with labels
+    .control-list {
+      padding-top: 0; // has to be padding because margin collaspes
+    }
+    // Move over buttons in .form-actions to align with .controls
+    .form-actions {
+      padding-left: 10px;
+      padding-right: 10px;
+    }
+  }
+
+  // Modals
+  .modal {
+    top:   10px;
+    left:  10px;
+    right: 10px;
+  }
+  .modal-header .close {
+    padding: 10px;
+    margin: -10px;
+  }
+
+  // Carousel
+  .carousel-caption {
+    position: static;
+  }
+
+}

+ 19 - 0
static/admin/css/bootstrap/responsive-768px-979px.less

@@ -0,0 +1,19 @@
+//
+// Responsive: Tablet to desktop
+// --------------------------------------------------
+
+
+@media (min-width: 768px) and (max-width: 979px) {
+
+  // Fixed grid
+  #grid > .core(@gridColumnWidth768, @gridGutterWidth768);
+
+  // Fluid grid
+  #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768);
+
+  // Input grid
+  #grid > .input(@gridColumnWidth768, @gridGutterWidth768);
+
+  // No need to reset .thumbnails here since it's the same @gridGutterWidth
+
+}

+ 177 - 0
static/admin/css/bootstrap/responsive-navbar.less

@@ -0,0 +1,177 @@
+//
+// Responsive: Navbar
+// --------------------------------------------------
+
+
+// TABLETS AND BELOW
+// -----------------
+@media (max-width: @navbarCollapseWidth) {
+
+  // UNFIX THE TOPBAR
+  // ----------------
+  // Remove any padding from the body
+  body {
+    padding-top: 0;
+  }
+  // Unfix the navbars
+  .navbar-fixed-top,
+  .navbar-fixed-bottom {
+    position: static;
+  }
+  .navbar-fixed-top {
+    margin-bottom: @baseLineHeight;
+  }
+  .navbar-fixed-bottom {
+    margin-top: @baseLineHeight;
+  }
+  .navbar-fixed-top .navbar-inner,
+  .navbar-fixed-bottom .navbar-inner {
+    padding: 5px;
+  }
+  .navbar .container {
+    width: auto;
+    padding: 0;
+  }
+  // Account for brand name
+  .navbar .brand {
+    padding-left: 10px;
+    padding-right: 10px;
+    margin: 0 0 0 -5px;
+  }
+
+  // COLLAPSIBLE NAVBAR
+  // ------------------
+  // Nav collapse clears brand
+  .nav-collapse {
+    clear: both;
+  }
+  // Block-level the nav
+  .nav-collapse .nav {
+    float: none;
+    margin: 0 0 (@baseLineHeight / 2);
+  }
+  .nav-collapse .nav > li {
+    float: none;
+  }
+  .nav-collapse .nav > li > a {
+    margin-bottom: 2px;
+  }
+  .nav-collapse .nav > .divider-vertical {
+    display: none;
+  }
+  .nav-collapse .nav .nav-header {
+    color: @navbarText;
+    text-shadow: none;
+  }
+  // Nav and dropdown links in navbar
+  .nav-collapse .nav > li > a,
+  .nav-collapse .dropdown-menu a {
+    padding: 9px 15px;
+    font-weight: bold;
+    color: @navbarLinkColor;
+    .border-radius(3px);
+  }
+  // Buttons
+  .nav-collapse .btn {
+    padding: 4px 10px 4px;
+    font-weight: normal;
+    .border-radius(4px);
+  }
+  .nav-collapse .dropdown-menu li + li a {
+    margin-bottom: 2px;
+  }
+  .nav-collapse .nav > li > a:hover,
+  .nav-collapse .dropdown-menu a:hover {
+    background-color: @navbarBackground;
+  }
+  .navbar-inverse .nav-collapse .nav > li > a:hover,
+  .navbar-inverse .nav-collapse .dropdown-menu a:hover {
+    background-color: @navbarInverseBackground;
+  }
+  // Buttons in the navbar
+  .nav-collapse.in .btn-group {
+    margin-top: 5px;
+    padding: 0;
+  }
+  // Dropdowns in the navbar
+  .nav-collapse .dropdown-menu {
+    position: static;
+    top: auto;
+    left: auto;
+    float: none;
+    display: block;
+    max-width: none;
+    margin: 0 15px;
+    padding: 0;
+    background-color: transparent;
+    border: none;
+    .border-radius(0);
+    .box-shadow(none);
+  }
+  .nav-collapse .dropdown-menu:before,
+  .nav-collapse .dropdown-menu:after {
+    display: none;
+  }
+  .nav-collapse .dropdown-menu .divider {
+    display: none;
+  }
+  .nav-collapse .nav > li > .dropdown-menu {
+    &:before,
+    &:after {
+      display: none;
+    }
+  }
+  // Forms in navbar
+  .nav-collapse .navbar-form,
+  .nav-collapse .navbar-search {
+    float: none;
+    padding: (@baseLineHeight / 2) 15px;
+    margin: (@baseLineHeight / 2) 0;
+    border-top: 1px solid @navbarBackground;
+    border-bottom: 1px solid @navbarBackground;
+    .box-shadow(inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1));
+  }
+  .navbar-inverse .nav-collapse .navbar-form,
+  .navbar-inverse .nav-collapse .navbar-search {
+    border-top-color: @navbarInverseBackground;
+    border-bottom-color: @navbarInverseBackground;
+  }
+  // Pull right (secondary) nav content
+  .navbar .nav-collapse .nav.pull-right {
+    float: none;
+    margin-left: 0;
+  }
+  // Hide everything in the navbar save .brand and toggle button */
+  .nav-collapse,
+  .nav-collapse.collapse {
+    overflow: hidden;
+    height: 0;
+  }
+  // Navbar button
+  .navbar .btn-navbar {
+    display: block;
+  }
+
+  // STATIC NAVBAR
+  // -------------
+  .navbar-static .navbar-inner {
+    padding-left:  10px;
+    padding-right: 10px;
+  }
+
+
+}
+
+
+// DEFAULT DESKTOP
+// ---------------
+
+@media (min-width: 980px) {
+
+  // Required to make the collapsing navbar work on regular desktops
+  .nav-collapse.collapse {
+    height: auto !important;
+    overflow: visible !important;
+  }
+
+}

+ 43 - 0
static/admin/css/bootstrap/responsive-utilities.less

@@ -0,0 +1,43 @@
+//
+// Responsive: Utility classes
+// --------------------------------------------------
+
+
+// Hide from screenreaders and browsers
+// Credit: HTML5 Boilerplate
+.hidden {
+  display: none;
+  visibility: hidden;
+}
+
+// Visibility utilities
+
+// For desktops
+.visible-phone     { display: none !important; }
+.visible-tablet    { display: none !important; }
+.hidden-phone      { }
+.hidden-tablet     { }
+.hidden-desktop    { display: none !important; }
+.visible-desktop   { display: inherit !important; }
+
+// Tablets & small desktops only
+@media (min-width: 768px) and (max-width: 979px) {
+  // Hide everything else
+  .hidden-desktop    { display: inherit !important; }
+  .visible-desktop   { display: none !important ; }
+  // Show
+  .visible-tablet    { display: inherit !important; }
+  // Hide
+  .hidden-tablet     { display: none !important; }
+}
+
+// Phones only
+@media (max-width: 767px) {
+  // Hide everything else
+  .hidden-desktop    { display: inherit !important; }
+  .visible-desktop   { display: none !important; }
+  // Show
+  .visible-phone     { display: inherit !important; } // Use inherit to restore previous behavior
+  // Hide
+  .hidden-phone      { display: none !important; }
+}

+ 48 - 0
static/admin/css/bootstrap/responsive.less

@@ -0,0 +1,48 @@
+/*!
+ * Bootstrap Responsive v2.1.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+
+// Responsive.less
+// For phone and tablet devices
+// -------------------------------------------------------------
+
+
+// REPEAT VARIABLES & MIXINS
+// -------------------------
+// Required since we compile the responsive stuff separately
+
+@import "variables.less"; // Modify this for custom colors, font-sizes, etc
+@import "mixins.less";
+
+
+// RESPONSIVE CLASSES
+// ------------------
+
+@import "responsive-utilities.less";
+
+
+// MEDIA QUERIES
+// ------------------
+
+// Large desktops
+@import "responsive-1200px-min.less";
+
+// Tablets to regular desktops
+@import "responsive-768px-979px.less";
+
+// Phones to portrait tablets and narrow desktops
+@import "responsive-767px-max.less";
+
+
+// RESPONSIVE NAVBAR
+// ------------------
+
+// From 979px and below, show a button to toggle navbar contents
+@import "responsive-navbar.less";

+ 52 - 0
static/admin/css/bootstrap/scaffolding.less

@@ -0,0 +1,52 @@
+//
+// Scaffolding
+// --------------------------------------------------
+
+
+// Body reset
+// -------------------------
+
+body {
+  margin: 0;
+  font-family: @baseFontFamily;
+  font-size: @baseFontSize;
+  line-height: @baseLineHeight;
+  color: @textColor;
+  background-color: @bodyBackground;
+}
+
+
+// Links
+// -------------------------
+
+a {
+  color: @linkColor;
+  text-decoration: none;
+}
+a:hover {
+  color: @linkColorHover;
+  text-decoration: underline;
+}
+
+
+// Images
+// -------------------------
+
+// Rounded corners
+.img-rounded {
+  .border-radius(6px);
+}
+
+// Add polaroid-esque trim
+.img-polaroid {
+  padding: 4px;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0,0,0,.2);
+  .box-shadow(0 1px 3px rgba(0,0,0,.1));
+}
+
+// Perfect circle
+.img-circle {
+  .border-radius(500px); // crank the border-radius so it works with most reasonably sized images
+}

+ 193 - 0
static/admin/css/bootstrap/sprites.less

@@ -0,0 +1,193 @@
+//
+// Sprites
+// --------------------------------------------------
+
+
+// ICONS
+// -----
+
+// All icons receive the styles of the <i> tag with a base class
+// of .i and are then given a unique class to add width, height,
+// and background-position. Your resulting HTML will look like
+// <i class="icon-inbox"></i>.
+
+// For the white version of the icons, just add the .icon-white class:
+// <i class="icon-inbox icon-white"></i>
+
+[class^="icon-"],
+[class*=" icon-"] {
+  display: inline-block;
+  width: 14px;
+  height: 14px;
+  .ie7-restore-right-whitespace();
+  line-height: 14px;
+  vertical-align: text-top;
+  background-image: url("@{iconSpritePath}");
+  background-position: 14px 14px;
+  background-repeat: no-repeat;
+  margin-top: 1px;
+}
+
+/* White icons with optional class, or on hover/active states of certain elements */
+.icon-white,
+.nav-tabs > .active > a > [class^="icon-"],
+.nav-tabs > .active > a > [class*=" icon-"],
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"] {
+  background-image: url("@{iconWhiteSpritePath}");
+}
+
+.icon-glass              { background-position: 0      0; }
+.icon-music              { background-position: -24px  0; }
+.icon-search             { background-position: -48px  0; }
+.icon-envelope           { background-position: -72px  0; }
+.icon-heart              { background-position: -96px  0; }
+.icon-star               { background-position: -120px 0; }
+.icon-star-empty         { background-position: -144px 0; }
+.icon-user               { background-position: -168px 0; }
+.icon-film               { background-position: -192px 0; }
+.icon-th-large           { background-position: -216px 0; }
+.icon-th                 { background-position: -240px 0; }
+.icon-th-list            { background-position: -264px 0; }
+.icon-ok                 { background-position: -288px 0; }
+.icon-remove             { background-position: -312px 0; }
+.icon-zoom-in            { background-position: -336px 0; }
+.icon-zoom-out           { background-position: -360px 0; }
+.icon-off                { background-position: -384px 0; }
+.icon-signal             { background-position: -408px 0; }
+.icon-cog                { background-position: -432px 0; }
+.icon-trash              { background-position: -456px 0; }
+
+.icon-home               { background-position: 0      -24px; }
+.icon-file               { background-position: -24px  -24px; }
+.icon-time               { background-position: -48px  -24px; }
+.icon-road               { background-position: -72px  -24px; }
+.icon-download-alt       { background-position: -96px  -24px; }
+.icon-download           { background-position: -120px -24px; }
+.icon-upload             { background-position: -144px -24px; }
+.icon-inbox              { background-position: -168px -24px; }
+.icon-play-circle        { background-position: -192px -24px; }
+.icon-repeat             { background-position: -216px -24px; }
+.icon-refresh            { background-position: -240px -24px; }
+.icon-list-alt           { background-position: -264px -24px; }
+.icon-lock               { background-position: -287px -24px; } // 1px off
+.icon-flag               { background-position: -312px -24px; }
+.icon-headphones         { background-position: -336px -24px; }
+.icon-volume-off         { background-position: -360px -24px; }
+.icon-volume-down        { background-position: -384px -24px; }
+.icon-volume-up          { background-position: -408px -24px; }
+.icon-qrcode             { background-position: -432px -24px; }
+.icon-barcode            { background-position: -456px -24px; }
+
+.icon-tag                { background-position: 0      -48px; }
+.icon-tags               { background-position: -25px  -48px; } // 1px off
+.icon-book               { background-position: -48px  -48px; }
+.icon-bookmark           { background-position: -72px  -48px; }
+.icon-print              { background-position: -96px  -48px; }
+.icon-camera             { background-position: -120px -48px; }
+.icon-font               { background-position: -144px -48px; }
+.icon-bold               { background-position: -167px -48px; } // 1px off
+.icon-italic             { background-position: -192px -48px; }
+.icon-text-height        { background-position: -216px -48px; }
+.icon-text-width         { background-position: -240px -48px; }
+.icon-align-left         { background-position: -264px -48px; }
+.icon-align-center       { background-position: -288px -48px; }
+.icon-align-right        { background-position: -312px -48px; }
+.icon-align-justify      { background-position: -336px -48px; }
+.icon-list               { background-position: -360px -48px; }
+.icon-indent-left        { background-position: -384px -48px; }
+.icon-indent-right       { background-position: -408px -48px; }
+.icon-facetime-video     { background-position: -432px -48px; }
+.icon-picture            { background-position: -456px -48px; }
+
+.icon-pencil             { background-position: 0      -72px; }
+.icon-map-marker         { background-position: -24px  -72px; }
+.icon-adjust             { background-position: -48px  -72px; }
+.icon-tint               { background-position: -72px  -72px; }
+.icon-edit               { background-position: -96px  -72px; }
+.icon-share              { background-position: -120px -72px; }
+.icon-check              { background-position: -144px -72px; }
+.icon-move               { background-position: -168px -72px; }
+.icon-step-backward      { background-position: -192px -72px; }
+.icon-fast-backward      { background-position: -216px -72px; }
+.icon-backward           { background-position: -240px -72px; }
+.icon-play               { background-position: -264px -72px; }
+.icon-pause              { background-position: -288px -72px; }
+.icon-stop               { background-position: -312px -72px; }
+.icon-forward            { background-position: -336px -72px; }
+.icon-fast-forward       { background-position: -360px -72px; }
+.icon-step-forward       { background-position: -384px -72px; }
+.icon-eject              { background-position: -408px -72px; }
+.icon-chevron-left       { background-position: -432px -72px; }
+.icon-chevron-right      { background-position: -456px -72px; }
+
+.icon-plus-sign          { background-position: 0      -96px; }
+.icon-minus-sign         { background-position: -24px  -96px; }
+.icon-remove-sign        { background-position: -48px  -96px; }
+.icon-ok-sign            { background-position: -72px  -96px; }
+.icon-question-sign      { background-position: -96px  -96px; }
+.icon-info-sign          { background-position: -120px -96px; }
+.icon-screenshot         { background-position: -144px -96px; }
+.icon-remove-circle      { background-position: -168px -96px; }
+.icon-ok-circle          { background-position: -192px -96px; }
+.icon-ban-circle         { background-position: -216px -96px; }
+.icon-arrow-left         { background-position: -240px -96px; }
+.icon-arrow-right        { background-position: -264px -96px; }
+.icon-arrow-up           { background-position: -289px -96px; } // 1px off
+.icon-arrow-down         { background-position: -312px -96px; }
+.icon-share-alt          { background-position: -336px -96px; }
+.icon-resize-full        { background-position: -360px -96px; }
+.icon-resize-small       { background-position: -384px -96px; }
+.icon-plus               { background-position: -408px -96px; }
+.icon-minus              { background-position: -433px -96px; }
+.icon-asterisk           { background-position: -456px -96px; }
+
+.icon-exclamation-sign   { background-position: 0      -120px; }
+.icon-gift               { background-position: -24px  -120px; }
+.icon-leaf               { background-position: -48px  -120px; }
+.icon-fire               { background-position: -72px  -120px; }
+.icon-eye-open           { background-position: -96px  -120px; }
+.icon-eye-close          { background-position: -120px -120px; }
+.icon-warning-sign       { background-position: -144px -120px; }
+.icon-plane              { background-position: -168px -120px; }
+.icon-calendar           { background-position: -192px -120px; }
+.icon-random             { background-position: -216px -120px; width: 16px; }
+.icon-comment            { background-position: -240px -120px; }
+.icon-magnet             { background-position: -264px -120px; }
+.icon-chevron-up         { background-position: -288px -120px; }
+.icon-chevron-down       { background-position: -313px -119px; } // 1px, 1px off
+.icon-retweet            { background-position: -336px -120px; }
+.icon-shopping-cart      { background-position: -360px -120px; }
+.icon-folder-close       { background-position: -384px -120px; }
+.icon-folder-open        { background-position: -408px -120px; width: 16px; }
+.icon-resize-vertical    { background-position: -432px -119px; } // 1px, 1px off
+.icon-resize-horizontal  { background-position: -456px -118px; } // 1px, 2px off
+
+.icon-hdd                     { background-position: 0      -144px; }
+.icon-bullhorn                { background-position: -24px  -144px; }
+.icon-bell                    { background-position: -48px  -144px; }
+.icon-certificate             { background-position: -72px  -144px; }
+.icon-thumbs-up               { background-position: -96px  -144px; }
+.icon-thumbs-down             { background-position: -120px -144px; }
+.icon-hand-right              { background-position: -144px -144px; }
+.icon-hand-left               { background-position: -168px -144px; }
+.icon-hand-up                 { background-position: -192px -144px; }
+.icon-hand-down               { background-position: -216px -144px; }
+.icon-circle-arrow-right      { background-position: -240px -144px; }
+.icon-circle-arrow-left       { background-position: -264px -144px; }
+.icon-circle-arrow-up         { background-position: -288px -144px; }
+.icon-circle-arrow-down       { background-position: -312px -144px; }
+.icon-globe                   { background-position: -336px -144px; }
+.icon-wrench                  { background-position: -360px -144px; }
+.icon-tasks                   { background-position: -384px -144px; }
+.icon-filter                  { background-position: -408px -144px; }
+.icon-briefcase               { background-position: -432px -144px; }
+.icon-fullscreen              { background-position: -456px -144px; }

+ 245 - 0
static/admin/css/bootstrap/tables.less

@@ -0,0 +1,245 @@
+//
+// Tables
+// --------------------------------------------------
+
+
+// BASE TABLES
+// -----------------
+
+table {
+  max-width: 100%;
+  background-color: @tableBackground;
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+// BASELINE STYLES
+// ---------------
+
+.table {
+  width: 100%;
+  margin-bottom: @baseLineHeight;
+  // Cells
+  th,
+  td {
+    padding: 8px;
+    line-height: @baseLineHeight;
+    text-align: left;
+    vertical-align: top;
+    border-top: 1px solid @tableBorder;
+  }
+  th {
+    font-weight: bold;
+  }
+  // Bottom align for column headings
+  thead th {
+    vertical-align: bottom;
+  }
+  // Remove top border from thead by default
+  caption + thead tr:first-child th,
+  caption + thead tr:first-child td,
+  colgroup + thead tr:first-child th,
+  colgroup + thead tr:first-child td,
+  thead:first-child tr:first-child th,
+  thead:first-child tr:first-child td {
+    border-top: 0;
+  }
+  // Account for multiple tbody instances
+  tbody + tbody {
+    border-top: 2px solid @tableBorder;
+  }
+}
+
+
+
+// CONDENSED TABLE W/ HALF PADDING
+// -------------------------------
+
+.table-condensed {
+  th,
+  td {
+    padding: 4px 5px;
+  }
+}
+
+
+// BORDERED VERSION
+// ----------------
+
+.table-bordered {
+  border: 1px solid @tableBorder;
+  border-collapse: separate; // Done so we can round those corners!
+  *border-collapse: collapse; // IE7 can't round corners anyway
+  border-left: 0;
+  .border-radius(4px);
+  th,
+  td {
+    border-left: 1px solid @tableBorder;
+  }
+  // Prevent a double border
+  caption + thead tr:first-child th,
+  caption + tbody tr:first-child th,
+  caption + tbody tr:first-child td,
+  colgroup + thead tr:first-child th,
+  colgroup + tbody tr:first-child th,
+  colgroup + tbody tr:first-child td,
+  thead:first-child tr:first-child th,
+  tbody:first-child tr:first-child th,
+  tbody:first-child tr:first-child td {
+    border-top: 0;
+  }
+  // For first th or td in the first row in the first thead or tbody
+  thead:first-child tr:first-child th:first-child,
+  tbody:first-child tr:first-child td:first-child {
+    -webkit-border-top-left-radius: 4px;
+            border-top-left-radius: 4px;
+        -moz-border-radius-topleft: 4px;
+  }
+  thead:first-child tr:first-child th:last-child,
+  tbody:first-child tr:first-child td:last-child {
+    -webkit-border-top-right-radius: 4px;
+            border-top-right-radius: 4px;
+        -moz-border-radius-topright: 4px;
+  }
+  // For first th or td in the first row in the first thead or tbody
+  thead:last-child tr:last-child th:first-child,
+  tbody:last-child tr:last-child td:first-child,
+  tfoot:last-child tr:last-child td:first-child {
+    .border-radius(0 0 0 4px);
+    -webkit-border-bottom-left-radius: 4px;
+            border-bottom-left-radius: 4px;
+        -moz-border-radius-bottomleft: 4px;
+  }
+  thead:last-child tr:last-child th:last-child,
+  tbody:last-child tr:last-child td:last-child,
+  tfoot:last-child tr:last-child td:last-child {
+    -webkit-border-bottom-right-radius: 4px;
+            border-bottom-right-radius: 4px;
+        -moz-border-radius-bottomright: 4px;
+  }
+
+  // Special fixes to round the left border on the first td/th
+  caption + thead tr:first-child th:first-child,
+  caption + tbody tr:first-child td:first-child,
+  colgroup + thead tr:first-child th:first-child,
+  colgroup + tbody tr:first-child td:first-child {
+    -webkit-border-top-left-radius: 4px;
+            border-top-left-radius: 4px;
+        -moz-border-radius-topleft: 4px;
+  }
+  caption + thead tr:first-child th:last-child,
+  caption + tbody tr:first-child td:last-child,
+  colgroup + thead tr:first-child th:last-child,
+  colgroup + tbody tr:first-child td:last-child {
+    -webkit-border-top-right-radius: 4px;
+            border-top-right-radius: 4px;
+         -moz-border-radius-topleft: 4px;
+  }
+
+}
+
+
+
+
+// ZEBRA-STRIPING
+// --------------
+
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+.table-striped {
+  tbody {
+    tr:nth-child(odd) td,
+    tr:nth-child(odd) th {
+      background-color: @tableBackgroundAccent;
+    }
+  }
+}
+
+
+// HOVER EFFECT
+// ------------
+// Placed here since it has to come after the potential zebra striping
+.table-hover {
+  tbody {
+    tr:hover td,
+    tr:hover th {
+      background-color: @tableBackgroundHover;
+    }
+  }
+}
+
+
+// TABLE CELL SIZING
+// -----------------
+
+// Reset default grid behavior
+table [class*=span],
+.row-fluid table [class*=span] {
+  display: table-cell;
+  float: none; // undo default grid column styles
+  margin-left: 0; // undo default grid column styles
+}
+
+// Change the column widths to account for td/th padding
+.table {
+  .span1     { .tableColumns(1); }
+  .span2     { .tableColumns(2); }
+  .span3     { .tableColumns(3); }
+  .span4     { .tableColumns(4); }
+  .span5     { .tableColumns(5); }
+  .span6     { .tableColumns(6); }
+  .span7     { .tableColumns(7); }
+  .span8     { .tableColumns(8); }
+  .span9     { .tableColumns(9); }
+  .span10    { .tableColumns(10); }
+  .span11    { .tableColumns(11); }
+  .span12    { .tableColumns(12); }
+  .span13    { .tableColumns(13); }
+  .span14    { .tableColumns(14); }
+  .span15    { .tableColumns(15); }
+  .span16    { .tableColumns(16); }
+  .span17    { .tableColumns(17); }
+  .span18    { .tableColumns(18); }
+  .span19    { .tableColumns(19); }
+  .span20    { .tableColumns(20); }
+  .span21    { .tableColumns(21); }
+  .span22    { .tableColumns(22); }
+  .span23    { .tableColumns(23); }
+  .span24    { .tableColumns(24); }
+}
+
+
+
+// TABLE BACKGROUNDS
+// -----------------
+// Exact selectors below required to override .table-striped
+
+.table tbody tr {
+  &.success td {
+    background-color: @successBackground;
+  }
+  &.error td {
+    background-color: @errorBackground;
+  }
+  &.warning td {
+    background-color: @warningBackground;
+  }
+  &.info td {
+    background-color: @infoBackground;
+  }
+}
+
+// Hover states for .table-hover
+.table-hover tbody tr {
+  &.success:hover td {
+    background-color: darken(@successBackground, 5%);
+  }
+  &.error:hover td {
+    background-color: darken(@errorBackground, 5%);
+  }
+  &.warning:hover td {
+    background-color: darken(@warningBackground, 5%);
+  }
+  &.info:hover td {
+    background-color: darken(@infoBackground, 5%);
+  }
+}

+ 52 - 0
static/admin/css/bootstrap/thumbnails.less

@@ -0,0 +1,52 @@
+//
+// Thumbnails
+// --------------------------------------------------
+
+
+// Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files
+
+// Make wrapper ul behave like the grid
+.thumbnails {
+  margin-left: -@gridGutterWidth;
+  list-style: none;
+  .clearfix();
+}
+// Fluid rows have no left margin
+.row-fluid .thumbnails {
+  margin-left: 0;
+}
+
+// Float li to make thumbnails appear in a row
+.thumbnails > li {
+  float: left; // Explicity set the float since we don't require .span* classes
+  margin-bottom: @baseLineHeight;
+  margin-left: @gridGutterWidth;
+}
+
+// The actual thumbnail (can be `a` or `div`)
+.thumbnail {
+  display: block;
+  padding: 4px;
+  line-height: @baseLineHeight;
+  border: 1px solid #ddd;
+  .border-radius(4px);
+  .box-shadow(0 1px 3px rgba(0,0,0,.055));
+  .transition(all .2s ease-in-out);
+}
+// Add a hover state for linked versions only
+a.thumbnail:hover {
+  border-color: @linkColor;
+  .box-shadow(0 1px 4px rgba(0,105,214,.25));
+}
+
+// Images and captions
+.thumbnail > img {
+  display: block;
+  max-width: 100%;
+  margin-left: auto;
+  margin-right: auto;
+}
+.thumbnail .caption {
+  padding: 9px;
+  color: @gray;
+}

+ 70 - 0
static/admin/css/bootstrap/tooltip.less

@@ -0,0 +1,70 @@
+//
+// Tooltips
+// --------------------------------------------------
+
+
+// Base class
+.tooltip {
+  position: absolute;
+  z-index: @zindexTooltip;
+  display: block;
+  visibility: visible;
+  padding: 5px;
+  font-size: 11px;
+  .opacity(0);
+  &.in     { .opacity(80); }
+  &.top    { margin-top:  -3px; }
+  &.right  { margin-left:  3px; }
+  &.bottom { margin-top:   3px; }
+  &.left   { margin-left: -3px; }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+  max-width: 200px;
+  padding: 3px 8px;
+  color: @tooltipColor;
+  text-align: center;
+  text-decoration: none;
+  background-color: @tooltipBackground;
+  .border-radius(4px);
+}
+
+// Arrows
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.tooltip {
+  &.top .tooltip-arrow {
+    bottom: 0;
+    left: 50%;
+    margin-left: -@tooltipArrowWidth;
+    border-width: @tooltipArrowWidth @tooltipArrowWidth 0;
+    border-top-color: @tooltipArrowColor;
+  }
+  &.right .tooltip-arrow {
+    top: 50%;
+    left: 0;
+    margin-top: -@tooltipArrowWidth;
+    border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0;
+    border-right-color: @tooltipArrowColor;
+  }
+  &.left .tooltip-arrow {
+    top: 50%;
+    right: 0;
+    margin-top: -@tooltipArrowWidth;
+    border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth;
+    border-left-color: @tooltipArrowColor;
+  }
+  &.bottom .tooltip-arrow {
+    top: 0;
+    left: 50%;
+    margin-left: -@tooltipArrowWidth;
+    border-width: 0 @tooltipArrowWidth @tooltipArrowWidth;
+    border-bottom-color: @tooltipArrowColor;
+  }
+}

+ 221 - 0
static/admin/css/bootstrap/type.less

@@ -0,0 +1,221 @@
+//
+// Typography
+// --------------------------------------------------
+
+
+// Body text
+// -------------------------
+
+p {
+  margin: 0 0 @baseLineHeight / 2;
+}
+.lead {
+  margin-bottom: @baseLineHeight;
+  font-size: @baseFontSize * 1.5;
+  font-weight: 200;
+  line-height: @baseLineHeight * 1.5;
+}
+
+
+// Emphasis & misc
+// -------------------------
+
+small {
+  font-size: 85%; // Ex: 14px base font * 85% = about 12px
+}
+strong {
+  font-weight: bold;
+}
+em {
+  font-style: italic;
+}
+cite {
+  font-style: normal;
+}
+
+// Utility classes
+.muted {
+  color: @grayLight;
+}
+.text-warning {
+  color: @warningText;
+}
+.text-error {
+  color: @errorText;
+}
+.text-info {
+  color: @infoText;
+}
+.text-success {
+  color: @successText;
+}
+
+
+// Headings
+// -------------------------
+
+h1, h2, h3, h4, h5, h6 {
+  margin: (@baseLineHeight / 2) 0;
+  font-family: @headingsFontFamily;
+  font-weight: @headingsFontWeight;
+  line-height: 1;
+  color: @headingsColor;
+  text-rendering: optimizelegibility; // Fix the character spacing for headings
+  small {
+    font-weight: normal;
+    line-height: 1;
+    color: @grayLight;
+  }
+}
+h1 { font-size: 36px; line-height: 40px; }
+h2 { font-size: 30px; line-height: 40px; }
+h3 { font-size: 24px; line-height: 40px; }
+h4 { font-size: 18px; line-height: 20px; }
+h5 { font-size: 14px; line-height: 20px; }
+h6 { font-size: 12px; line-height: 20px; }
+
+h1 small { font-size: 24px; }
+h2 small { font-size: 18px; }
+h3 small { font-size: 14px; }
+h4 small { font-size: 14px; }
+
+
+// Page header
+// -------------------------
+
+.page-header {
+  padding-bottom: (@baseLineHeight / 2) - 1;
+  margin: @baseLineHeight 0 (@baseLineHeight * 1.5);
+  border-bottom: 1px solid @grayLighter;
+}
+
+
+
+// Lists
+// --------------------------------------------------
+
+// Unordered and Ordered lists
+ul, ol {
+  padding: 0;
+  margin: 0 0 @baseLineHeight / 2 25px;
+}
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+  margin-bottom: 0;
+}
+li {
+  line-height: @baseLineHeight;
+}
+ul.unstyled,
+ol.unstyled {
+  margin-left: 0;
+  list-style: none;
+}
+
+// Description Lists
+dl {
+  margin-bottom: @baseLineHeight;
+}
+dt,
+dd {
+  line-height: @baseLineHeight;
+}
+dt {
+  font-weight: bold;
+}
+dd {
+  margin-left: @baseLineHeight / 2;
+}
+// Horizontal layout (like forms)
+.dl-horizontal {
+  .clearfix(); // Ensure dl clears floats if empty dd elements present
+  dt {
+    float: left;
+    width: @horizontalComponentOffset - 20;
+    clear: left;
+    text-align: right;
+    .text-overflow();
+  }
+  dd {
+    margin-left: @horizontalComponentOffset;
+  }
+}
+
+// MISC
+// ----
+
+// Horizontal rules
+hr {
+  margin: @baseLineHeight 0;
+  border: 0;
+  border-top: 1px solid @hrBorder;
+  border-bottom: 1px solid @white;
+}
+
+// Abbreviations and acronyms
+abbr[title] {
+  cursor: help;
+  border-bottom: 1px dotted @grayLight;
+}
+abbr.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+
+// Blockquotes
+blockquote {
+  padding: 0 0 0 15px;
+  margin: 0 0 @baseLineHeight;
+  border-left: 5px solid @grayLighter;
+  p {
+    margin-bottom: 0;
+    #font > .shorthand(16px,300,@baseLineHeight * 1.25);
+  }
+  small {
+    display: block;
+    line-height: @baseLineHeight;
+    color: @grayLight;
+    &:before {
+      content: '\2014 \00A0';
+    }
+  }
+
+  // Float right with text-align: right
+  &.pull-right {
+    float: right;
+    padding-right: 15px;
+    padding-left: 0;
+    border-right: 5px solid @grayLighter;
+    border-left: 0;
+    p,
+    small {
+      text-align: right;
+    }
+    small {
+      &:before {
+        content: '';
+      }
+      &:after {
+        content: '\00A0 \2014';
+      }
+    }
+  }
+}
+
+// Quotes
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+  content: "";
+}
+
+// Addresses
+address {
+  display: block;
+  margin-bottom: @baseLineHeight;
+  font-style: normal;
+  line-height: @baseLineHeight;
+}

+ 30 - 0
static/admin/css/bootstrap/utilities.less

@@ -0,0 +1,30 @@
+//
+// Utility classes
+// --------------------------------------------------
+
+
+// Quick floats
+.pull-right {
+  float: right;
+}
+.pull-left {
+  float: left;
+}
+
+// Toggling content
+.hide {
+  display: none;
+}
+.show {
+  display: block;
+}
+
+// Visibility
+.invisible {
+  visibility: hidden;
+}
+
+// For Affix plugin
+.affix {
+  position: fixed;
+}

+ 279 - 0
static/admin/css/bootstrap/variables.less

@@ -0,0 +1,279 @@
+//
+// Variables
+// --------------------------------------------------
+
+
+// Global values
+// --------------------------------------------------
+
+
+// Grays
+// -------------------------
+@black:                 #000;
+@grayDarker:            #222;
+@grayDark:              #333;
+@gray:                  #555;
+@grayLight:             #999;
+@grayLighter:           #eee;
+@white:                 #fff;
+
+
+// Accent colors
+// -------------------------
+@blue:                  #049cdb;
+@blueDark:              #0064cd;
+@green:                 #46a546;
+@red:                   #9d261d;
+@yellow:                #ffc40d;
+@orange:                #f89406;
+@pink:                  #c3325f;
+@purple:                #7a43b6;
+
+
+// Scaffolding
+// -------------------------
+@bodyBackground:        @white;
+@textColor:             @grayDark;
+
+
+// Links
+// -------------------------
+@linkColor:             #08c;
+@linkColorHover:        darken(@linkColor, 15%);
+
+
+// Typography
+// -------------------------
+@sansFontFamily:        "Helvetica Neue", Helvetica, Arial, sans-serif;
+@serifFontFamily:       Georgia, "Times New Roman", Times, serif;
+@monoFontFamily:        Monaco, Menlo, Consolas, "Courier New", monospace;
+
+@baseFontSize:          14px;
+@baseFontFamily:        @sansFontFamily;
+@baseLineHeight:        20px;
+@altFontFamily:         @serifFontFamily;
+
+@headingsFontFamily:    inherit; // empty to use BS default, @baseFontFamily
+@headingsFontWeight:    bold;    // instead of browser default, bold
+@headingsColor:         inherit; // empty to use BS default, @textColor
+
+// Tables
+// -------------------------
+@tableBackground:                   transparent; // overall background-color
+@tableBackgroundAccent:             #f9f9f9; // for striping
+@tableBackgroundHover:              #f5f5f5; // for hover
+@tableBorder:                       #ddd; // table and cell border
+
+// Buttons
+// -------------------------
+@btnBackground:                     @white;
+@btnBackgroundHighlight:            darken(@white, 10%);
+@btnBorder:                         #bbb;
+
+@btnPrimaryBackground:              @linkColor;
+@btnPrimaryBackgroundHighlight:     spin(@btnPrimaryBackground, 20%);
+
+@btnInfoBackground:                 #5bc0de;
+@btnInfoBackgroundHighlight:        #2f96b4;
+
+@btnSuccessBackground:              #62c462;
+@btnSuccessBackgroundHighlight:     #51a351;
+
+@btnWarningBackground:              lighten(@orange, 15%);
+@btnWarningBackgroundHighlight:     @orange;
+
+@btnDangerBackground:               #ee5f5b;
+@btnDangerBackgroundHighlight:      #bd362f;
+
+@btnInverseBackground:              #444;
+@btnInverseBackgroundHighlight:     @grayDarker;
+
+
+// Forms
+// -------------------------
+@inputBackground:               @white;
+@inputBorder:                   #ccc;
+@inputBorderRadius:             3px;
+@inputDisabledBackground:       @grayLighter;
+@formActionsBackground:         #f5f5f5;
+
+// Dropdowns
+// -------------------------
+@dropdownBackground:            @white;
+@dropdownBorder:                rgba(0,0,0,.2);
+@dropdownDividerTop:            #e5e5e5;
+@dropdownDividerBottom:         @white;
+
+@dropdownLinkColor:             @grayDark;
+@dropdownLinkColorHover:        @white;
+@dropdownLinkColorActive:       @dropdownLinkColor;
+
+@dropdownLinkBackgroundActive:  @linkColor;
+@dropdownLinkBackgroundHover:   @dropdownLinkBackgroundActive;
+
+
+
+// COMPONENT VARIABLES
+// --------------------------------------------------
+
+// Z-index master list
+// -------------------------
+// Used for a bird's eye view of components dependent on the z-axis
+// Try to avoid customizing these :)
+@zindexDropdown:          1000;
+@zindexPopover:           1010;
+@zindexTooltip:           1030;
+@zindexFixedNavbar:       1030;
+@zindexModalBackdrop:     1040;
+@zindexModal:             1050;
+
+
+// Sprite icons path
+// -------------------------
+@iconSpritePath:          "../img/glyphicons-halflings.png";
+@iconWhiteSpritePath:     "../img/glyphicons-halflings-white.png";
+
+
+// Input placeholder text color
+// -------------------------
+@placeholderText:         @grayLight;
+
+
+// Hr border color
+// -------------------------
+@hrBorder:                @grayLighter;
+
+
+// Horizontal forms & lists
+// -------------------------
+@horizontalComponentOffset:       180px;
+
+
+// Wells
+// -------------------------
+@wellBackground:                  #f5f5f5;
+
+
+// Navbar
+// -------------------------
+@navbarCollapseWidth:             979px;
+
+@navbarHeight:                    40px;
+@navbarBackgroundHighlight:       #ffffff;
+@navbarBackground:                darken(@navbarBackgroundHighlight, 5%);
+@navbarBorder:                    darken(@navbarBackground, 12%);
+
+@navbarText:                      #777;
+@navbarLinkColor:                 #777;
+@navbarLinkColorHover:            @grayDark;
+@navbarLinkColorActive:           @gray;
+@navbarLinkBackgroundHover:       transparent;
+@navbarLinkBackgroundActive:      darken(@navbarBackground, 5%);
+
+@navbarBrandColor:                @navbarLinkColor;
+
+// Inverted navbar
+@navbarInverseBackground:                #111111;
+@navbarInverseBackgroundHighlight:       #222222;
+@navbarInverseBorder:                    #252525;
+
+@navbarInverseText:                      @grayLight;
+@navbarInverseLinkColor:                 @grayLight;
+@navbarInverseLinkColorHover:            @white;
+@navbarInverseLinkColorActive:           @navbarInverseLinkColorHover;
+@navbarInverseLinkBackgroundHover:       transparent;
+@navbarInverseLinkBackgroundActive:      @navbarInverseBackground;
+
+@navbarInverseSearchBackground:          lighten(@navbarInverseBackground, 25%);
+@navbarInverseSearchBackgroundFocus:     @white;
+@navbarInverseSearchBorder:              @navbarInverseBackground;
+@navbarInverseSearchPlaceholderColor:    #ccc;
+
+@navbarInverseBrandColor:                @navbarInverseLinkColor;
+
+
+// Pagination
+// -------------------------
+@paginationBackground:                #fff;
+@paginationBorder:                    #ddd;
+@paginationActiveBackground:          #f5f5f5;
+
+
+// Hero unit
+// -------------------------
+@heroUnitBackground:              @grayLighter;
+@heroUnitHeadingColor:            inherit;
+@heroUnitLeadColor:               inherit;
+
+
+// Form states and alerts
+// -------------------------
+@warningText:             #c09853;
+@warningBackground:       #fcf8e3;
+@warningBorder:           darken(spin(@warningBackground, -10), 3%);
+
+@errorText:               #b94a48;
+@errorBackground:         #f2dede;
+@errorBorder:             darken(spin(@errorBackground, -10), 3%);
+
+@successText:             #468847;
+@successBackground:       #dff0d8;
+@successBorder:           darken(spin(@successBackground, -10), 5%);
+
+@infoText:                #3a87ad;
+@infoBackground:          #d9edf7;
+@infoBorder:              darken(spin(@infoBackground, -10), 7%);
+
+
+// Tooltips and popovers
+// -------------------------
+@tooltipColor:            #fff;
+@tooltipBackground:       #000;
+@tooltipArrowWidth:       5px;
+@tooltipArrowColor:       @tooltipBackground;
+
+@popoverBackground:       #fff;
+@popoverArrowWidth:       10px;
+@popoverArrowColor:       #fff;
+@popoverTitleBackground:  darken(@popoverBackground, 3%);
+
+// Special enhancement for popovers
+@popoverArrowOuterWidth:  @popoverArrowWidth + 1;
+@popoverArrowOuterColor:  rgba(0,0,0,.25);
+
+
+
+// GRID
+// --------------------------------------------------
+
+
+// Default 940px grid
+// -------------------------
+@gridColumns:             12;
+@gridColumnWidth:         60px;
+@gridGutterWidth:         20px;
+@gridRowWidth:            (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
+
+// 1200px min
+@gridColumnWidth1200:     70px;
+@gridGutterWidth1200:     30px;
+@gridRowWidth1200:        (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
+
+// 768px-979px
+@gridColumnWidth768:      42px;
+@gridGutterWidth768:      20px;
+@gridRowWidth768:         (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
+
+
+// Fluid grid
+// -------------------------
+@fluidGridColumnWidth:    percentage(@gridColumnWidth/@gridRowWidth);
+@fluidGridGutterWidth:    percentage(@gridGutterWidth/@gridRowWidth);
+
+// 1200px min
+@fluidGridColumnWidth1200:     percentage(@gridColumnWidth1200/@gridRowWidth1200);
+@fluidGridGutterWidth1200:     percentage(@gridGutterWidth1200/@gridRowWidth1200);
+
+// 768px-979px
+@fluidGridColumnWidth768:      percentage(@gridColumnWidth768/@gridRowWidth768);
+@fluidGridGutterWidth768:      percentage(@gridGutterWidth768/@gridRowWidth768);

+ 29 - 0
static/admin/css/bootstrap/wells.less

@@ -0,0 +1,29 @@
+//
+// Wells
+// --------------------------------------------------
+
+
+// Base class
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: @wellBackground;
+  border: 1px solid darken(@wellBackground, 7%);
+  .border-radius(4px);
+  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
+  blockquote {
+    border-color: #ddd;
+    border-color: rgba(0,0,0,.15);
+  }
+}
+
+// Sizes
+.well-large {
+  padding: 24px;
+  .border-radius(6px);
+}
+.well-small {
+  padding: 9px;
+  .border-radius(3px);
+}

+ 281 - 0
static/admin/css/variables.less

@@ -0,0 +1,281 @@
+//
+// Variables
+// --------------------------------------------------
+
+
+// Global values
+// --------------------------------------------------
+
+
+// Grays
+// -------------------------
+@black:                 #000;
+@grayDarker:            #222;
+@grayDark:              #333;
+@gray:                  #555;
+@grayLight:             #999;
+@grayLighter:           #eee;
+@white:                 #fff;
+
+
+// Accent colors
+// -------------------------
+@blue:                  #049cdb;
+@blueDark:              #0064cd;
+@green:                 #46a546;
+@red:                   #9d261d;
+@yellow:                #ffc40d;
+@orange:                #f89406;
+@pink:                  #c3325f;
+@purple:                #7a43b6;
+
+
+// Scaffolding
+// -------------------------
+@bodyBackground:        darken(@white, 1%);
+@textColor:             @grayDark;
+
+
+// Links
+// -------------------------
+@linkColor:             #08c;
+@linkColorHover:        lighten(@linkColor, 10%);
+
+
+// Typography
+// -------------------------
+@sansFontFamily:        "Helvetica Neue", Helvetica, Arial, sans-serif;
+@serifFontFamily:       Georgia, "Times New Roman", Times, serif;
+@monoFontFamily:        Monaco, Menlo, Consolas, "Courier New", monospace;
+
+@baseFontSize:          14px;
+@baseFontFamily:        @sansFontFamily;
+@baseLineHeight:        20px;
+@altFontFamily:         @serifFontFamily;
+
+@headingsFontFamily:    inherit; // empty to use BS default, @baseFontFamily
+@headingsFontWeight:    bold;    // instead of browser default, bold
+@headingsColor:         inherit; // empty to use BS default, @textColor
+
+// Tables
+// -------------------------
+@tableBackground:                   transparent; // overall background-color
+@tableBackgroundAccent:             darken(@bodyBackground, 2%); // for striping
+@tableBackgroundHover:              darken(@bodyBackground, 4%); // for hover
+@tableBorder:                       #ddd; // table and cell border
+
+
+// Buttons
+// -------------------------
+@btnBackground:                     darken(@grayLighter, 7%);
+@btnBackgroundHighlight:            darken(@grayLighter, 5%);
+@btnBorder:                         #ccc;
+
+@btnPrimaryBackground:              @linkColor;
+@btnPrimaryBackgroundHighlight:     lighten(@btnPrimaryBackground, 8%);
+
+@btnInfoBackground:                 lighten(desaturate(@linkColor, 70%), 5%);
+@btnInfoBackgroundHighlight:        lighten(@btnInfoBackground, 12%);
+
+@btnSuccessBackground:              @green;
+@btnSuccessBackgroundHighlight:     lighten(@btnSuccessBackground, 12%);
+
+@btnWarningBackground:              darken(@orange, 2%);
+@btnWarningBackgroundHighlight:     lighten(@btnWarningBackground, 12%);
+
+@btnDangerBackground:               lighten(@red, 12%);
+@btnDangerBackgroundHighlight:      lighten(@btnDangerBackground, 12%);
+
+@btnInverseBackground:              @gray;
+@btnInverseBackgroundHighlight:     lighten(@btnInverseBackground, 16%);
+
+
+// Forms
+// -------------------------
+@inputBackground:               @white;
+@inputBorder:                   #ccc;
+@inputBorderRadius:             3px;
+@inputDisabledBackground:       @grayLighter;
+@formActionsBackground:         #f5f5f5;
+
+// Dropdowns
+// -------------------------
+@dropdownBackground:            @white;
+@dropdownBorder:                rgba(0,0,0,.2);
+@dropdownDividerTop:            #e5e5e5;
+@dropdownDividerBottom:         @white;
+
+@dropdownLinkColor:             @grayDark;
+@dropdownLinkColorHover:        @white;
+@dropdownLinkColorActive:       @dropdownLinkColor;
+
+@dropdownLinkBackgroundActive:  @linkColor;
+@dropdownLinkBackgroundHover:   @dropdownLinkBackgroundActive;
+
+
+
+// COMPONENT VARIABLES
+// --------------------------------------------------
+
+// Z-index master list
+// -------------------------
+// Used for a bird's eye view of components dependent on the z-axis
+// Try to avoid customizing these :)
+@zindexDropdown:          1000;
+@zindexPopover:           1010;
+@zindexTooltip:           1030;
+@zindexFixedNavbar:       1030;
+@zindexModalBackdrop:     1040;
+@zindexModal:             1050;
+
+
+// Sprite icons path
+// -------------------------
+@iconSpritePath:          "../img/glyphicons-halflings.png";
+@iconWhiteSpritePath:     "../img/glyphicons-halflings-white.png";
+
+
+// Input placeholder text color
+// -------------------------
+@placeholderText:         @grayLight;
+
+
+// Hr border color
+// -------------------------
+@hrBorder:                @grayLighter;
+
+
+// Horizontal forms & lists
+// -------------------------
+@horizontalComponentOffset:       180px;
+
+
+// Wells
+// -------------------------
+@wellBackground:                  #f5f5f5;
+
+
+// Navbar
+// -------------------------
+@navbarCollapseWidth:             979px;
+
+@navbarHeight:                    64px;
+@navbarBackground:                darken(@bodyBackground, 4%);
+@navbarBackgroundHighlight:       lighten(@navbarBackground, 10%);
+@navbarBorder:                    @white;
+
+@navbarText:                      @grayDarker;
+@navbarLinkColor:                 @grayDarker;
+@navbarLinkColorHover:            @grayDark;
+@navbarLinkColorActive:           @white;
+@navbarLinkBackgroundHover:       darken(@navbarBackground, 4%);
+@navbarLinkBackgroundActive:      @linkColor;
+
+@navbarBrandColor:                @navbarLinkColor;
+
+// Inverted navbar
+@navbarInverseHeight:                    40px;
+@navbarInverseBackground:                darken(@navbarBackground, 5%);
+@navbarInverseBackgroundHighlight:       @navbarBackground;
+@navbarInverseBorder:                    darken(@navbarInverseBackground, 10%);
+
+@navbarInverseText:                      @grayDark;
+@navbarInverseLinkColor:                 @grayLight;
+@navbarInverseLinkColorHover:            @white;
+@navbarInverseLinkColorActive:           @navbarInverseLinkColorHover;
+@navbarInverseLinkBackgroundHover:       lighten(@navbarInverseBackground, 2%);
+@navbarInverseLinkBackgroundActive:      @navbarBackground;
+
+@navbarInverseSearchBackground:          lighten(@navbarInverseBackground, 25%);
+@navbarInverseSearchBackgroundFocus:     @white;
+@navbarInverseSearchBorder:              @navbarInverseBackground;
+@navbarInverseSearchPlaceholderColor:    #ccc;
+
+@navbarInverseBrandColor:                @navbarInverseLinkColor;
+
+
+// Pagination
+// -------------------------
+@paginationBackground:                #fff;
+@paginationBorder:                    #ddd;
+@paginationActiveBackground:          #f5f5f5;
+
+
+// Hero unit
+// -------------------------
+@heroUnitBackground:              @grayLighter;
+@heroUnitHeadingColor:            inherit;
+@heroUnitLeadColor:               inherit;
+
+
+// Form states and alerts
+// -------------------------
+@warningText:             #c09853;
+@warningBackground:       #fcf8e3;
+@warningBorder:           darken(spin(@warningBackground, -10), 3%);
+
+@errorText:               #b94a48;
+@errorBackground:         #f2dede;
+@errorBorder:             darken(spin(@errorBackground, -10), 3%);
+
+@successText:             #468847;
+@successBackground:       #dff0d8;
+@successBorder:           darken(spin(@successBackground, -10), 5%);
+
+@infoText:                #3a87ad;
+@infoBackground:          #d9edf7;
+@infoBorder:              darken(spin(@infoBackground, -10), 7%);
+
+
+// Tooltips and popovers
+// -------------------------
+@tooltipColor:            #fff;
+@tooltipBackground:       #000;
+@tooltipArrowWidth:       5px;
+@tooltipArrowColor:       @tooltipBackground;
+
+@popoverBackground:       #fff;
+@popoverArrowWidth:       10px;
+@popoverArrowColor:       #fff;
+@popoverTitleBackground:  darken(@popoverBackground, 3%);
+
+// Special enhancement for popovers
+@popoverArrowOuterWidth:  @popoverArrowWidth + 1;
+@popoverArrowOuterColor:  rgba(0,0,0,.25);
+
+
+
+// GRID
+// --------------------------------------------------
+
+
+// Default 940px grid
+// -------------------------
+@gridColumns:             12;
+@gridColumnWidth:         60px;
+@gridGutterWidth:         20px;
+@gridRowWidth:            (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
+
+// 1200px min
+@gridColumnWidth1200:     70px;
+@gridGutterWidth1200:     30px;
+@gridRowWidth1200:        (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
+
+// 768px-979px
+@gridColumnWidth768:      42px;
+@gridGutterWidth768:      20px;
+@gridRowWidth768:         (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
+
+
+// Fluid grid
+// -------------------------
+@fluidGridColumnWidth:    percentage(@gridColumnWidth/@gridRowWidth);
+@fluidGridGutterWidth:    percentage(@gridGutterWidth/@gridRowWidth);
+
+// 1200px min
+@fluidGridColumnWidth1200:     percentage(@gridColumnWidth1200/@gridRowWidth1200);
+@fluidGridGutterWidth1200:     percentage(@gridGutterWidth1200/@gridRowWidth1200);
+
+// 768px-979px
+@fluidGridColumnWidth768:      percentage(@gridColumnWidth768/@gridRowWidth768);
+@fluidGridGutterWidth768:      percentage(@gridGutterWidth768/@gridRowWidth768);

BIN
static/admin/img/glyphicons-halflings-white.png


BIN
static/admin/img/glyphicons-halflings.png


+ 65 - 0
static/admin/js/admin.js

@@ -0,0 +1,65 @@
+$(function () {
+	// Register tooltips
+	$('.tooltip-top').tooltip({placement: 'top'})
+	$('.tooltip-bottom').tooltip({placement: 'bottom'})
+	$('.tooltip-left').tooltip({placement: 'left'})
+	$('.tooltip-right').tooltip({placement: 'right'})
+	
+	// Register popovers
+	$('.popover-top').popover({placement: 'top'})
+	$('.popover-bottom').popover({placement: 'bottom'})
+	$('.popover-left').popover({placement: 'left'})
+	$('.popover-right').popover({placement: 'right'})
+	
+	// Start all dropdowns
+	$('.dropdown-toggle').dropdown()
+	
+	// Dont hide clickable dropdowns
+	$('.dropdown-clickable').on('click', function (e) {
+	  e.stopPropagation()
+	});
+	
+	// Make yes-no switches work
+	$('.yes-no-switch').toggleButtons({
+	  style: {
+	    enabled: "primary",
+	    disabled: "danger"
+	  }
+	});
+	
+	// Checkbox Group Master
+	$('input.checkbox-master').live('click', function(){
+		if($(this).is(':checked')){
+			$('input.checkbox-member').attr("checked" ,"checked");
+		}
+		else
+		{
+			$('input.checkbox-member').removeAttr('checked');
+		}
+	});
+	
+	// Checkbox Group Member
+	$('input.checkbox-member').live('click', function(){
+		if(!$(this).is(':checked')){
+			$('input.checkbox-master').removeAttr('checked');
+		}
+	});
+	
+	// Check Confirmation on links
+	$('a.confirm').live('click', function(){
+		var decision = confirm(jQuery.data(this, 'jsconfirm'));
+		return decision
+	});
+	
+	// Check Confirmation on forms
+	$('form.confirm').live('submit', function(){
+		data = $(this).data();
+		var decision = confirm(data.jsconfirm);
+		return decision
+	});
+	
+	// Go back one page
+	$('.go-back').on('click', function (e) {
+	  history.go(-1)
+	})
+})

+ 6 - 0
static/admin/js/bootstrap.min.js

@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){e(function(){"use strict";e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(function(){e("body").on("click.alert.data-api",t,n.prototype.close)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(function(){e("body").on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f=e.Event("slide",{relatedTarget:i[0]});this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u]();if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(function(){e("body").on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=!i.data("modal")&&e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(function(){e("body").on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})})}(window.jQuery),!function(e){"use strict";function r(){i(e(t)).removeClass("open")}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a<r.length-1&&a++,~a||(a=0),r.eq(a).focus()}},e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e(function(){e("html").on("click.dropdown.data-api touchstart.dropdown.data-api",r),e("body").on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;e("body").addClass("modal-open"),this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1).focus(),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.trigger("shown")}):t.$element.trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,e("body").removeClass("modal-open"),this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(e){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(function(){e("body").on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.remove().css({top:0,left:0,display:"block"}).appendTo(t?this.$element:document.body),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.css(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).remove()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.remove()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.remove(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0,html:!0}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active a").last()[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(function(){e("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),(e.browser.chrome||e.browser.webkit||e.browser.msie)&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e(function(){e("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);

+ 4 - 0
static/admin/js/jquery-1.7.2.min.js

@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);

+ 138 - 0
static/admin/js/jquery.toggle.buttons.js

@@ -0,0 +1,138 @@
+!function ($) {
+  "use strict";
+  // version: 1.6
+  // by Mattia Larentis - follow me on twitter! @SpiritualGuru
+
+  $.fn.toggleButtons = function (method) {
+    var $element
+      , $labelEnabled
+      , options
+      , active
+      , styleActive
+      , styleDisabled
+      , animationCss
+      , transitionSpeed = 0.05
+      , defaultSpeed = 0.05
+      , methods = {
+        init: function (opt) {
+          this.each(function () {
+            $element = $(this);
+
+            options = $.extend({}, $.fn.toggleButtons.defaults, opt);
+
+            $element.attr("data-enabled", options.label.enabled === undefined ? "ON" : options.label.enabled);
+            $element.attr("data-disabled", options.label.disabled === undefined ? "OFF " : options.label.disabled);
+
+            $element.addClass('toggle-button');
+
+            $labelEnabled = $('<label></label>').attr('for', $element.find('input').attr('id'));
+            $element.append($labelEnabled);
+
+            if (options.animated) {
+              $element.addClass('toggle-button-animated');
+
+              if (options.transitionSpeed !== undefined)
+                if (/^(\d*%$)/.test(options.transitionSpeed))  // is a percent value?
+                  transitionSpeed = defaultSpeed * parseInt(options.transitionSpeed) / 100;
+                else
+                  transitionSpeed = options.transitionSpeed;
+
+              animationCss = ["-webkit-", "-moz-", "-o-", ""];
+              $(animationCss).each(function () {
+                $element.find('label').css(this + 'transition', 'all ' + transitionSpeed + 's');
+              });
+            }
+
+            $element.css('width', options.width);
+
+            active = $element.find('input').is(':checked');
+
+            if (!active)
+              $element.addClass('disabled');
+
+            if($element.find('input').is(':disabled'))
+              $element.addClass('deactivate');
+
+            styleActive = options.style.enabled === undefined ? "" : options.style.enabled;
+            styleDisabled = options.style.disabled === undefined ? "" : options.style.disabled;
+
+            if (active && styleActive !== undefined)
+              $element.addClass(styleActive);
+            if (!active && styleDisabled !== undefined)
+              $element.addClass(styleDisabled);
+
+            $element.on('click', function (e) {
+              if ($(e.target).is('input'))
+                return true;
+
+              e.stopPropagation();
+              $(this).find('label').click();
+            });
+
+            $element.find('input').on('change', function(e) {
+              e.stopPropagation();
+              e.preventDefault();
+
+              $element.toggleButtons("toggleState", true);
+            });
+
+            $element.find('label').on('click', function (e) {
+              e.stopPropagation();
+              e.preventDefault();
+
+              if($element.is('.deactivate'))
+                return true;
+
+              $element = $(this).parent();
+
+              $element
+                .delay(transitionSpeed * 500).queue(function () {
+                  $(this).toggleClass('disabled')
+                    .toggleClass(styleActive)
+                    .toggleClass(styleDisabled)
+                    .dequeue();
+                });
+
+              active = !($element.find('input').is(':checked'));
+
+              $element.find('input').attr('checked', active);
+              options.onChange($element, active, e);
+            });
+          });
+        },
+        toggleActivation: function () {
+          $(this).toggleClass('deactivate');
+        },
+        toggleState: function(clickOnAnotherLabel) {
+          if(clickOnAnotherLabel !== undefined)
+            $(this).toggleClass('disabled');
+          else
+            $(this).find('label').click();
+        }
+      };
+
+    if (methods[method]) {
+      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+    } else if (typeof method === 'object' || !method) {
+      return methods.init.apply(this, arguments);
+    } else {
+      $.error('Method ' + method + ' does not exist on jQuery.tooltip');
+    }
+  };
+
+  $.fn.toggleButtons.defaults = {
+    onChange: function () {
+    },
+    width: 100,
+    animated: true,
+    transitionSpeed: undefined,
+    label: {
+      enabled: undefined,
+      disabled: undefined
+    },
+    style: {
+      enabled: undefined,
+      disabled: undefined
+    }
+  };
+}($);

BIN
static/favicon.ico


+ 1 - 0
static/sora/css/admin.css

@@ -0,0 +1 @@
+lessc: ENOENT, open 'J:\_misago\misago\static\sora\css\admin.less'

+ 34 - 0
static/sora/css/bootstrap/accordion.less

@@ -0,0 +1,34 @@
+//
+// Accordion
+// --------------------------------------------------
+
+
+// Parent container
+.accordion {
+  margin-bottom: @baseLineHeight;
+}
+
+// Group == heading + body
+.accordion-group {
+  margin-bottom: 2px;
+  border: 1px solid #e5e5e5;
+  .border-radius(4px);
+}
+.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;
+}

+ 65 - 0
static/sora/css/bootstrap/alerts.less

@@ -0,0 +1,65 @@
+//
+// 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(4px);
+  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-danger,
+.alert-error {
+  background-color: @errorBackground;
+  border-color: @errorBorder;
+  color: @errorText;
+}
+.alert-info {
+  background-color: @infoBackground;
+  border-color: @infoBorder;
+  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;
+}

+ 62 - 0
static/sora/css/bootstrap/bootstrap.less

@@ -0,0 +1,62 @@
+/*!
+ * Bootstrap v2.1.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+// CSS Reset
+@import "reset.less";
+
+// Core variables and mixins
+@import "variables.less"; // Modify this for custom colors, font-sizes, etc
+@import "mixins.less";
+
+// Grid system and page structure
+@import "scaffolding.less";
+@import "grid.less";
+@import "layouts.less";
+
+// Base CSS
+@import "type.less";
+@import "code.less";
+@import "forms.less";
+@import "tables.less";
+
+// Components: common
+@import "sprites.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 "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

+ 24 - 0
static/sora/css/bootstrap/breadcrumbs.less

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

+ 245 - 0
static/sora/css/bootstrap/button-groups.less

@@ -0,0 +1,245 @@
+//
+// Button groups
+// --------------------------------------------------
+
+
+// Make the div behave like a button
+.btn-group {
+  position: relative;
+  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-group {
+    display: inline-block;
+    .ie7-inline-block();
+  }
+  .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 {
+  font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack
+}
+
+// Reset fonts for other sizes
+.btn-group > .btn-mini {
+  font-size: 11px;
+}
+.btn-group > .btn-small {
+  font-size: 12px;
+}
+.btn-group > .btn-large {
+  font-size: 16px;
+}
+
+// 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;
+     -webkit-border-top-left-radius: 4px;
+         -moz-border-radius-topleft: 4px;
+             border-top-left-radius: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+      -moz-border-radius-bottomleft: 4px;
+          border-bottom-left-radius: 4px;
+}
+// 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 {
+     -webkit-border-top-right-radius: 4px;
+         -moz-border-radius-topright: 4px;
+             border-top-right-radius: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+      -moz-border-radius-bottomright: 4px;
+          border-bottom-right-radius: 4px;
+}
+// Reset corners for large buttons
+.btn-group > .btn.large:first-child {
+  margin-left: 0;
+     -webkit-border-top-left-radius: 6px;
+         -moz-border-radius-topleft: 6px;
+             border-top-left-radius: 6px;
+  -webkit-border-bottom-left-radius: 6px;
+      -moz-border-radius-bottomleft: 6px;
+          border-bottom-left-radius: 6px;
+}
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+     -webkit-border-top-right-radius: 6px;
+         -moz-border-radius-topright: 6px;
+             border-top-right-radius: 6px;
+  -webkit-border-bottom-right-radius: 6px;
+      -moz-border-radius-bottomright: 6px;
+          border-bottom-right-radius: 6px;
+}
+
+// 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-mini .caret,
+.btn-small .caret,
+.btn-large .caret {
+  margin-top: 6px;
+}
+.btn-large .caret {
+  border-left-width:  5px;
+  border-right-width: 5px;
+  border-top-width:   5px;
+}
+// Upside down carets for .dropup
+.dropup .btn-large .caret {
+  border-bottom: 5px solid @black;
+  border-top: 0;
+}
+
+
+
+// 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;
+  width: 100%;
+  .border-radius(0);
+}
+.btn-group-vertical .btn + .btn {
+  margin-left: 0;
+  margin-top: -1px;
+}
+.btn-group-vertical .btn:first-child {
+  .border-radius(4px 4px 0 0);
+}
+.btn-group-vertical .btn:last-child {
+  .border-radius(0 0 4px 4px);
+}
+.btn-group-vertical .btn-large:first-child {
+  .border-radius(6px 6px 0 0);
+}
+.btn-group-vertical .btn-large:last-child {
+  .border-radius(0 0 6px 6px);
+}

+ 231 - 0
static/sora/css/bootstrap/buttons.less

@@ -0,0 +1,231 @@
+//
+// Buttons
+// --------------------------------------------------
+
+
+// Base styles
+// --------------------------------------------------
+
+// Core
+.btn {
+  display: inline-block;
+  .ie7-inline-block();
+  padding: 4px 14px;
+  margin-bottom: 0; // For input.btn
+  font-size: @baseFontSize;
+  line-height: @baseLineHeight;
+  *line-height: @baseLineHeight;
+  text-align: center;
+  vertical-align: middle;
+  cursor: pointer;
+  .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75));
+  border: 1px solid @btnBorder;
+  *border: 0; // Remove the border to prevent IE7's black border on input:focus
+  border-bottom-color: darken(@btnBorder, 10%);
+  .border-radius(4px);
+  .ie7-restore-left-whitespace(); // Give IE7 some love
+  .box-shadow(inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05));
+
+  // Hover state
+  &:hover {
+    color: @grayDark;
+    text-decoration: none;
+    background-color: darken(@white, 10%);
+    *background-color: darken(@white, 15%); /* Buttons in IE7 don't get borders, so darken on hover */
+    background-position: 0 -15px;
+
+    // transition is only when going to hover, otherwise the background
+    // behind the gradient (there for IE<=9 fallback) gets mismatched
+    .transition(background-position .1s linear);
+  }
+
+  // Focus state for keyboard and accessibility
+  &:focus {
+    .tab-focus();
+  }
+
+  // Active state
+  &.active,
+  &:active {
+    background-color: darken(@white, 10%);
+    background-color: darken(@white, 15%) e("\9");
+    background-image: none;
+    outline: 0;
+    .box-shadow(inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05));
+  }
+
+  // Disabled state
+  &.disabled,
+  &[disabled] {
+    cursor: default;
+    background-color: darken(@white, 10%);
+    background-image: none;
+    .opacity(65);
+    .box-shadow(none);
+  }
+
+}
+
+
+
+// Button Sizes
+// --------------------------------------------------
+
+// Large
+.btn-large {
+  padding: 9px 14px;
+  font-size: @baseFontSize + 2px;
+  line-height: normal;
+  .border-radius(5px);
+}
+.btn-large [class^="icon-"] {
+  margin-top: 2px;
+}
+
+// Small
+.btn-small {
+  padding: 3px 9px;
+  font-size: @baseFontSize - 2px;
+  line-height: @baseLineHeight - 2px;
+}
+.btn-small [class^="icon-"] {
+  margin-top: 0;
+}
+
+// Mini
+.btn-mini {
+  padding: 2px 6px;
+  font-size: @baseFontSize - 3px;
+  line-height: @baseLineHeight - 3px;
+}
+
+
+// Block button
+// -------------------------
+
+.btn-block {
+  display: block;
+  width: 100%;
+  padding-left: 0;
+  padding-right: 0;
+  .box-sizing(border-box);
+}
+
+// Vertically space out multiple block buttons
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+
+// Specificity overrides
+input[type="submit"],
+input[type="reset"],
+input[type="button"] {
+  &.btn-block {
+    width: 100%;
+  }
+}
+
+
+
+// Alternate buttons
+// --------------------------------------------------
+
+// Provide *some* extra contrast for those who can get it
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+  color: rgba(255,255,255,.75);
+}
+
+// Set the backgrounds
+// -------------------------
+.btn {
+  // reset here as of 2.0.3 due to Recess property order
+  border-color: #c5c5c5;
+  border-color: rgba(0,0,0,.15) rgba(0,0,0,.15) rgba(0,0,0,.25);
+}
+.btn-primary {
+  .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
+}
+// Warning appears are orange
+.btn-warning {
+  .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight);
+}
+// Danger and error appear as red
+.btn-danger {
+  .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight);
+}
+// Success appears as green
+.btn-success {
+  .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
+}
+// Info appears as a neutral blue
+.btn-info {
+  .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight);
+}
+// Inverse appears as dark gray
+.btn-inverse {
+  .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight);
+}
+
+
+// Cross-browser Jank
+// --------------------------------------------------
+
+button.btn,
+input[type="submit"].btn {
+
+  // Firefox 3.6 only I believe
+  &::-moz-focus-inner {
+    padding: 0;
+    border: 0;
+  }
+
+  // IE7 has some default padding on button controls
+  *padding-top: 3px;
+  *padding-bottom: 3px;
+
+  &.btn-large {
+    *padding-top: 7px;
+    *padding-bottom: 7px;
+  }
+  &.btn-small {
+    *padding-top: 3px;
+    *padding-bottom: 3px;
+  }
+  &.btn-mini {
+    *padding-top: 1px;
+    *padding-bottom: 1px;
+  }
+}
+
+
+// Link buttons
+// --------------------------------------------------
+
+// Make a button look and behave like a link
+.btn-link,
+.btn-link:active,
+.btn-link[disabled] {
+  background-color: transparent;
+  background-image: none;
+  .box-shadow(none);
+}
+.btn-link {
+  border-color: transparent;
+  cursor: pointer;
+  color: @linkColor;
+  .border-radius(0);
+}
+.btn-link:hover {
+  color: @linkColorHover;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover {
+  color: @grayDark;
+  text-decoration: none;
+}

+ 131 - 0
static/sora/css/bootstrap/carousel.less

@@ -0,0 +1,131 @@
+//
+// Carousel
+// --------------------------------------------------
+
+
+.carousel {
+  position: relative;
+  margin-bottom: @baseLineHeight;
+  line-height: 1;
+}
+
+.carousel-inner {
+  overflow: hidden;
+  width: 100%;
+  position: relative;
+}
+
+.carousel {
+
+  .item {
+    display: none;
+    position: relative;
+    .transition(.6s ease-in-out left);
+  }
+
+  // Account for jankitude on images
+  .item > 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 state
+  &:hover {
+    color: @white;
+    text-decoration: none;
+    .opacity(90);
+  }
+}
+
+
+// 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;
+}

+ 31 - 0
static/sora/css/bootstrap/close.less

@@ -0,0 +1,31 @@
+//
+// 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 {
+    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;
+}

+ 58 - 0
static/sora/css/bootstrap/code.less

@@ -0,0 +1,58 @@
+//
+// 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;
+}
+
+// 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(4px);
+
+  // 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;
+    background-color: transparent;
+    border: 0;
+  }
+}
+
+// Enable scrollable blocks of code
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}

+ 22 - 0
static/sora/css/bootstrap/component-animations.less

@@ -0,0 +1,22 @@
+//
+// 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;
+  }
+}

+ 210 - 0
static/sora/css/bootstrap/dropdowns.less

@@ -0,0 +1,210 @@
+//
+// 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
+  a {
+    display: block;
+    padding: 3px 20px;
+    clear: both;
+    font-weight: normal;
+    line-height: @baseLineHeight;
+    color: @dropdownLinkColor;
+    white-space: nowrap;
+  }
+}
+
+// Hover state
+// -----------
+.dropdown-menu li > a:hover,
+.dropdown-menu li > a:focus,
+.dropdown-submenu:hover > a {
+  text-decoration: none;
+  color: @dropdownLinkColorHover;
+  background-color: @dropdownLinkBackgroundHover;
+  #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%));
+}
+
+// Active state
+// ------------
+.dropdown-menu .active > a,
+.dropdown-menu .active > a:hover {
+  color: @dropdownLinkColorHover;
+  text-decoration: none;
+  outline: 0;
+  background-color: @dropdownLinkBackgroundActive;
+  #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%));
+}
+
+// Disabled state
+// --------------
+// Gray out text and ensure the hover state remains gray
+.dropdown-menu .disabled > a,
+.dropdown-menu .disabled > a:hover {
+  color: @grayLight;
+}
+// Nuke hover effects
+.dropdown-menu .disabled > a:hover {
+  text-decoration: none;
+  background-color: transparent;
+  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;
+  }
+}
+
+// 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;
+}
+.dropdown-submenu > .dropdown-menu {
+  top: 0;
+  left: 100%;
+  margin-top: -6px;
+  margin-left: -1px;
+  -webkit-border-radius: 0 6px 6px 6px;
+     -moz-border-radius: 0 6px 6px 6px;
+          border-radius: 0 6px 6px 6px;
+}
+.dropdown-submenu:hover > .dropdown-menu {
+  display: block;
+}
+
+.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;
+}
+
+
+// Tweak nav headers
+// -----------------
+// Increase padding from 15px to 20px on sides
+.dropdown .dropdown-menu .nav-header {
+  padding-left: 20px;
+  padding-right: 20px;
+}
+
+// Typeahead
+// ---------
+.typeahead {
+  margin-top: 2px; // give it some space to breathe
+  .border-radius(4px);
+}

+ 650 - 0
static/sora/css/bootstrap/forms.less

@@ -0,0 +1,650 @@
+//
+// Forms
+// --------------------------------------------------
+
+
+// GENERAL STYLES
+// --------------
+
+// Make all forms have space below them
+form {
+  margin: 0 0 @baseLineHeight;
+}
+
+fieldset {
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+
+// Groups of fields with labels on top (legends)
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: @baseLineHeight;
+  font-size: @baseFontSize * 1.5;
+  line-height: @baseLineHeight * 2;
+  color: @grayDark;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+
+  // Small
+  small {
+    font-size: @baseLineHeight * .75;
+    color: @grayLight;
+  }
+}
+
+// Set font for forms
+label,
+input,
+button,
+select,
+textarea {
+  #font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here
+}
+input,
+button,
+select,
+textarea {
+  font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element)
+}
+
+// Identify controls by their labels
+label {
+  display: block;
+  margin-bottom: 5px;
+}
+
+// Form controls
+// -------------------------
+
+// Shared size and type resets
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+  display: inline-block;
+  height: @baseLineHeight;
+  padding: 4px 6px;
+  margin-bottom: 9px;
+  font-size: @baseFontSize;
+  line-height: @baseLineHeight;
+  color: @gray;
+  .border-radius(@inputBorderRadius);
+}
+
+// Reset appearance properties for textual inputs and textarea
+// Declare width for legacy (can't be on input[type=*] selectors or it's too specific)
+input,
+textarea,
+.uneditable-input {
+  width: 206px; // plus 12px padding and 2px border
+}
+// Reset height since textareas have rows
+textarea {
+  height: auto;
+}
+// Everything else
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+  background-color: @inputBackground;
+  border: 1px solid @inputBorder;
+  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));
+  .transition(~"border linear .2s, box-shadow linear .2s");
+
+  // Focus state
+  &:focus {
+    border-color: rgba(82,168,236,.8);
+    outline: 0;
+    outline: thin dotted \9; /* IE6-9 */
+    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6));
+  }
+}
+
+// Position radios and checkboxes better
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  *margin-top: 0; /* IE7 */
+  margin-top: 1px \9; /* IE8-9 */
+  line-height: normal;
+  cursor: pointer;
+}
+
+// Reset width of input images, buttons, radios, checkboxes
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+  width: auto; // Override of generic input selector
+}
+
+// Set the height of select and file controls to match text inputs
+select,
+input[type="file"] {
+  height: 30px; /* In IE7, the height of the select element cannot be changed by height, only font-size */
+  *margin-top: 4px; /* For IE7, add top margin to align select with labels */
+  line-height: 30px;
+}
+
+// Make select elements obey height by applying a border
+select {
+  width: 220px; // default input width + 10px of padding that doesn't get applied
+  border: 1px solid @inputBorder;
+  background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color
+}
+
+// Make multiple select elements height not fixed
+select[multiple],
+select[size] {
+  height: auto;
+}
+
+// Focus for select, file, radio, and checkbox
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  .tab-focus();
+}
+
+
+// Uneditable inputs
+// -------------------------
+
+// Make uneditable inputs look inactive
+.uneditable-input,
+.uneditable-textarea {
+  color: @grayLight;
+  background-color: darken(@inputBackground, 1%);
+  border-color: @inputBorder;
+  .box-shadow(inset 0 1px 2px rgba(0,0,0,.025));
+  cursor: not-allowed;
+}
+
+// For text that needs to appear as an input but should not be an input
+.uneditable-input {
+  overflow: hidden; // prevent text from wrapping, but still cut it off like an input does
+  white-space: nowrap;
+}
+
+// Make uneditable textareas behave like a textarea
+.uneditable-textarea {
+  width: auto;
+  height: auto;
+}
+
+
+// Placeholder
+// -------------------------
+
+// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn’t understand a selector
+input,
+textarea {
+  .placeholder();
+}
+
+
+// CHECKBOXES & RADIOS
+// -------------------
+
+// Indent the labels to position radios/checkboxes as hanging
+.radio,
+.checkbox {
+  min-height: 18px; // clear the floating input if there is no label text
+  padding-left: 18px;
+}
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+  float: left;
+  margin-left: -18px;
+}
+
+// Move the options list down to align with labels
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+  padding-top: 5px; // has to be padding because margin collaspes
+}
+
+// Radios and checkboxes on same line
+// TODO v3: Convert .inline to .control-inline
+.radio.inline,
+.checkbox.inline {
+  display: inline-block;
+  padding-top: 5px;
+  margin-bottom: 0;
+  vertical-align: middle;
+}
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+  margin-left: 10px; // space out consecutive inline controls
+}
+
+
+
+// INPUT SIZES
+// -----------
+
+// General classes for quick sizes
+.input-mini       { width: 60px; }
+.input-small      { width: 90px; }
+.input-medium     { width: 150px; }
+.input-large      { width: 210px; }
+.input-xlarge     { width: 270px; }
+.input-xxlarge    { width: 530px; }
+
+// Grid style input sizes
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+// Redeclare since the fluid row class is more specific
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+  float: none;
+  margin-left: 0;
+}
+// Ensure input-prepend/append never wraps
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+  display: inline-block;
+}
+
+
+
+// GRID SIZING FOR INPUTS
+// ----------------------
+
+// Grid sizes
+#grid > .input(@gridColumnWidth, @gridGutterWidth);
+
+// Control row for multiple inputs per line
+.controls-row {
+  .clearfix(); // Clear the float from controls
+}
+.controls-row [class*="span"] {
+  float: left; // Float to collapse white-space for proper grid alignment
+}
+
+
+
+
+// DISABLED STATE
+// --------------
+
+// Disabled and read-only inputs
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+  cursor: not-allowed;
+  background-color: @inputDisabledBackground;
+}
+// Explicitly reset the colors here
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+  background-color: transparent;
+}
+
+
+
+
+// FORM FIELD FEEDBACK STATES
+// --------------------------
+
+// Warning
+.control-group.warning {
+  .formFieldState(@warningText, @warningText, @warningBackground);
+}
+// Error
+.control-group.error {
+  .formFieldState(@errorText, @errorText, @errorBackground);
+}
+// Success
+.control-group.success {
+  .formFieldState(@successText, @successText, @successBackground);
+}
+// Success
+.control-group.info {
+  .formFieldState(@infoText, @infoText, @infoBackground);
+}
+
+// HTML5 invalid states
+// Shares styles with the .control-group.error above
+input:focus:required:invalid,
+textarea:focus:required:invalid,
+select:focus:required:invalid {
+  color: #b94a48;
+  border-color: #ee5f5b;
+  &:focus {
+    border-color: darken(#ee5f5b, 10%);
+    .box-shadow(0 0 6px lighten(#ee5f5b, 20%));
+  }
+}
+
+
+
+// FORM ACTIONS
+// ------------
+
+.form-actions {
+  padding: (@baseLineHeight - 1) 20px @baseLineHeight;
+  margin-top: @baseLineHeight;
+  margin-bottom: @baseLineHeight;
+  background-color: @formActionsBackground;
+  border-top: 1px solid #e5e5e5;
+  .clearfix(); // Adding clearfix to allow for .pull-right button containers
+}
+
+
+
+// HELP TEXT
+// ---------
+
+.help-block,
+.help-inline {
+  color: lighten(@textColor, 15%); // lighten the text some for contrast
+}
+
+.help-block {
+  display: block; // account for any element using help-block
+  margin-bottom: @baseLineHeight / 2;
+}
+
+.help-inline {
+  display: inline-block;
+  .ie7-inline-block();
+  vertical-align: middle;
+  padding-left: 5px;
+}
+
+
+
+// INPUT GROUPS
+// ------------
+
+// Allow us to put symbols and text within the input field for a cleaner look
+.input-append,
+.input-prepend {
+  margin-bottom: 5px;
+  font-size: 0;
+  white-space: nowrap; // Prevent span and input from separating
+
+  input,
+  select,
+  .uneditable-input {
+    position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness
+    margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms
+    *margin-left: 0;
+    font-size: @baseFontSize;
+    vertical-align: top;
+    .border-radius(0 @inputBorderRadius @inputBorderRadius 0);
+    // Make input on top when focused so blue border and shadow always show
+    &:focus {
+      z-index: 2;
+    }
+  }
+  .add-on {
+    display: inline-block;
+    width: auto;
+    height: @baseLineHeight;
+    min-width: 16px;
+    padding: 4px 5px;
+    font-size: @baseFontSize;
+    font-weight: normal;
+    line-height: @baseLineHeight;
+    text-align: center;
+    text-shadow: 0 1px 0 @white;
+    background-color: @grayLighter;
+    border: 1px solid #ccc;
+  }
+  .add-on,
+  .btn {
+    vertical-align: top;
+    .border-radius(0);
+  }
+  .active {
+    background-color: lighten(@green, 30);
+    border-color: @green;
+  }
+}
+.input-prepend {
+  .add-on,
+  .btn {
+    margin-right: -1px;
+  }
+  .add-on:first-child,
+  .btn:first-child {
+    .border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
+  }
+}
+.input-append {
+  input,
+  select,
+  .uneditable-input {
+    .border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
+  }
+  .add-on,
+  .btn {
+    margin-left: -1px;
+  }
+  .add-on:last-child,
+  .btn:last-child {
+    .border-radius(0 @inputBorderRadius @inputBorderRadius 0);
+  }
+}
+// Remove all border-radius for inputs with both prepend and append
+.input-prepend.input-append {
+  input,
+  select,
+  .uneditable-input {
+    .border-radius(0);
+  }
+  .add-on:first-child,
+  .btn:first-child {
+    margin-right: -1px;
+    .border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
+  }
+  .add-on:last-child,
+  .btn:last-child {
+    margin-left: -1px;
+    .border-radius(0 @inputBorderRadius @inputBorderRadius 0);
+  }
+}
+
+
+
+// SEARCH FORM
+// -----------
+
+input.search-query {
+  padding-right: 14px;
+  padding-right: 4px \9;
+  padding-left: 14px;
+  padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */
+  margin-bottom: 0; // Remove the default margin on all inputs
+  .border-radius(15px);
+}
+
+/* Allow for input prepend/append in search forms */
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+  .border-radius(0); // Override due to specificity
+}
+.form-search .input-append .search-query {
+  .border-radius(14px 0 0 14px);
+}
+.form-search .input-append .btn {
+  .border-radius(0 14px 14px 0);
+}
+.form-search .input-prepend .search-query {
+  .border-radius(0 14px 14px 0);
+}
+.form-search .input-prepend .btn {
+  .border-radius(14px 0 0 14px);
+}
+
+
+
+
+// HORIZONTAL & VERTICAL FORMS
+// ---------------------------
+
+// Common properties
+// -----------------
+
+.form-search,
+.form-inline,
+.form-horizontal {
+  input,
+  textarea,
+  select,
+  .help-inline,
+  .uneditable-input,
+  .input-prepend,
+  .input-append {
+    display: inline-block;
+    .ie7-inline-block();
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  // Re-hide hidden elements due to specifity
+  .hide {
+    display: none;
+  }
+}
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+  display: inline-block;
+}
+// Remove margin for input-prepend/-append
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+  margin-bottom: 0;
+}
+// Inline checkbox/radio labels (remove padding on left)
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+  padding-left: 0;
+  margin-bottom: 0;
+  vertical-align: middle;
+}
+// Remove float and margin, set to inline-block
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+  float: left;
+  margin-right: 3px;
+  margin-left: 0;
+}
+
+
+// Margin to space out fieldsets
+.control-group {
+  margin-bottom: @baseLineHeight / 2;
+}
+
+// Legend collapses margin, so next element is responsible for spacing
+legend + .control-group {
+  margin-top: @baseLineHeight;
+  -webkit-margin-top-collapse: separate;
+}
+
+// Horizontal-specific styles
+// --------------------------
+
+.form-horizontal {
+  // Increase spacing between groups
+  .control-group {
+    margin-bottom: @baseLineHeight;
+    .clearfix();
+  }
+  // Float the labels left
+  .control-label {
+    float: left;
+    width: @horizontalComponentOffset - 20;
+    padding-top: 5px;
+    text-align: right;
+  }
+  // Move over all input controls and content
+  .controls {
+    // Super jank IE7 fix to ensure the inputs in .input-append and input-prepend
+    // don't inherit the margin of the parent, in this case .controls
+    *display: inline-block;
+    *padding-left: 20px;
+    margin-left: @horizontalComponentOffset;
+    *margin-left: 0;
+    &:first-child {
+      *padding-left: @horizontalComponentOffset;
+    }
+  }
+  // Remove bottom margin on block level help text since that's accounted for on .control-group
+  .help-block {
+    margin-bottom: 0;
+  }
+  // And apply it only to .help-block instances that follow a form control
+  input,
+  select,
+  textarea {
+    + .help-block {
+      margin-top: @baseLineHeight / 2;
+    }
+  }
+  // Move over buttons in .form-actions to align with .controls
+  .form-actions {
+    padding-left: @horizontalComponentOffset;
+  }
+}

+ 21 - 0
static/sora/css/bootstrap/grid.less

@@ -0,0 +1,21 @@
+//
+// Grid system
+// --------------------------------------------------
+
+
+// Fixed (940px)
+#grid > .core(@gridColumnWidth, @gridGutterWidth);
+
+// Fluid (940px)
+#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
+
+// Reset utility classes due to specificity
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+  display: none;
+}
+
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+  float: right;
+}

+ 24 - 0
static/sora/css/bootstrap/hero-unit.less

@@ -0,0 +1,24 @@
+//
+// Hero unit
+// --------------------------------------------------
+
+
+.hero-unit {
+  padding: 60px;
+  margin-bottom: 30px;
+  background-color: @heroUnitBackground;
+  .border-radius(6px);
+  h1 {
+    margin-bottom: 0;
+    font-size: 60px;
+    line-height: 1;
+    color: @heroUnitHeadingColor;
+    letter-spacing: -1px;
+  }
+  p {
+    font-size: 18px;
+    font-weight: 200;
+    line-height: @baseLineHeight * 1.5;
+    color: @heroUnitLeadColor;
+  }
+}

+ 72 - 0
static/sora/css/bootstrap/labels-badges.less

@@ -0,0 +1,72 @@
+//
+// Labels and badges
+// --------------------------------------------------
+
+
+// Base classes
+.label,
+.badge {
+  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 {
+  padding: 1px 4px 2px;
+  .border-radius(3px);
+}
+.badge {
+  padding: 1px 9px 2px;
+  .border-radius(9px);
+}
+
+// Hover state, but only for links
+a {
+  &.label:hover,
+  &.badge:hover {
+    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;
+  }
+}

+ 16 - 0
static/sora/css/bootstrap/layouts.less

@@ -0,0 +1,16 @@
+//
+// 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();
+}

+ 681 - 0
static/sora/css/bootstrap/mixins.less

@@ -0,0 +1,681 @@
+//
+// Mixins
+// --------------------------------------------------
+
+
+// UTILITY MIXINS
+// --------------------------------------------------
+
+// Clearfix
+// --------
+// For clearing floats like a boss h5bp.com/q
+.clearfix {
+  *zoom: 1;
+  &:before,
+  &:after {
+    display: table;
+    content: "";
+    // Fixes Opera/contenteditable bug:
+    // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952
+    line-height: 0;
+  }
+  &:after {
+    clear: both;
+  }
+}
+
+// Webkit-style focus
+// ------------------
+.tab-focus() {
+  // Default
+  outline: thin dotted #333;
+  // Webkit
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+// Center-align a block level element
+// ----------------------------------
+.center-block() {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+// IE7 inline-block
+// ----------------
+.ie7-inline-block() {
+  *display: inline; /* IE7 inline-block hack */
+  *zoom: 1;
+}
+
+// IE7 likes to collapse whitespace on either side of the inline-block elements.
+// Ems because we're attempting to match the width of a space character. Left
+// version is for form buttons, which typically come after other elements, and
+// right version is for icons, which come before. Applying both is ok, but it will
+// mean that space between those elements will be .6em (~2 space characters) in IE7,
+// instead of the 1 space in other browsers.
+.ie7-restore-left-whitespace() {
+  *margin-left: .3em;
+
+  &:first-child {
+    *margin-left: 0;
+  }
+}
+
+.ie7-restore-right-whitespace() {
+  *margin-right: .3em;
+}
+
+// Sizing shortcuts
+// -------------------------
+.size(@height, @width) {
+  width: @width;
+  height: @height;
+}
+.square(@size) {
+  .size(@size, @size);
+}
+
+// Placeholder text
+// -------------------------
+.placeholder(@color: @placeholderText) {
+  &:-moz-placeholder {
+    color: @color;
+  }
+  &:-ms-input-placeholder {
+    color: @color;
+  }
+  &::-webkit-input-placeholder {
+    color: @color;
+  }
+}
+
+// Text overflow
+// -------------------------
+// Requires inline-block or block for proper styling
+.text-overflow() {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+// CSS image replacement
+// -------------------------
+// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
+.hide-text {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+
+
+// FONTS
+// --------------------------------------------------
+
+#font {
+  #family {
+    .serif() {
+      font-family: @serifFontFamily;
+    }
+    .sans-serif() {
+      font-family: @sansFontFamily;
+    }
+    .monospace() {
+      font-family: @monoFontFamily;
+    }
+  }
+  .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    font-size: @size;
+    font-weight: @weight;
+    line-height: @lineHeight;
+  }
+  .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    #font > #family > .serif;
+    #font > .shorthand(@size, @weight, @lineHeight);
+  }
+  .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    #font > #family > .sans-serif;
+    #font > .shorthand(@size, @weight, @lineHeight);
+  }
+  .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) {
+    #font > #family > .monospace;
+    #font > .shorthand(@size, @weight, @lineHeight);
+  }
+}
+
+
+// FORMS
+// --------------------------------------------------
+
+// Block level inputs
+.input-block-level {
+  display: block;
+  width: 100%;
+  min-height: 30px;        // Make inputs at least the height of their button counterpart
+  .box-sizing(border-box); // Makes inputs behave like true block-level elements
+}
+
+
+
+// Mixin for form field states
+.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) {
+  // Set the text color
+  > label,
+  .help-block,
+  .help-inline {
+    color: @textColor;
+  }
+  // Style inputs accordingly
+  .checkbox,
+  .radio,
+  input,
+  select,
+  textarea {
+    color: @textColor;
+  }
+  input,
+  select,
+  textarea {
+    border-color: @borderColor;
+    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
+    &:focus {
+      border-color: darken(@borderColor, 10%);
+      .box-shadow(inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%));
+    }
+  }
+  // Give a small background color for input-prepend/-append
+  .input-prepend .add-on,
+  .input-append .add-on {
+    color: @textColor;
+    background-color: @backgroundColor;
+    border-color: @textColor;
+  }
+}
+
+
+
+// CSS3 PROPERTIES
+// --------------------------------------------------
+
+// Border Radius
+.border-radius(@radius) {
+  -webkit-border-radius: @radius;
+     -moz-border-radius: @radius;
+          border-radius: @radius;
+}
+
+// Single Corner Border Radius
+.border-top-left-radius(@radius) {
+  -webkit-border-top-left-radius: @radius;
+      -moz-border-radius-topleft: @radius;
+          border-top-left-radius: @radius;
+}
+.border-top-right-radius(@radius) {
+  -webkit-border-top-right-radius: @radius;
+      -moz-border-radius-topright: @radius;
+          border-top-right-radius: @radius;
+}
+.border-bottom-right-radius(@radius) {
+  -webkit-border-bottom-right-radius: @radius;
+      -moz-border-radius-bottomright: @radius;
+          border-bottom-right-radius: @radius;
+}
+.border-bottom-left-radius(@radius) {
+  -webkit-border-bottom-left-radius: @radius;
+      -moz-border-radius-bottomleft: @radius;
+          border-bottom-left-radius: @radius;
+}
+
+// Single Side Border Radius
+.border-top-radius(@radius) {
+  .border-top-right-radius(@radius);
+  .border-top-left-radius(@radius);
+}
+.border-right-radius(@radius) {
+  .border-top-right-radius(@radius);
+  .border-bottom-right-radius(@radius);
+}
+.border-bottom-radius(@radius) {
+  .border-bottom-right-radius(@radius);
+  .border-bottom-left-radius(@radius);
+}
+.border-left-radius(@radius) {
+  .border-top-left-radius(@radius);
+  .border-bottom-left-radius(@radius);
+}
+
+// Drop shadows
+.box-shadow(@shadowA, @shadowB:X, ...){
+  // Multiple shadow solution from http://toekneestuck.com/blog/2012/05/15/less-css-arguments-variable/
+  @props: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
+  -webkit-box-shadow: @props;
+     -moz-box-shadow: @props;
+          box-shadow: @props;
+}
+
+// Transitions
+.transition(@transition) {
+  -webkit-transition: @transition;
+     -moz-transition: @transition;
+       -o-transition: @transition;
+          transition: @transition;
+}
+.transition-delay(@transition-delay) {
+  -webkit-transition-delay: @transition-delay;
+     -moz-transition-delay: @transition-delay;
+       -o-transition-delay: @transition-delay;
+          transition-delay: @transition-delay;
+}
+
+// Transformations
+.rotate(@degrees) {
+  -webkit-transform: rotate(@degrees);
+     -moz-transform: rotate(@degrees);
+      -ms-transform: rotate(@degrees);
+       -o-transform: rotate(@degrees);
+          transform: rotate(@degrees);
+}
+.scale(@ratio) {
+  -webkit-transform: scale(@ratio);
+     -moz-transform: scale(@ratio);
+      -ms-transform: scale(@ratio);
+       -o-transform: scale(@ratio);
+          transform: scale(@ratio);
+}
+.translate(@x, @y) {
+  -webkit-transform: translate(@x, @y);
+     -moz-transform: translate(@x, @y);
+      -ms-transform: translate(@x, @y);
+       -o-transform: translate(@x, @y);
+          transform: translate(@x, @y);
+}
+.skew(@x, @y) {
+  -webkit-transform: skew(@x, @y);
+     -moz-transform: skew(@x, @y);
+      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twitter/bootstrap/issues/4885
+       -o-transform: skew(@x, @y);
+          transform: skew(@x, @y);
+}
+.translate3d(@x, @y, @z) {
+  -webkit-transform: translate3d(@x, @y, @z);
+     -moz-transform: translate3d(@x, @y, @z);
+       -o-transform: translate3d(@x, @y, @z);
+          transform: translate3d(@x, @y, @z);
+}
+
+// Backface visibility
+// Prevent browsers from flickering when using CSS 3D transforms.
+// Default value is `visible`, but can be changed to `hidden
+// See git pull https://github.com/dannykeane/bootstrap.git backface-visibility for examples
+.backface-visibility(@visibility){
+	-webkit-backface-visibility: @visibility;
+	   -moz-backface-visibility: @visibility;
+	        backface-visibility: @visibility;
+}
+
+// Background clipping
+// Heads up: FF 3.6 and under need "padding" instead of "padding-box"
+.background-clip(@clip) {
+  -webkit-background-clip: @clip;
+     -moz-background-clip: @clip;
+          background-clip: @clip;
+}
+
+// Background sizing
+.background-size(@size){
+  -webkit-background-size: @size;
+     -moz-background-size: @size;
+       -o-background-size: @size;
+          background-size: @size;
+}
+
+
+// Box sizing
+.box-sizing(@boxmodel) {
+  -webkit-box-sizing: @boxmodel;
+     -moz-box-sizing: @boxmodel;
+          box-sizing: @boxmodel;
+}
+
+// User select
+// For selecting text on the page
+.user-select(@select) {
+  -webkit-user-select: @select;
+     -moz-user-select: @select;
+      -ms-user-select: @select;
+       -o-user-select: @select;
+          user-select: @select;
+}
+
+// Resize anything
+.resizable(@direction) {
+  resize: @direction; // Options: horizontal, vertical, both
+  overflow: auto; // Safari fix
+}
+
+// CSS3 Content Columns
+.content-columns(@columnCount, @columnGap: @gridGutterWidth) {
+  -webkit-column-count: @columnCount;
+     -moz-column-count: @columnCount;
+          column-count: @columnCount;
+  -webkit-column-gap: @columnGap;
+     -moz-column-gap: @columnGap;
+          column-gap: @columnGap;
+}
+
+// Optional hyphenation
+.hyphens(@mode: auto) {
+  word-wrap: break-word;
+  -webkit-hyphens: @mode;
+     -moz-hyphens: @mode;
+      -ms-hyphens: @mode;
+       -o-hyphens: @mode;
+          hyphens: @mode;
+}
+
+// Opacity
+.opacity(@opacity) {
+  opacity: @opacity / 100;
+  filter: ~"alpha(opacity=@{opacity})";
+}
+
+
+
+// BACKGROUNDS
+// --------------------------------------------------
+
+// Add an alphatransparency value to any background or border color (via Elyse Holladay)
+#translucent {
+  .background(@color: @white, @alpha: 1) {
+    background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
+  }
+  .border(@color: @white, @alpha: 1) {
+    border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
+    .background-clip(padding-box);
+  }
+}
+
+// Gradient Bar Colors for buttons and alerts
+.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) {
+  color: @textColor;
+  text-shadow: @textShadow;
+  #gradient > .vertical(@primaryColor, @secondaryColor);
+  border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%);
+  border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%);
+}
+
+// Gradients
+#gradient {
+  .horizontal(@startColor: #555, @endColor: #333) {
+    background-color: @endColor;
+    background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+
+    background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+
+    background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+
+    background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10
+    background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10
+    background-repeat: repeat-x;
+    filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@startColor),argb(@endColor))); // IE9 and down
+  }
+  .vertical(@startColor: #555, @endColor: #333) {
+    background-color: mix(@startColor, @endColor, 60%);
+    background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+
+    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+
+    background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+
+    background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10
+    background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10
+    background-repeat: repeat-x;
+    filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down
+  }
+  .directional(@startColor: #555, @endColor: #333, @deg: 45deg) {
+    background-color: @endColor;
+    background-repeat: repeat-x;
+    background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+
+    background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+
+    background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10
+    background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10
+  }
+  .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) {
+    background-color: mix(@midColor, @endColor, 80%);
+    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor));
+    background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor);
+    background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor);
+    background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor);
+    background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor);
+    background-repeat: no-repeat;
+    filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback
+  }
+  .radial(@innerColor: #555, @outerColor: #333)  {
+    background-color: @outerColor;
+    background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor));
+    background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor);
+    background-image: -moz-radial-gradient(circle, @innerColor, @outerColor);
+    background-image: -o-radial-gradient(circle, @innerColor, @outerColor);
+    background-repeat: no-repeat;
+  }
+  .striped(@color: #555, @angle: 45deg) {
+    background-color: @color;
+    background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent));
+    background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+    background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+    background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+    background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
+  }
+}
+// Reset filters for IE
+.reset-filter() {
+  filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
+}
+
+
+
+// COMPONENT MIXINS
+// --------------------------------------------------
+
+// Horizontal dividers
+// -------------------------
+// Dividers (basically an hr) within dropdowns and nav lists
+.nav-divider(@top: #e5e5e5, @bottom: @white) {
+  // IE7 needs a set width since we gave a height. Restricting just
+  // to IE7 to keep the 1px left/right space in other browsers.
+  // It is unclear where IE is getting the extra space that we need
+  // to negative-margin away, but so it goes.
+  *width: 100%;
+  height: 1px;
+  margin: ((@baseLineHeight / 2) - 1) 1px; // 8px 1px
+  *margin: -5px 0 5px;
+  overflow: hidden;
+  background-color: @top;
+  border-bottom: 1px solid @bottom;
+}
+
+// Button backgrounds
+// ------------------
+.buttonBackground(@startColor, @endColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) {
+  // gradientBar will set the background to a pleasing blend of these, to support IE<=9
+  .gradientBar(@startColor, @endColor, @textColor, @textShadow);
+  *background-color: @endColor; /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+  .reset-filter();
+
+  // in these cases the gradient won't cover the background, so we override
+  &:hover, &:active, &.active, &.disabled, &[disabled] {
+    color: @textColor;
+    background-color: @endColor;
+    *background-color: darken(@endColor, 5%);
+  }
+
+  // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves
+  &:active,
+  &.active {
+    background-color: darken(@endColor, 10%) e("\9");
+  }
+}
+
+// Navbar vertical align
+// -------------------------
+// Vertically center elements in the navbar.
+// Example: an element has a height of 30px, so write out `.navbarVerticalAlign(30px);` to calculate the appropriate top margin.
+.navbarVerticalAlign(@elementHeight) {
+  margin-top: (@navbarHeight - @elementHeight) / 2;
+}
+
+
+
+// Grid System
+// -----------
+
+// Centered container element
+.container-fixed() {
+  margin-right: auto;
+  margin-left: auto;
+  .clearfix();
+}
+
+// Table columns
+.tableColumns(@columnSpan: 1) {
+  float: none; // undo default grid column styles
+  width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; // 16 is total padding on left and right of table cells
+  margin-left: 0; // undo default grid column styles
+}
+
+// Make a Grid
+// Use .makeRow and .makeColumn to assign semantic layouts grid system behavior
+.makeRow() {
+  margin-left: @gridGutterWidth * -1;
+  .clearfix();
+}
+.makeColumn(@columns: 1, @offset: 0) {
+  float: left;
+  margin-left: (@gridColumnWidth * @offset) + (@gridGutterWidth * (@offset - 1)) + (@gridGutterWidth * 2);
+  width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1));
+}
+
+// The Grid
+#grid {
+
+  .core (@gridColumnWidth, @gridGutterWidth) {
+
+    .spanX (@index) when (@index > 0) {
+      (~".span@{index}") { .span(@index); }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    .offsetX (@index) when (@index > 0) {
+      (~".offset@{index}") { .offset(@index); }
+      .offsetX(@index - 1);
+    }
+    .offsetX (0) {}
+
+    .offset (@columns) {
+      margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1));
+    }
+
+    .span (@columns) {
+      width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1));
+    }
+
+    .row {
+      margin-left: @gridGutterWidth * -1;
+      .clearfix();
+    }
+
+    [class*="span"] {
+      float: left;
+      min-height: 1px; // prevent collapsing columns
+      margin-left: @gridGutterWidth;
+    }
+
+    // Set the container width, and override it for fixed navbars in media queries
+    .container,
+    .navbar-static-top .container,
+    .navbar-fixed-top .container,
+    .navbar-fixed-bottom .container { .span(@gridColumns); }
+
+    // generate .spanX and .offsetX
+    .spanX (@gridColumns);
+    .offsetX (@gridColumns);
+
+  }
+
+  .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) {
+
+    .spanX (@index) when (@index > 0) {
+      (~".span@{index}") { .span(@index); }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    .offsetX (@index) when (@index > 0) {
+      (~'.offset@{index}') { .offset(@index); }
+      (~'.offset@{index}:first-child') { .offsetFirstChild(@index); }
+      .offsetX(@index - 1);
+    }
+    .offsetX (0) {}
+
+    .offset (@columns) {
+      margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2);
+  	  *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%);
+    }
+
+    .offsetFirstChild (@columns) {
+      margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth);
+      *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%);
+    }
+
+    .span (@columns) {
+      width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1));
+      *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%);
+    }
+
+    .row-fluid {
+      width: 100%;
+      .clearfix();
+      [class*="span"] {
+        .input-block-level();
+        float: left;
+        margin-left: @fluidGridGutterWidth;
+        *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%);
+      }
+      [class*="span"]:first-child {
+        margin-left: 0;
+      }
+
+      // generate .spanX and .offsetX
+      .spanX (@gridColumns);
+      .offsetX (@gridColumns);
+    }
+
+  }
+
+  .input(@gridColumnWidth, @gridGutterWidth) {
+
+    .spanX (@index) when (@index > 0) {
+      (~"input.span@{index}, textarea.span@{index}, .uneditable-input.span@{index}") { .span(@index); }
+      .spanX(@index - 1);
+    }
+    .spanX (0) {}
+
+    .span(@columns) {
+      width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14;
+    }
+
+    input,
+    textarea,
+    .uneditable-input {
+      margin-left: 0; // override margin-left from core grid system
+    }
+
+    // Space grid-sized controls properly if multiple per line
+    .controls-row [class*="span"] + [class*="span"] {
+      margin-left: @gridGutterWidth;
+    }
+
+    // generate .spanX
+    .spanX (@gridColumns);
+
+  }
+
+}

+ 98 - 0
static/sora/css/bootstrap/modals.less

@@ -0,0 +1,98 @@
+//
+// Modals
+// --------------------------------------------------
+
+
+// Recalculate z-index where appropriate,
+// but only apply to elements within modal
+.modal-open .modal {
+  .dropdown-menu {  z-index: @zindexDropdown + @zindexModal; }
+  .dropdown.open { *z-index: @zindexDropdown + @zindexModal; }
+  .popover       {  z-index: @zindexPopover  + @zindexModal; }
+  .tooltip       {  z-index: @zindexTooltip  + @zindexModal; }
+}
+
+// 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: 50%;
+  left: 50%;
+  z-index: @zindexModal;
+  overflow: auto;
+  width: 560px;
+  margin: -250px 0 0 -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);
+  &.fade {
+    .transition(e('opacity .3s linear, top .3s ease-out'));
+    top: -25%;
+  }
+  &.fade.in { top: 50%; }
+}
+.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 {
+  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;
+  }
+}

+ 475 - 0
static/sora/css/bootstrap/navbar.less

@@ -0,0 +1,475 @@
+//
+// Navbars (Redux)
+// --------------------------------------------------
+
+
+// COMMON STYLES
+// -------------
+
+// Base class and wrapper
+.navbar {
+  overflow: visible;
+  margin-bottom: @baseLineHeight;
+  color: @navbarText;
+
+  // 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(4px);
+  .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;
+}
+
+
+// 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 {
+    text-decoration: none;
+  }
+}
+
+// Plain text in topbar
+// -------------------------
+.navbar-text {
+  margin-bottom: 0;
+  line-height: @navbarHeight;
+}
+
+// Janky solution for now to account for links outside the .nav
+// -------------------------
+.navbar-link {
+  color: @navbarLinkColor;
+  &:hover {
+    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 {
+  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: 6px;
+    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
+  }
+}
+
+
+
+// Static navbar
+// -------------------------
+
+.navbar-static-top {
+  position: static;
+  width: 100%;
+  margin-bottom: 0; // remove 18px margin for default navbar
+  .navbar-inner {
+    .border-radius(0);
+  }
+}
+
+
+
+// Fixed navbar
+// -------------------------
+
+// Shared (top/bottom) styles
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: @zindexFixedNavbar;
+  margin-bottom: 0; // remove 18px margin for default navbar
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+  border-width: 0 0 1px;
+}
+.navbar-fixed-bottom .navbar-inner {
+  border-width: 1px 0 0;
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+  padding-left:  0;
+  padding-right: 0;
+  .border-radius(0);
+}
+
+// Reset container width
+// Required here as we reset the width earlier on and the grid mixins don't override early enough
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+  #grid > .core > .span(@gridColumns);
+}
+
+// Fixed to top
+.navbar-fixed-top {
+  top: 0;
+}
+.navbar-fixed-top,
+.navbar-static-top {
+  .navbar-inner {
+    .box-shadow(inset 0 -1px 0 rgba(0,0,0,.1), 0 1px 10px rgba(0,0,0,.1));
+  }
+}
+
+// Fixed to bottom
+.navbar-fixed-bottom {
+  bottom: 0;
+  .navbar-inner {
+    .box-shadow(inset 0 1px 0 rgba(0,0,0,.1), 0 -1px 10px rgba(0,0,0,.1));
+  }
+}
+
+
+
+// NAVIGATION
+// ----------
+
+.navbar .nav {
+  position: relative;
+  left: 0;
+  display: block;
+  float: left;
+  margin: 0 10px 0 0;
+}
+.navbar .nav.pull-right {
+  float: right; // redeclare due to specificity
+  margin-right: 0; // remove margin on float right nav
+}
+.navbar .nav > li {
+  float: left;
+}
+
+// Links
+.navbar .nav > li > a {
+  float: none;
+  // Vertically center the text given @navbarHeight
+  padding: ((@navbarHeight - @baseLineHeight) / 2) 15px ((@navbarHeight - @baseLineHeight) / 2);
+  color: @navbarLinkColor;
+  text-decoration: none;
+  text-shadow: 0 1px 0 @navbarBackgroundHighlight;
+}
+.navbar .nav .dropdown-toggle .caret {
+  margin-top: 8px;
+}
+
+// Hover
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+  background-color: @navbarLinkBackgroundHover; // "transparent" is default to differentiate :hover from .active
+  color: @navbarLinkColorHover;
+  text-decoration: none;
+}
+
+// Active nav items
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+  color: @navbarLinkColorActive;
+  text-decoration: none;
+  background-color: @navbarLinkBackgroundActive;
+  .box-shadow(inset 0 3px 8px rgba(0,0,0,.125));
+}
+
+// Navbar button for toggling navbar items in responsive layouts
+// These definitions need to come after '.navbar .btn'
+.navbar .btn-navbar {
+  display: none;
+  float: right;
+  padding: 7px 10px;
+  margin-left: 5px;
+  margin-right: 5px;
+  .buttonBackground(darken(@navbarBackgroundHighlight, 5%), darken(@navbarBackground, 5%));
+  .box-shadow(inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075));
+}
+.navbar .btn-navbar .icon-bar {
+  display: block;
+  width: 18px;
+  height: 2px;
+  background-color: #f5f5f5;
+  .border-radius(1px);
+  .box-shadow(0 1px 0 rgba(0,0,0,.25));
+}
+.btn-navbar .icon-bar + .icon-bar {
+  margin-top: 3px;
+}
+
+
+
+// Dropdown menus
+// --------------
+
+// Menu position and menu carets
+.navbar .nav > li > .dropdown-menu {
+  &:before {
+    content: '';
+    display: inline-block;
+    border-left:   7px solid transparent;
+    border-right:  7px solid transparent;
+    border-bottom: 7px solid #ccc;
+    border-bottom-color: @dropdownBorder;
+    position: absolute;
+    top: -7px;
+    left: 9px;
+  }
+  &:after {
+    content: '';
+    display: inline-block;
+    border-left:   6px solid transparent;
+    border-right:  6px solid transparent;
+    border-bottom: 6px solid @dropdownBackground;
+    position: absolute;
+    top: -6px;
+    left: 10px;
+  }
+}
+// Menu position and menu caret support for dropups via extra dropup class
+.navbar-fixed-bottom .nav > li > .dropdown-menu {
+  &:before {
+    border-top: 7px solid #ccc;
+    border-top-color: @dropdownBorder;
+    border-bottom: 0;
+    bottom: -7px;
+    top: auto;
+  }
+  &:after {
+    border-top: 6px solid @dropdownBackground;
+    border-bottom: 0;
+    bottom: -6px;
+    top: auto;
+  }
+}
+
+// Remove background color from open dropdown
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+  background-color: @navbarLinkBackgroundActive;
+  color: @navbarLinkColorActive;
+}
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+  border-top-color: @navbarLinkColor;
+  border-bottom-color: @navbarLinkColor;
+}
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+  border-top-color: @navbarLinkColorActive;
+  border-bottom-color: @navbarLinkColorActive;
+}
+
+// Right aligned menus need alt position
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+  left: auto;
+  right: 0;
+  &:before {
+    left: auto;
+    right: 12px;
+  }
+  &:after {
+    left: auto;
+    right: 13px;
+  }
+  .dropdown-menu {
+    left: auto;
+    right: 100%;
+    margin-left: 0;
+    margin-right: -1px;
+    .border-radius(6px 0 6px 6px);
+  }
+}
+
+
+// Inverted navbar
+// -------------------------
+
+.navbar-inverse {
+  color: @navbarInverseText;
+
+  .navbar-inner {
+    #gradient > .vertical(@navbarInverseBackgroundHighlight, @navbarInverseBackground);
+    border-color: @navbarInverseBorder;
+  }
+
+  .brand,
+  .nav > li > a {
+    color: @navbarInverseLinkColor;
+    text-shadow: 0 -1px 0 rgba(0,0,0,.25);
+    &:hover {
+      color: @navbarInverseLinkColorHover;
+    }
+  }
+
+  .nav > li > a:focus,
+  .nav > li > a:hover {
+    background-color: @navbarInverseLinkBackgroundHover;
+    color: @navbarInverseLinkColorHover;
+  }
+
+  .nav .active > a,
+  .nav .active > a:hover,
+  .nav .active > a:focus {
+    color: @navbarInverseLinkColorActive;
+    background-color: @navbarInverseLinkBackgroundActive;
+  }
+
+  // Inline text links
+  .navbar-link {
+    color: @navbarInverseLinkColor;
+    &:hover {
+      color: @navbarInverseLinkColorHover;
+    }
+  }
+
+  // Dividers in navbar
+  .divider-vertical {
+    border-left-color: @navbarInverseBackground;
+    border-right-color: @navbarInverseBackgroundHighlight;
+  }
+
+  // Dropdowns
+  .nav li.dropdown.open > .dropdown-toggle,
+  .nav li.dropdown.active > .dropdown-toggle,
+  .nav li.dropdown.open.active > .dropdown-toggle {
+    background-color: @navbarInverseLinkBackgroundActive;
+    color: @navbarInverseLinkColorActive;
+  }
+  .nav li.dropdown > .dropdown-toggle .caret {
+    border-top-color: @navbarInverseLinkColor;
+    border-bottom-color: @navbarInverseLinkColor;
+  }
+  .nav li.dropdown.open > .dropdown-toggle .caret,
+  .nav li.dropdown.active > .dropdown-toggle .caret,
+  .nav li.dropdown.open.active > .dropdown-toggle .caret {
+    border-top-color: @navbarInverseLinkColorActive;
+    border-bottom-color: @navbarInverseLinkColorActive;
+  }
+
+  // Navbar search
+  .navbar-search {
+    .search-query {
+      color: @white;
+      background-color: @navbarInverseSearchBackground;
+      border-color: @navbarInverseSearchBorder;
+      .box-shadow(inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15));
+      .transition(none);
+      .placeholder(@navbarInverseSearchPlaceholderColor);
+
+      // Focus states (we use .focused since IE7-8 and down doesn't support :focus)
+      &:focus,
+      &.focused {
+        padding: 5px 15px;
+        color: @grayDark;
+        text-shadow: 0 1px 0 @white;
+        background-color: @navbarInverseSearchBackgroundFocus;
+        border: 0;
+        .box-shadow(0 0 3px rgba(0,0,0,.15));
+        outline: 0;
+      }
+    }
+  }
+
+  // Navbar collapse button
+  .btn-navbar {
+    .buttonBackground(darken(@navbarInverseBackgroundHighlight, 5%), darken(@navbarInverseBackground, 5%));
+  }
+
+}
+
+
+

+ 384 - 0
static/sora/css/bootstrap/navs.less

@@ -0,0 +1,384 @@
+//
+// Navs
+// --------------------------------------------------
+
+
+// BASE CLASS
+// ----------
+
+.nav {
+  margin-left: 0;
+  margin-bottom: @baseLineHeight;
+  list-style: none;
+}
+
+// Make links block level
+.nav > li > a {
+  display: block;
+}
+.nav > li > a:hover {
+  text-decoration: none;
+  background-color: @grayLighter;
+}
+
+// Redeclare pull classes because of specifity
+.nav > .pull-right {
+  float: right;
+}
+
+// Nav headers (for dropdowns and lists)
+.nav-header {
+  display: block;
+  padding: 3px 15px;
+  font-size: 11px;
+  font-weight: bold;
+  line-height: @baseLineHeight;
+  color: @grayLight;
+  text-shadow: 0 1px 0 rgba(255,255,255,.5);
+  text-transform: uppercase;
+}
+// Space them out when they follow another list item (link)
+.nav li + .nav-header {
+  margin-top: 9px;
+}
+
+
+
+// NAV LIST
+// --------
+
+.nav-list {
+  padding-left: 15px;
+  padding-right: 15px;
+  margin-bottom: 0;
+}
+.nav-list > li > a,
+.nav-list .nav-header {
+  margin-left:  -15px;
+  margin-right: -15px;
+  text-shadow: 0 1px 0 rgba(255,255,255,.5);
+}
+.nav-list > li > a {
+  padding: 3px 15px;
+}
+.nav-list > .active > a,
+.nav-list > .active > a:hover {
+  color: @white;
+  text-shadow: 0 -1px 0 rgba(0,0,0,.2);
+  background-color: @linkColor;
+}
+.nav-list [class^="icon-"] {
+  margin-right: 2px;
+}
+// Dividers (basically an hr) within the dropdown
+.nav-list .divider {
+  .nav-divider();
+}
+
+
+
+// TABS AND PILLS
+// -------------
+
+// Common styles
+.nav-tabs,
+.nav-pills {
+  .clearfix();
+}
+.nav-tabs > li,
+.nav-pills > li {
+  float: left;
+}
+.nav-tabs > li > a,
+.nav-pills > li > a {
+  padding-right: 12px;
+  padding-left: 12px;
+  margin-right: 2px;
+  line-height: 14px; // keeps the overall height an even number
+}
+
+// TABS
+// ----
+
+// Give the tabs something to sit on
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+// Make the list-items overlay the bottom border
+.nav-tabs > li {
+  margin-bottom: -1px;
+}
+// Actual tabs (as links)
+.nav-tabs > li > a {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  line-height: @baseLineHeight;
+  border: 1px solid transparent;
+  .border-radius(4px 4px 0 0);
+  &:hover {
+    border-color: @grayLighter @grayLighter #ddd;
+  }
+}
+// Active state, and it's :hover to override normal :hover
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover {
+  color: @gray;
+  background-color: @white;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+  cursor: default;
+}
+
+
+// PILLS
+// -----
+
+// Links rendered as pills
+.nav-pills > li > a {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  .border-radius(5px);
+}
+
+// Active state
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover {
+  color: @white;
+  background-color: @linkColor;
+}
+
+
+
+// STACKED NAV
+// -----------
+
+// Stacked tabs and pills
+.nav-stacked > li {
+  float: none;
+}
+.nav-stacked > li > a {
+  margin-right: 0; // no need for the gap between nav items
+}
+
+// Tabs
+.nav-tabs.nav-stacked {
+  border-bottom: 0;
+}
+.nav-tabs.nav-stacked > li > a {
+  border: 1px solid #ddd;
+  .border-radius(0);
+}
+.nav-tabs.nav-stacked > li:first-child > a {
+  .border-top-radius(4px);
+}
+.nav-tabs.nav-stacked > li:last-child > a {
+  .border-bottom-radius(4px);
+}
+.nav-tabs.nav-stacked > li > a:hover {
+  border-color: #ddd;
+  z-index: 2;
+}
+
+// Pills
+.nav-pills.nav-stacked > li > a {
+  margin-bottom: 3px;
+}
+.nav-pills.nav-stacked > li:last-child > a {
+  margin-bottom: 1px; // decrease margin to match sizing of stacked tabs
+}
+
+
+
+// DROPDOWNS
+// ---------
+
+.nav-tabs .dropdown-menu {
+  .border-radius(0 0 6px 6px); // remove the top rounded corners here since there is a hard edge above the menu
+}
+.nav-pills .dropdown-menu {
+  .border-radius(6px); // make rounded corners match the pills
+}
+
+// Default dropdown links
+// -------------------------
+// Make carets use linkColor to start
+.nav .dropdown-toggle .caret {
+  border-top-color: @linkColor;
+  border-bottom-color: @linkColor;
+  margin-top: 6px;
+}
+.nav .dropdown-toggle:hover .caret {
+  border-top-color: @linkColorHover;
+  border-bottom-color: @linkColorHover;
+}
+/* move down carets for tabs */
+.nav-tabs .dropdown-toggle .caret {
+  margin-top: 8px;
+}
+
+// Active dropdown links
+// -------------------------
+.nav .active .dropdown-toggle .caret {
+  border-top-color: #fff;
+  border-bottom-color: #fff;
+}
+.nav-tabs .active .dropdown-toggle .caret {
+  border-top-color: @gray;
+  border-bottom-color: @gray;
+}
+
+// Active:hover dropdown links
+// -------------------------
+.nav > .dropdown.active > a:hover {
+  cursor: pointer;
+}
+
+// Open dropdowns
+// -------------------------
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+  color: @white;
+  background-color: @grayLight;
+  border-color: @grayLight;
+}
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret {
+  border-top-color: @white;
+  border-bottom-color: @white;
+  .opacity(100);
+}
+
+// Dropdowns in stacked tabs
+.tabs-stacked .open > a:hover {
+  border-color: @grayLight;
+}
+
+
+
+// TABBABLE
+// --------
+
+
+// COMMON STYLES
+// -------------
+
+// Clear any floats
+.tabbable {
+  .clearfix();
+}
+.tab-content {
+  overflow: auto; // prevent content from running below tabs
+}
+
+// Remove border on bottom, left, right
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+  border-bottom: 0;
+}
+
+// Show/hide tabbable areas
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+  display: none;
+}
+.tab-content > .active,
+.pill-content > .active {
+  display: block;
+}
+
+
+// BOTTOM
+// ------
+
+.tabs-below > .nav-tabs {
+  border-top: 1px solid #ddd;
+}
+.tabs-below > .nav-tabs > li {
+  margin-top: -1px;
+  margin-bottom: 0;
+}
+.tabs-below > .nav-tabs > li > a {
+  .border-radius(0 0 4px 4px);
+  &:hover {
+    border-bottom-color: transparent;
+    border-top-color: #ddd;
+  }
+}
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover {
+  border-color: transparent #ddd #ddd #ddd;
+}
+
+// LEFT & RIGHT
+// ------------
+
+// Common styles
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+  float: none;
+}
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+  min-width: 74px;
+  margin-right: 0;
+  margin-bottom: 3px;
+}
+
+// Tabs on the left
+.tabs-left > .nav-tabs {
+  float: left;
+  margin-right: 19px;
+  border-right: 1px solid #ddd;
+}
+.tabs-left > .nav-tabs > li > a {
+  margin-right: -1px;
+  .border-radius(4px 0 0 4px);
+}
+.tabs-left > .nav-tabs > li > a:hover {
+  border-color: @grayLighter #ddd @grayLighter @grayLighter;
+}
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover {
+  border-color: #ddd transparent #ddd #ddd;
+  *border-right-color: @white;
+}
+
+// Tabs on the right
+.tabs-right > .nav-tabs {
+  float: right;
+  margin-left: 19px;
+  border-left: 1px solid #ddd;
+}
+.tabs-right > .nav-tabs > li > a {
+  margin-left: -1px;
+  .border-radius(0 4px 4px 0);
+}
+.tabs-right > .nav-tabs > li > a:hover {
+  border-color: @grayLighter @grayLighter @grayLighter #ddd;
+}
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover {
+  border-color: #ddd #ddd #ddd transparent;
+  *border-left-color: @white;
+}
+
+
+
+// DISABLED STATES
+// ---------------
+
+// Gray out text
+.nav > .disabled > a {
+  color: @grayLight;
+}
+// Nuke hover effects
+.nav > .disabled > a:hover {
+  text-decoration: none;
+  background-color: transparent;
+  cursor: default;
+}

+ 40 - 0
static/sora/css/bootstrap/pager.less

@@ -0,0 +1,40 @@
+//
+// Pager pagination
+// --------------------------------------------------
+
+
+.pager {
+  margin: @baseLineHeight 0;
+  list-style: none;
+  text-align: center;
+  .clearfix();
+}
+.pager li {
+  display: inline;
+}
+.pager a,
+.pager span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  .border-radius(15px);
+}
+.pager a:hover {
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.pager .next a,
+.pager .next span {
+  float: right;
+}
+.pager .previous a {
+  float: left;
+}
+.pager .disabled a,
+.pager .disabled a:hover,
+.pager .disabled span {
+  color: @grayLight;
+  background-color: #fff;
+  cursor: default;
+}

+ 64 - 0
static/sora/css/bootstrap/pagination.less

@@ -0,0 +1,64 @@
+//
+// Pagination (multiple pages)
+// --------------------------------------------------
+
+
+.pagination {
+  height: @baseLineHeight * 2;
+  margin: @baseLineHeight 0;
+ }
+.pagination ul {
+  display: inline-block;
+  .ie7-inline-block();
+  margin-left: 0;
+  margin-bottom: 0;
+  .border-radius(3px);
+  .box-shadow(0 1px 2px rgba(0,0,0,.05));
+}
+.pagination ul > li {
+  display: inline;
+}
+.pagination ul > li > a,
+.pagination ul > li > span {
+  float: left;
+  padding: 0 14px;
+  line-height: (@baseLineHeight * 2) - 2;
+  text-decoration: none;
+  background-color: @paginationBackground;
+  border: 1px solid @paginationBorder;
+  border-left-width: 0;
+}
+.pagination ul > li > a:hover,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  background-color: #f5f5f5;
+}
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  color: @grayLight;
+  cursor: default;
+}
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover {
+  color: @grayLight;
+  background-color: transparent;
+  cursor: default;
+}
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
+  border-left-width: 1px;
+  .border-radius(3px 0 0 3px);
+}
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+  .border-radius(0 3px 3px 0);
+}
+
+// Centered
+.pagination-centered {
+  text-align: center;
+}
+.pagination-right {
+  text-align: right;
+}

+ 117 - 0
static/sora/css/bootstrap/popovers.less

@@ -0,0 +1,117 @@
+//
+// Popovers
+// --------------------------------------------------
+
+
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: @zindexPopover;
+  display: none;
+  width: 236px;
+  padding: 1px;
+  background-color: @popoverBackground;
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0,0,0,.2);
+  .border-radius(6px);
+  .box-shadow(0 5px 10px rgba(0,0,0,.2));
+
+  // Offset the popover to account for the popover arrow
+  &.top     { margin-bottom: 10px; }
+  &.right   { margin-left: 10px; }
+  &.bottom  { margin-top: 10px; }
+  &.left    { margin-right: 10px; }
+
+}
+
+.popover-title {
+  margin: 0; // reset heading margin
+  padding: 8px 14px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 18px;
+  background-color: @popoverTitleBackground;
+  border-bottom: 1px solid darken(@popoverTitleBackground, 5%);
+  .border-radius(5px 5px 0 0);
+}
+
+.popover-content {
+  padding: 9px 14px;
+  p, ul, ol {
+    margin-bottom: 0;
+  }
+}
+
+// Arrows
+.popover .arrow,
+.popover .arrow:after {
+  position: absolute;
+  display: inline-block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.popover .arrow:after {
+  content: "";
+  z-index: -1;
+}
+
+.popover {
+  &.top .arrow {
+    bottom: -@popoverArrowWidth;
+    left: 50%;
+    margin-left: -@popoverArrowWidth;
+    border-width: @popoverArrowWidth @popoverArrowWidth 0;
+    border-top-color: @popoverArrowColor;
+    &:after {
+      border-width: @popoverArrowOuterWidth @popoverArrowOuterWidth 0;
+      border-top-color: @popoverArrowOuterColor;
+      bottom: -1px;
+      left: -@popoverArrowOuterWidth;
+    }
+  }
+  &.right .arrow {
+    top: 50%;
+    left: -@popoverArrowWidth;
+    margin-top: -@popoverArrowWidth;
+    border-width: @popoverArrowWidth @popoverArrowWidth @popoverArrowWidth 0;
+    border-right-color: @popoverArrowColor;
+    &:after {
+      border-width: @popoverArrowOuterWidth @popoverArrowOuterWidth @popoverArrowOuterWidth 0;
+      border-right-color: @popoverArrowOuterColor;
+      bottom: -@popoverArrowOuterWidth;
+      left: -1px;
+    }
+  }
+  &.bottom .arrow {
+    top: -@popoverArrowWidth;
+    left: 50%;
+    margin-left: -@popoverArrowWidth;
+    border-width: 0 @popoverArrowWidth @popoverArrowWidth;
+    border-bottom-color: @popoverArrowColor;
+    &:after {
+      border-width: 0 @popoverArrowOuterWidth @popoverArrowOuterWidth;
+      border-bottom-color: @popoverArrowOuterColor;
+      top: -1px;
+      left: -@popoverArrowOuterWidth;
+    }
+  }
+  &.left .arrow {
+    top: 50%;
+    right: -@popoverArrowWidth;
+    margin-top: -@popoverArrowWidth;
+    border-width: @popoverArrowWidth 0 @popoverArrowWidth @popoverArrowWidth;
+    border-left-color: @popoverArrowColor;
+    &:after {
+      border-width: @popoverArrowOuterWidth 0 @popoverArrowOuterWidth @popoverArrowOuterWidth;
+      border-left-color: @popoverArrowOuterColor;
+      bottom: -@popoverArrowOuterWidth;
+      right: -1px;
+    }
+  }
+}

+ 122 - 0
static/sora/css/bootstrap/progress-bars.less

@@ -0,0 +1,122 @@
+//
+// Progress bars
+// --------------------------------------------------
+
+
+// ANIMATIONS
+// ----------
+
+// Webkit
+@-webkit-keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+// Firefox
+@-moz-keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+// IE9
+@-ms-keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+// Opera
+@-o-keyframes progress-bar-stripes {
+  from  { background-position: 0 0; }
+  to    { background-position: 40px 0; }
+}
+
+// Spec
+@keyframes progress-bar-stripes {
+  from  { background-position: 40px 0; }
+  to    { background-position: 0 0; }
+}
+
+
+
+// THE BARS
+// --------
+
+// Outer container
+.progress {
+  overflow: hidden;
+  height: @baseLineHeight;
+  margin-bottom: @baseLineHeight;
+  #gradient > .vertical(#f5f5f5, #f9f9f9);
+  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
+  .border-radius(4px);
+}
+
+// Bar of progress
+.progress .bar {
+  width: 0%;
+  height: 100%;
+  color: @white;
+  float: left;
+  font-size: 12px;
+  text-align: center;
+  text-shadow: 0 -1px 0 rgba(0,0,0,.25);
+  #gradient > .vertical(#149bdf, #0480be);
+  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
+  .box-sizing(border-box);
+  .transition(width .6s ease);
+}
+.progress .bar + .bar {
+  .box-shadow(inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15));
+}
+
+// Striped bars
+.progress-striped .bar {
+  #gradient > .striped(#149bdf);
+  .background-size(40px 40px);
+}
+
+// Call animation for the active one
+.progress.active .bar {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+     -moz-animation: progress-bar-stripes 2s linear infinite;
+      -ms-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+
+
+
+// COLORS
+// ------
+
+// Danger (red)
+.progress-danger .bar, .progress .bar-danger {
+  #gradient > .vertical(#ee5f5b, #c43c35);
+}
+.progress-danger.progress-striped .bar, .progress-striped .bar-danger {
+  #gradient > .striped(#ee5f5b);
+}
+
+// Success (green)
+.progress-success .bar, .progress .bar-success {
+  #gradient > .vertical(#62c462, #57a957);
+}
+.progress-success.progress-striped .bar, .progress-striped .bar-success {
+  #gradient > .striped(#62c462);
+}
+
+// Info (teal)
+.progress-info .bar, .progress .bar-info {
+  #gradient > .vertical(#5bc0de, #339bb9);
+}
+.progress-info.progress-striped .bar, .progress-striped .bar-info {
+  #gradient > .striped(#5bc0de);
+}
+
+// Warning (orange)
+.progress-warning .bar, .progress .bar-warning {
+  #gradient > .vertical(lighten(@orange, 15%), @orange);
+}
+.progress-warning.progress-striped .bar, .progress-striped .bar-warning {
+  #gradient > .striped(lighten(@orange, 15%));
+}

+ 137 - 0
static/sora/css/bootstrap/reset.less

@@ -0,0 +1,137 @@
+//
+// Modals
+// Adapted from http://github.com/necolas/normalize.css
+// --------------------------------------------------
+
+
+// Display in IE6-9 and FF3
+// -------------------------
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+  display: block;
+}
+
+// Display block in IE6-9 and FF3
+// -------------------------
+
+audio,
+canvas,
+video {
+  display: inline-block;
+  *display: inline;
+  *zoom: 1;
+}
+
+// Prevents modern browsers from displaying 'audio' without controls
+// -------------------------
+
+audio:not([controls]) {
+    display: none;
+}
+
+// Base settings
+// -------------------------
+
+html {
+  font-size: 100%;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+// Focus states
+a:focus {
+  .tab-focus();
+}
+// Hover & Active
+a:hover,
+a:active {
+  outline: 0;
+}
+
+// Prevents sub and sup affecting line-height in all browsers
+// -------------------------
+
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+sup {
+  top: -0.5em;
+}
+sub {
+  bottom: -0.25em;
+}
+
+// Img border in a's and image quality
+// -------------------------
+
+img {
+  /* Responsive images (ensure images don't scale beyond their parents) */
+  max-width: 100%; /* Part 1: Set a maxium relative to the parent */
+  width: auto\9; /* IE7-8 need help adjusting responsive images */
+  height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */
+
+  vertical-align: middle;
+  border: 0;
+  -ms-interpolation-mode: bicubic;
+}
+
+// Prevent max-width from affecting Google Maps
+#map_canvas img {
+  max-width: none;
+}
+
+// Forms
+// -------------------------
+
+// Font size in all browsers, margin changes, misc consistency
+button,
+input,
+select,
+textarea {
+  margin: 0;
+  font-size: 100%;
+  vertical-align: middle;
+}
+button,
+input {
+  *overflow: visible; // Inner spacing ie IE6/7
+  line-height: normal; // FF3/4 have !important on line-height in UA stylesheet
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
+  padding: 0;
+  border: 0;
+}
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  cursor: pointer; // Cursors on all buttons applied consistently
+  -webkit-appearance: button; // Style clickable inputs in iOS
+}
+input[type="search"] { // Appearance in Safari/Chrome
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+  -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
+}
+textarea {
+  overflow: auto; // Remove vertical scrollbar in IE6-9
+  vertical-align: top; // Readability and alignment cross-browser
+}

+ 28 - 0
static/sora/css/bootstrap/responsive-1200px-min.less

@@ -0,0 +1,28 @@
+//
+// Responsive: Large desktop and up
+// --------------------------------------------------
+
+
+@media (min-width: 1200px) {
+
+  // Fixed grid
+  #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200);
+
+  // Fluid grid
+  #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200);
+
+  // Input grid
+  #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200);
+
+  // Thumbnails
+  .thumbnails {
+    margin-left: -@gridGutterWidth1200;
+  }
+  .thumbnails > li {
+    margin-left: @gridGutterWidth1200;
+  }
+  .row-fluid .thumbnails {
+    margin-left: 0;
+  }
+
+}

+ 174 - 0
static/sora/css/bootstrap/responsive-767px-max.less

@@ -0,0 +1,174 @@
+//
+// Responsive: Landscape phone to desktop/tablet
+// --------------------------------------------------
+
+
+@media (max-width: 767px) {
+
+  // Padding to set content in a bit
+  body {
+    padding-left: 20px;
+    padding-right: 20px;
+  }
+  // Negative indent the now static "fixed" navbar
+  .navbar-fixed-top,
+  .navbar-fixed-bottom,
+  .navbar-static-top {
+    margin-left: -20px;
+    margin-right: -20px;
+  }
+  // Remove padding on container given explicit padding set on body
+  .container-fluid {
+    padding: 0;
+  }
+
+  // TYPOGRAPHY
+  // ----------
+  // Reset horizontal dl
+  .dl-horizontal {
+    dt {
+      float: none;
+      clear: none;
+      width: auto;
+      text-align: left;
+    }
+    dd {
+      margin-left: 0;
+    }
+  }
+
+  // GRID & CONTAINERS
+  // -----------------
+  // Remove width from containers
+  .container {
+    width: auto;
+  }
+  // Fluid rows
+  .row-fluid {
+    width: 100%;
+  }
+  // Undo negative margin on rows and thumbnails
+  .row,
+  .thumbnails {
+    margin-left: 0;
+  }
+  .thumbnails > li {
+    float: none;
+    margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present
+  }
+  // Make all grid-sized elements block level again
+  [class*="span"],
+  .row-fluid [class*="span"] {
+    float: none;
+    display: block;
+    width: 100%;
+    margin-left: 0;
+    .box-sizing(border-box);
+  }
+  .span12,
+  .row-fluid .span12 {
+    width: 100%;
+    .box-sizing(border-box);
+  }
+
+  // FORM FIELDS
+  // -----------
+  // Make span* classes full width
+  .input-large,
+  .input-xlarge,
+  .input-xxlarge,
+  input[class*="span"],
+  select[class*="span"],
+  textarea[class*="span"],
+  .uneditable-input {
+    .input-block-level();
+  }
+  // But don't let it screw up prepend/append inputs
+  .input-prepend input,
+  .input-append input,
+  .input-prepend input[class*="span"],
+  .input-append input[class*="span"] {
+    display: inline-block; // redeclare so they don't wrap to new lines
+    width: auto;
+  }
+  .controls-row [class*="span"] + [class*="span"] {
+    margin-left: 0;
+  }
+
+  // Modals
+  .modal {
+    position: fixed;
+    top:   20px;
+    left:  20px;
+    right: 20px;
+    width: auto;
+    margin: 0;
+    &.fade.in { top: auto; }
+  }
+
+}
+
+
+
+// UP TO LANDSCAPE PHONE
+// ---------------------
+
+@media (max-width: 480px) {
+
+  // Smooth out the collapsing/expanding nav
+  .nav-collapse {
+    -webkit-transform: translate3d(0, 0, 0); // activate the GPU
+  }
+
+  // Block level the page header small tag for readability
+  .page-header h1 small {
+    display: block;
+    line-height: @baseLineHeight;
+  }
+
+  // Update checkboxes for iOS
+  input[type="checkbox"],
+  input[type="radio"] {
+    border: 1px solid #ccc;
+  }
+
+  // Remove the horizontal form styles
+  .form-horizontal {
+    .control-label {
+      float: none;
+      width: auto;
+      padding-top: 0;
+      text-align: left;
+    }
+    // Move over all input controls and content
+    .controls {
+      margin-left: 0;
+    }
+    // Move the options list down to align with labels
+    .control-list {
+      padding-top: 0; // has to be padding because margin collaspes
+    }
+    // Move over buttons in .form-actions to align with .controls
+    .form-actions {
+      padding-left: 10px;
+      padding-right: 10px;
+    }
+  }
+
+  // Modals
+  .modal {
+    top:   10px;
+    left:  10px;
+    right: 10px;
+  }
+  .modal-header .close {
+    padding: 10px;
+    margin: -10px;
+  }
+
+  // Carousel
+  .carousel-caption {
+    position: static;
+  }
+
+}

+ 19 - 0
static/sora/css/bootstrap/responsive-768px-979px.less

@@ -0,0 +1,19 @@
+//
+// Responsive: Tablet to desktop
+// --------------------------------------------------
+
+
+@media (min-width: 768px) and (max-width: 979px) {
+
+  // Fixed grid
+  #grid > .core(@gridColumnWidth768, @gridGutterWidth768);
+
+  // Fluid grid
+  #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768);
+
+  // Input grid
+  #grid > .input(@gridColumnWidth768, @gridGutterWidth768);
+
+  // No need to reset .thumbnails here since it's the same @gridGutterWidth
+
+}

+ 177 - 0
static/sora/css/bootstrap/responsive-navbar.less

@@ -0,0 +1,177 @@
+//
+// Responsive: Navbar
+// --------------------------------------------------
+
+
+// TABLETS AND BELOW
+// -----------------
+@media (max-width: @navbarCollapseWidth) {
+
+  // UNFIX THE TOPBAR
+  // ----------------
+  // Remove any padding from the body
+  body {
+    padding-top: 0;
+  }
+  // Unfix the navbars
+  .navbar-fixed-top,
+  .navbar-fixed-bottom {
+    position: static;
+  }
+  .navbar-fixed-top {
+    margin-bottom: @baseLineHeight;
+  }
+  .navbar-fixed-bottom {
+    margin-top: @baseLineHeight;
+  }
+  .navbar-fixed-top .navbar-inner,
+  .navbar-fixed-bottom .navbar-inner {
+    padding: 5px;
+  }
+  .navbar .container {
+    width: auto;
+    padding: 0;
+  }
+  // Account for brand name
+  .navbar .brand {
+    padding-left: 10px;
+    padding-right: 10px;
+    margin: 0 0 0 -5px;
+  }
+
+  // COLLAPSIBLE NAVBAR
+  // ------------------
+  // Nav collapse clears brand
+  .nav-collapse {
+    clear: both;
+  }
+  // Block-level the nav
+  .nav-collapse .nav {
+    float: none;
+    margin: 0 0 (@baseLineHeight / 2);
+  }
+  .nav-collapse .nav > li {
+    float: none;
+  }
+  .nav-collapse .nav > li > a {
+    margin-bottom: 2px;
+  }
+  .nav-collapse .nav > .divider-vertical {
+    display: none;
+  }
+  .nav-collapse .nav .nav-header {
+    color: @navbarText;
+    text-shadow: none;
+  }
+  // Nav and dropdown links in navbar
+  .nav-collapse .nav > li > a,
+  .nav-collapse .dropdown-menu a {
+    padding: 9px 15px;
+    font-weight: bold;
+    color: @navbarLinkColor;
+    .border-radius(3px);
+  }
+  // Buttons
+  .nav-collapse .btn {
+    padding: 4px 10px 4px;
+    font-weight: normal;
+    .border-radius(4px);
+  }
+  .nav-collapse .dropdown-menu li + li a {
+    margin-bottom: 2px;
+  }
+  .nav-collapse .nav > li > a:hover,
+  .nav-collapse .dropdown-menu a:hover {
+    background-color: @navbarBackground;
+  }
+  .navbar-inverse .nav-collapse .nav > li > a:hover,
+  .navbar-inverse .nav-collapse .dropdown-menu a:hover {
+    background-color: @navbarInverseBackground;
+  }
+  // Buttons in the navbar
+  .nav-collapse.in .btn-group {
+    margin-top: 5px;
+    padding: 0;
+  }
+  // Dropdowns in the navbar
+  .nav-collapse .dropdown-menu {
+    position: static;
+    top: auto;
+    left: auto;
+    float: none;
+    display: block;
+    max-width: none;
+    margin: 0 15px;
+    padding: 0;
+    background-color: transparent;
+    border: none;
+    .border-radius(0);
+    .box-shadow(none);
+  }
+  .nav-collapse .dropdown-menu:before,
+  .nav-collapse .dropdown-menu:after {
+    display: none;
+  }
+  .nav-collapse .dropdown-menu .divider {
+    display: none;
+  }
+  .nav-collapse .nav > li > .dropdown-menu {
+    &:before,
+    &:after {
+      display: none;
+    }
+  }
+  // Forms in navbar
+  .nav-collapse .navbar-form,
+  .nav-collapse .navbar-search {
+    float: none;
+    padding: (@baseLineHeight / 2) 15px;
+    margin: (@baseLineHeight / 2) 0;
+    border-top: 1px solid @navbarBackground;
+    border-bottom: 1px solid @navbarBackground;
+    .box-shadow(inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1));
+  }
+  .navbar-inverse .nav-collapse .navbar-form,
+  .navbar-inverse .nav-collapse .navbar-search {
+    border-top-color: @navbarInverseBackground;
+    border-bottom-color: @navbarInverseBackground;
+  }
+  // Pull right (secondary) nav content
+  .navbar .nav-collapse .nav.pull-right {
+    float: none;
+    margin-left: 0;
+  }
+  // Hide everything in the navbar save .brand and toggle button */
+  .nav-collapse,
+  .nav-collapse.collapse {
+    overflow: hidden;
+    height: 0;
+  }
+  // Navbar button
+  .navbar .btn-navbar {
+    display: block;
+  }
+
+  // STATIC NAVBAR
+  // -------------
+  .navbar-static .navbar-inner {
+    padding-left:  10px;
+    padding-right: 10px;
+  }
+
+
+}
+
+
+// DEFAULT DESKTOP
+// ---------------
+
+@media (min-width: 980px) {
+
+  // Required to make the collapsing navbar work on regular desktops
+  .nav-collapse.collapse {
+    height: auto !important;
+    overflow: visible !important;
+  }
+
+}

+ 43 - 0
static/sora/css/bootstrap/responsive-utilities.less

@@ -0,0 +1,43 @@
+//
+// Responsive: Utility classes
+// --------------------------------------------------
+
+
+// Hide from screenreaders and browsers
+// Credit: HTML5 Boilerplate
+.hidden {
+  display: none;
+  visibility: hidden;
+}
+
+// Visibility utilities
+
+// For desktops
+.visible-phone     { display: none !important; }
+.visible-tablet    { display: none !important; }
+.hidden-phone      { }
+.hidden-tablet     { }
+.hidden-desktop    { display: none !important; }
+.visible-desktop   { display: inherit !important; }
+
+// Tablets & small desktops only
+@media (min-width: 768px) and (max-width: 979px) {
+  // Hide everything else
+  .hidden-desktop    { display: inherit !important; }
+  .visible-desktop   { display: none !important ; }
+  // Show
+  .visible-tablet    { display: inherit !important; }
+  // Hide
+  .hidden-tablet     { display: none !important; }
+}
+
+// Phones only
+@media (max-width: 767px) {
+  // Hide everything else
+  .hidden-desktop    { display: inherit !important; }
+  .visible-desktop   { display: none !important; }
+  // Show
+  .visible-phone     { display: inherit !important; } // Use inherit to restore previous behavior
+  // Hide
+  .hidden-phone      { display: none !important; }
+}

+ 48 - 0
static/sora/css/bootstrap/responsive.less

@@ -0,0 +1,48 @@
+/*!
+ * Bootstrap Responsive v2.1.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+
+// Responsive.less
+// For phone and tablet devices
+// -------------------------------------------------------------
+
+
+// REPEAT VARIABLES & MIXINS
+// -------------------------
+// Required since we compile the responsive stuff separately
+
+@import "variables.less"; // Modify this for custom colors, font-sizes, etc
+@import "mixins.less";
+
+
+// RESPONSIVE CLASSES
+// ------------------
+
+@import "responsive-utilities.less";
+
+
+// MEDIA QUERIES
+// ------------------
+
+// Large desktops
+@import "responsive-1200px-min.less";
+
+// Tablets to regular desktops
+@import "responsive-768px-979px.less";
+
+// Phones to portrait tablets and narrow desktops
+@import "responsive-767px-max.less";
+
+
+// RESPONSIVE NAVBAR
+// ------------------
+
+// From 979px and below, show a button to toggle navbar contents
+@import "responsive-navbar.less";

+ 52 - 0
static/sora/css/bootstrap/scaffolding.less

@@ -0,0 +1,52 @@
+//
+// Scaffolding
+// --------------------------------------------------
+
+
+// Body reset
+// -------------------------
+
+body {
+  margin: 0;
+  font-family: @baseFontFamily;
+  font-size: @baseFontSize;
+  line-height: @baseLineHeight;
+  color: @textColor;
+  background-color: @bodyBackground;
+}
+
+
+// Links
+// -------------------------
+
+a {
+  color: @linkColor;
+  text-decoration: none;
+}
+a:hover {
+  color: @linkColorHover;
+  text-decoration: underline;
+}
+
+
+// Images
+// -------------------------
+
+// Rounded corners
+.img-rounded {
+  .border-radius(6px);
+}
+
+// Add polaroid-esque trim
+.img-polaroid {
+  padding: 4px;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0,0,0,.2);
+  .box-shadow(0 1px 3px rgba(0,0,0,.1));
+}
+
+// Perfect circle
+.img-circle {
+  .border-radius(500px); // crank the border-radius so it works with most reasonably sized images
+}

+ 193 - 0
static/sora/css/bootstrap/sprites.less

@@ -0,0 +1,193 @@
+//
+// Sprites
+// --------------------------------------------------
+
+
+// ICONS
+// -----
+
+// All icons receive the styles of the <i> tag with a base class
+// of .i and are then given a unique class to add width, height,
+// and background-position. Your resulting HTML will look like
+// <i class="icon-inbox"></i>.
+
+// For the white version of the icons, just add the .icon-white class:
+// <i class="icon-inbox icon-white"></i>
+
+[class^="icon-"],
+[class*=" icon-"] {
+  display: inline-block;
+  width: 14px;
+  height: 14px;
+  .ie7-restore-right-whitespace();
+  line-height: 14px;
+  vertical-align: text-top;
+  background-image: url("@{iconSpritePath}");
+  background-position: 14px 14px;
+  background-repeat: no-repeat;
+  margin-top: 1px;
+}
+
+/* White icons with optional class, or on hover/active states of certain elements */
+.icon-white,
+.nav-tabs > .active > a > [class^="icon-"],
+.nav-tabs > .active > a > [class*=" icon-"],
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"] {
+  background-image: url("@{iconWhiteSpritePath}");
+}
+
+.icon-glass              { background-position: 0      0; }
+.icon-music              { background-position: -24px  0; }
+.icon-search             { background-position: -48px  0; }
+.icon-envelope           { background-position: -72px  0; }
+.icon-heart              { background-position: -96px  0; }
+.icon-star               { background-position: -120px 0; }
+.icon-star-empty         { background-position: -144px 0; }
+.icon-user               { background-position: -168px 0; }
+.icon-film               { background-position: -192px 0; }
+.icon-th-large           { background-position: -216px 0; }
+.icon-th                 { background-position: -240px 0; }
+.icon-th-list            { background-position: -264px 0; }
+.icon-ok                 { background-position: -288px 0; }
+.icon-remove             { background-position: -312px 0; }
+.icon-zoom-in            { background-position: -336px 0; }
+.icon-zoom-out           { background-position: -360px 0; }
+.icon-off                { background-position: -384px 0; }
+.icon-signal             { background-position: -408px 0; }
+.icon-cog                { background-position: -432px 0; }
+.icon-trash              { background-position: -456px 0; }
+
+.icon-home               { background-position: 0      -24px; }
+.icon-file               { background-position: -24px  -24px; }
+.icon-time               { background-position: -48px  -24px; }
+.icon-road               { background-position: -72px  -24px; }
+.icon-download-alt       { background-position: -96px  -24px; }
+.icon-download           { background-position: -120px -24px; }
+.icon-upload             { background-position: -144px -24px; }
+.icon-inbox              { background-position: -168px -24px; }
+.icon-play-circle        { background-position: -192px -24px; }
+.icon-repeat             { background-position: -216px -24px; }
+.icon-refresh            { background-position: -240px -24px; }
+.icon-list-alt           { background-position: -264px -24px; }
+.icon-lock               { background-position: -287px -24px; } // 1px off
+.icon-flag               { background-position: -312px -24px; }
+.icon-headphones         { background-position: -336px -24px; }
+.icon-volume-off         { background-position: -360px -24px; }
+.icon-volume-down        { background-position: -384px -24px; }
+.icon-volume-up          { background-position: -408px -24px; }
+.icon-qrcode             { background-position: -432px -24px; }
+.icon-barcode            { background-position: -456px -24px; }
+
+.icon-tag                { background-position: 0      -48px; }
+.icon-tags               { background-position: -25px  -48px; } // 1px off
+.icon-book               { background-position: -48px  -48px; }
+.icon-bookmark           { background-position: -72px  -48px; }
+.icon-print              { background-position: -96px  -48px; }
+.icon-camera             { background-position: -120px -48px; }
+.icon-font               { background-position: -144px -48px; }
+.icon-bold               { background-position: -167px -48px; } // 1px off
+.icon-italic             { background-position: -192px -48px; }
+.icon-text-height        { background-position: -216px -48px; }
+.icon-text-width         { background-position: -240px -48px; }
+.icon-align-left         { background-position: -264px -48px; }
+.icon-align-center       { background-position: -288px -48px; }
+.icon-align-right        { background-position: -312px -48px; }
+.icon-align-justify      { background-position: -336px -48px; }
+.icon-list               { background-position: -360px -48px; }
+.icon-indent-left        { background-position: -384px -48px; }
+.icon-indent-right       { background-position: -408px -48px; }
+.icon-facetime-video     { background-position: -432px -48px; }
+.icon-picture            { background-position: -456px -48px; }
+
+.icon-pencil             { background-position: 0      -72px; }
+.icon-map-marker         { background-position: -24px  -72px; }
+.icon-adjust             { background-position: -48px  -72px; }
+.icon-tint               { background-position: -72px  -72px; }
+.icon-edit               { background-position: -96px  -72px; }
+.icon-share              { background-position: -120px -72px; }
+.icon-check              { background-position: -144px -72px; }
+.icon-move               { background-position: -168px -72px; }
+.icon-step-backward      { background-position: -192px -72px; }
+.icon-fast-backward      { background-position: -216px -72px; }
+.icon-backward           { background-position: -240px -72px; }
+.icon-play               { background-position: -264px -72px; }
+.icon-pause              { background-position: -288px -72px; }
+.icon-stop               { background-position: -312px -72px; }
+.icon-forward            { background-position: -336px -72px; }
+.icon-fast-forward       { background-position: -360px -72px; }
+.icon-step-forward       { background-position: -384px -72px; }
+.icon-eject              { background-position: -408px -72px; }
+.icon-chevron-left       { background-position: -432px -72px; }
+.icon-chevron-right      { background-position: -456px -72px; }
+
+.icon-plus-sign          { background-position: 0      -96px; }
+.icon-minus-sign         { background-position: -24px  -96px; }
+.icon-remove-sign        { background-position: -48px  -96px; }
+.icon-ok-sign            { background-position: -72px  -96px; }
+.icon-question-sign      { background-position: -96px  -96px; }
+.icon-info-sign          { background-position: -120px -96px; }
+.icon-screenshot         { background-position: -144px -96px; }
+.icon-remove-circle      { background-position: -168px -96px; }
+.icon-ok-circle          { background-position: -192px -96px; }
+.icon-ban-circle         { background-position: -216px -96px; }
+.icon-arrow-left         { background-position: -240px -96px; }
+.icon-arrow-right        { background-position: -264px -96px; }
+.icon-arrow-up           { background-position: -289px -96px; } // 1px off
+.icon-arrow-down         { background-position: -312px -96px; }
+.icon-share-alt          { background-position: -336px -96px; }
+.icon-resize-full        { background-position: -360px -96px; }
+.icon-resize-small       { background-position: -384px -96px; }
+.icon-plus               { background-position: -408px -96px; }
+.icon-minus              { background-position: -433px -96px; }
+.icon-asterisk           { background-position: -456px -96px; }
+
+.icon-exclamation-sign   { background-position: 0      -120px; }
+.icon-gift               { background-position: -24px  -120px; }
+.icon-leaf               { background-position: -48px  -120px; }
+.icon-fire               { background-position: -72px  -120px; }
+.icon-eye-open           { background-position: -96px  -120px; }
+.icon-eye-close          { background-position: -120px -120px; }
+.icon-warning-sign       { background-position: -144px -120px; }
+.icon-plane              { background-position: -168px -120px; }
+.icon-calendar           { background-position: -192px -120px; }
+.icon-random             { background-position: -216px -120px; width: 16px; }
+.icon-comment            { background-position: -240px -120px; }
+.icon-magnet             { background-position: -264px -120px; }
+.icon-chevron-up         { background-position: -288px -120px; }
+.icon-chevron-down       { background-position: -313px -119px; } // 1px, 1px off
+.icon-retweet            { background-position: -336px -120px; }
+.icon-shopping-cart      { background-position: -360px -120px; }
+.icon-folder-close       { background-position: -384px -120px; }
+.icon-folder-open        { background-position: -408px -120px; width: 16px; }
+.icon-resize-vertical    { background-position: -432px -119px; } // 1px, 1px off
+.icon-resize-horizontal  { background-position: -456px -118px; } // 1px, 2px off
+
+.icon-hdd                     { background-position: 0      -144px; }
+.icon-bullhorn                { background-position: -24px  -144px; }
+.icon-bell                    { background-position: -48px  -144px; }
+.icon-certificate             { background-position: -72px  -144px; }
+.icon-thumbs-up               { background-position: -96px  -144px; }
+.icon-thumbs-down             { background-position: -120px -144px; }
+.icon-hand-right              { background-position: -144px -144px; }
+.icon-hand-left               { background-position: -168px -144px; }
+.icon-hand-up                 { background-position: -192px -144px; }
+.icon-hand-down               { background-position: -216px -144px; }
+.icon-circle-arrow-right      { background-position: -240px -144px; }
+.icon-circle-arrow-left       { background-position: -264px -144px; }
+.icon-circle-arrow-up         { background-position: -288px -144px; }
+.icon-circle-arrow-down       { background-position: -312px -144px; }
+.icon-globe                   { background-position: -336px -144px; }
+.icon-wrench                  { background-position: -360px -144px; }
+.icon-tasks                   { background-position: -384px -144px; }
+.icon-filter                  { background-position: -408px -144px; }
+.icon-briefcase               { background-position: -432px -144px; }
+.icon-fullscreen              { background-position: -456px -144px; }

+ 245 - 0
static/sora/css/bootstrap/tables.less

@@ -0,0 +1,245 @@
+//
+// Tables
+// --------------------------------------------------
+
+
+// BASE TABLES
+// -----------------
+
+table {
+  max-width: 100%;
+  background-color: @tableBackground;
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+// BASELINE STYLES
+// ---------------
+
+.table {
+  width: 100%;
+  margin-bottom: @baseLineHeight;
+  // Cells
+  th,
+  td {
+    padding: 8px;
+    line-height: @baseLineHeight;
+    text-align: left;
+    vertical-align: top;
+    border-top: 1px solid @tableBorder;
+  }
+  th {
+    font-weight: bold;
+  }
+  // Bottom align for column headings
+  thead th {
+    vertical-align: bottom;
+  }
+  // Remove top border from thead by default
+  caption + thead tr:first-child th,
+  caption + thead tr:first-child td,
+  colgroup + thead tr:first-child th,
+  colgroup + thead tr:first-child td,
+  thead:first-child tr:first-child th,
+  thead:first-child tr:first-child td {
+    border-top: 0;
+  }
+  // Account for multiple tbody instances
+  tbody + tbody {
+    border-top: 2px solid @tableBorder;
+  }
+}
+
+
+
+// CONDENSED TABLE W/ HALF PADDING
+// -------------------------------
+
+.table-condensed {
+  th,
+  td {
+    padding: 4px 5px;
+  }
+}
+
+
+// BORDERED VERSION
+// ----------------
+
+.table-bordered {
+  border: 1px solid @tableBorder;
+  border-collapse: separate; // Done so we can round those corners!
+  *border-collapse: collapse; // IE7 can't round corners anyway
+  border-left: 0;
+  .border-radius(4px);
+  th,
+  td {
+    border-left: 1px solid @tableBorder;
+  }
+  // Prevent a double border
+  caption + thead tr:first-child th,
+  caption + tbody tr:first-child th,
+  caption + tbody tr:first-child td,
+  colgroup + thead tr:first-child th,
+  colgroup + tbody tr:first-child th,
+  colgroup + tbody tr:first-child td,
+  thead:first-child tr:first-child th,
+  tbody:first-child tr:first-child th,
+  tbody:first-child tr:first-child td {
+    border-top: 0;
+  }
+  // For first th or td in the first row in the first thead or tbody
+  thead:first-child tr:first-child th:first-child,
+  tbody:first-child tr:first-child td:first-child {
+    -webkit-border-top-left-radius: 4px;
+            border-top-left-radius: 4px;
+        -moz-border-radius-topleft: 4px;
+  }
+  thead:first-child tr:first-child th:last-child,
+  tbody:first-child tr:first-child td:last-child {
+    -webkit-border-top-right-radius: 4px;
+            border-top-right-radius: 4px;
+        -moz-border-radius-topright: 4px;
+  }
+  // For first th or td in the first row in the first thead or tbody
+  thead:last-child tr:last-child th:first-child,
+  tbody:last-child tr:last-child td:first-child,
+  tfoot:last-child tr:last-child td:first-child {
+    .border-radius(0 0 0 4px);
+    -webkit-border-bottom-left-radius: 4px;
+            border-bottom-left-radius: 4px;
+        -moz-border-radius-bottomleft: 4px;
+  }
+  thead:last-child tr:last-child th:last-child,
+  tbody:last-child tr:last-child td:last-child,
+  tfoot:last-child tr:last-child td:last-child {
+    -webkit-border-bottom-right-radius: 4px;
+            border-bottom-right-radius: 4px;
+        -moz-border-radius-bottomright: 4px;
+  }
+
+  // Special fixes to round the left border on the first td/th
+  caption + thead tr:first-child th:first-child,
+  caption + tbody tr:first-child td:first-child,
+  colgroup + thead tr:first-child th:first-child,
+  colgroup + tbody tr:first-child td:first-child {
+    -webkit-border-top-left-radius: 4px;
+            border-top-left-radius: 4px;
+        -moz-border-radius-topleft: 4px;
+  }
+  caption + thead tr:first-child th:last-child,
+  caption + tbody tr:first-child td:last-child,
+  colgroup + thead tr:first-child th:last-child,
+  colgroup + tbody tr:first-child td:last-child {
+    -webkit-border-top-right-radius: 4px;
+            border-top-right-radius: 4px;
+         -moz-border-radius-topleft: 4px;
+  }
+
+}
+
+
+
+
+// ZEBRA-STRIPING
+// --------------
+
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+.table-striped {
+  tbody {
+    tr:nth-child(odd) td,
+    tr:nth-child(odd) th {
+      background-color: @tableBackgroundAccent;
+    }
+  }
+}
+
+
+// HOVER EFFECT
+// ------------
+// Placed here since it has to come after the potential zebra striping
+.table-hover {
+  tbody {
+    tr:hover td,
+    tr:hover th {
+      background-color: @tableBackgroundHover;
+    }
+  }
+}
+
+
+// TABLE CELL SIZING
+// -----------------
+
+// Reset default grid behavior
+table [class*=span],
+.row-fluid table [class*=span] {
+  display: table-cell;
+  float: none; // undo default grid column styles
+  margin-left: 0; // undo default grid column styles
+}
+
+// Change the column widths to account for td/th padding
+.table {
+  .span1     { .tableColumns(1); }
+  .span2     { .tableColumns(2); }
+  .span3     { .tableColumns(3); }
+  .span4     { .tableColumns(4); }
+  .span5     { .tableColumns(5); }
+  .span6     { .tableColumns(6); }
+  .span7     { .tableColumns(7); }
+  .span8     { .tableColumns(8); }
+  .span9     { .tableColumns(9); }
+  .span10    { .tableColumns(10); }
+  .span11    { .tableColumns(11); }
+  .span12    { .tableColumns(12); }
+  .span13    { .tableColumns(13); }
+  .span14    { .tableColumns(14); }
+  .span15    { .tableColumns(15); }
+  .span16    { .tableColumns(16); }
+  .span17    { .tableColumns(17); }
+  .span18    { .tableColumns(18); }
+  .span19    { .tableColumns(19); }
+  .span20    { .tableColumns(20); }
+  .span21    { .tableColumns(21); }
+  .span22    { .tableColumns(22); }
+  .span23    { .tableColumns(23); }
+  .span24    { .tableColumns(24); }
+}
+
+
+
+// TABLE BACKGROUNDS
+// -----------------
+// Exact selectors below required to override .table-striped
+
+.table tbody tr {
+  &.success td {
+    background-color: @successBackground;
+  }
+  &.error td {
+    background-color: @errorBackground;
+  }
+  &.warning td {
+    background-color: @warningBackground;
+  }
+  &.info td {
+    background-color: @infoBackground;
+  }
+}
+
+// Hover states for .table-hover
+.table-hover tbody tr {
+  &.success:hover td {
+    background-color: darken(@successBackground, 5%);
+  }
+  &.error:hover td {
+    background-color: darken(@errorBackground, 5%);
+  }
+  &.warning:hover td {
+    background-color: darken(@warningBackground, 5%);
+  }
+  &.info:hover td {
+    background-color: darken(@infoBackground, 5%);
+  }
+}

+ 52 - 0
static/sora/css/bootstrap/thumbnails.less

@@ -0,0 +1,52 @@
+//
+// Thumbnails
+// --------------------------------------------------
+
+
+// Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files
+
+// Make wrapper ul behave like the grid
+.thumbnails {
+  margin-left: -@gridGutterWidth;
+  list-style: none;
+  .clearfix();
+}
+// Fluid rows have no left margin
+.row-fluid .thumbnails {
+  margin-left: 0;
+}
+
+// Float li to make thumbnails appear in a row
+.thumbnails > li {
+  float: left; // Explicity set the float since we don't require .span* classes
+  margin-bottom: @baseLineHeight;
+  margin-left: @gridGutterWidth;
+}
+
+// The actual thumbnail (can be `a` or `div`)
+.thumbnail {
+  display: block;
+  padding: 4px;
+  line-height: @baseLineHeight;
+  border: 1px solid #ddd;
+  .border-radius(4px);
+  .box-shadow(0 1px 3px rgba(0,0,0,.055));
+  .transition(all .2s ease-in-out);
+}
+// Add a hover state for linked versions only
+a.thumbnail:hover {
+  border-color: @linkColor;
+  .box-shadow(0 1px 4px rgba(0,105,214,.25));
+}
+
+// Images and captions
+.thumbnail > img {
+  display: block;
+  max-width: 100%;
+  margin-left: auto;
+  margin-right: auto;
+}
+.thumbnail .caption {
+  padding: 9px;
+  color: @gray;
+}

+ 70 - 0
static/sora/css/bootstrap/tooltip.less

@@ -0,0 +1,70 @@
+//
+// Tooltips
+// --------------------------------------------------
+
+
+// Base class
+.tooltip {
+  position: absolute;
+  z-index: @zindexTooltip;
+  display: block;
+  visibility: visible;
+  padding: 5px;
+  font-size: 11px;
+  .opacity(0);
+  &.in     { .opacity(80); }
+  &.top    { margin-top:  -3px; }
+  &.right  { margin-left:  3px; }
+  &.bottom { margin-top:   3px; }
+  &.left   { margin-left: -3px; }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+  max-width: 200px;
+  padding: 3px 8px;
+  color: @tooltipColor;
+  text-align: center;
+  text-decoration: none;
+  background-color: @tooltipBackground;
+  .border-radius(4px);
+}
+
+// Arrows
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.tooltip {
+  &.top .tooltip-arrow {
+    bottom: 0;
+    left: 50%;
+    margin-left: -@tooltipArrowWidth;
+    border-width: @tooltipArrowWidth @tooltipArrowWidth 0;
+    border-top-color: @tooltipArrowColor;
+  }
+  &.right .tooltip-arrow {
+    top: 50%;
+    left: 0;
+    margin-top: -@tooltipArrowWidth;
+    border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0;
+    border-right-color: @tooltipArrowColor;
+  }
+  &.left .tooltip-arrow {
+    top: 50%;
+    right: 0;
+    margin-top: -@tooltipArrowWidth;
+    border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth;
+    border-left-color: @tooltipArrowColor;
+  }
+  &.bottom .tooltip-arrow {
+    top: 0;
+    left: 50%;
+    margin-left: -@tooltipArrowWidth;
+    border-width: 0 @tooltipArrowWidth @tooltipArrowWidth;
+    border-bottom-color: @tooltipArrowColor;
+  }
+}

+ 221 - 0
static/sora/css/bootstrap/type.less

@@ -0,0 +1,221 @@
+//
+// Typography
+// --------------------------------------------------
+
+
+// Body text
+// -------------------------
+
+p {
+  margin: 0 0 @baseLineHeight / 2;
+}
+.lead {
+  margin-bottom: @baseLineHeight;
+  font-size: @baseFontSize * 1.5;
+  font-weight: 200;
+  line-height: @baseLineHeight * 1.5;
+}
+
+
+// Emphasis & misc
+// -------------------------
+
+small {
+  font-size: 85%; // Ex: 14px base font * 85% = about 12px
+}
+strong {
+  font-weight: bold;
+}
+em {
+  font-style: italic;
+}
+cite {
+  font-style: normal;
+}
+
+// Utility classes
+.muted {
+  color: @grayLight;
+}
+.text-warning {
+  color: @warningText;
+}
+.text-error {
+  color: @errorText;
+}
+.text-info {
+  color: @infoText;
+}
+.text-success {
+  color: @successText;
+}
+
+
+// Headings
+// -------------------------
+
+h1, h2, h3, h4, h5, h6 {
+  margin: (@baseLineHeight / 2) 0;
+  font-family: @headingsFontFamily;
+  font-weight: @headingsFontWeight;
+  line-height: 1;
+  color: @headingsColor;
+  text-rendering: optimizelegibility; // Fix the character spacing for headings
+  small {
+    font-weight: normal;
+    line-height: 1;
+    color: @grayLight;
+  }
+}
+h1 { font-size: 36px; line-height: 40px; }
+h2 { font-size: 30px; line-height: 40px; }
+h3 { font-size: 24px; line-height: 40px; }
+h4 { font-size: 18px; line-height: 20px; }
+h5 { font-size: 14px; line-height: 20px; }
+h6 { font-size: 12px; line-height: 20px; }
+
+h1 small { font-size: 24px; }
+h2 small { font-size: 18px; }
+h3 small { font-size: 14px; }
+h4 small { font-size: 14px; }
+
+
+// Page header
+// -------------------------
+
+.page-header {
+  padding-bottom: (@baseLineHeight / 2) - 1;
+  margin: @baseLineHeight 0 (@baseLineHeight * 1.5);
+  border-bottom: 1px solid @grayLighter;
+}
+
+
+
+// Lists
+// --------------------------------------------------
+
+// Unordered and Ordered lists
+ul, ol {
+  padding: 0;
+  margin: 0 0 @baseLineHeight / 2 25px;
+}
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+  margin-bottom: 0;
+}
+li {
+  line-height: @baseLineHeight;
+}
+ul.unstyled,
+ol.unstyled {
+  margin-left: 0;
+  list-style: none;
+}
+
+// Description Lists
+dl {
+  margin-bottom: @baseLineHeight;
+}
+dt,
+dd {
+  line-height: @baseLineHeight;
+}
+dt {
+  font-weight: bold;
+}
+dd {
+  margin-left: @baseLineHeight / 2;
+}
+// Horizontal layout (like forms)
+.dl-horizontal {
+  .clearfix(); // Ensure dl clears floats if empty dd elements present
+  dt {
+    float: left;
+    width: @horizontalComponentOffset - 20;
+    clear: left;
+    text-align: right;
+    .text-overflow();
+  }
+  dd {
+    margin-left: @horizontalComponentOffset;
+  }
+}
+
+// MISC
+// ----
+
+// Horizontal rules
+hr {
+  margin: @baseLineHeight 0;
+  border: 0;
+  border-top: 1px solid @hrBorder;
+  border-bottom: 1px solid @white;
+}
+
+// Abbreviations and acronyms
+abbr[title] {
+  cursor: help;
+  border-bottom: 1px dotted @grayLight;
+}
+abbr.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+
+// Blockquotes
+blockquote {
+  padding: 0 0 0 15px;
+  margin: 0 0 @baseLineHeight;
+  border-left: 5px solid @grayLighter;
+  p {
+    margin-bottom: 0;
+    #font > .shorthand(16px,300,@baseLineHeight * 1.25);
+  }
+  small {
+    display: block;
+    line-height: @baseLineHeight;
+    color: @grayLight;
+    &:before {
+      content: '\2014 \00A0';
+    }
+  }
+
+  // Float right with text-align: right
+  &.pull-right {
+    float: right;
+    padding-right: 15px;
+    padding-left: 0;
+    border-right: 5px solid @grayLighter;
+    border-left: 0;
+    p,
+    small {
+      text-align: right;
+    }
+    small {
+      &:before {
+        content: '';
+      }
+      &:after {
+        content: '\00A0 \2014';
+      }
+    }
+  }
+}
+
+// Quotes
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+  content: "";
+}
+
+// Addresses
+address {
+  display: block;
+  margin-bottom: @baseLineHeight;
+  font-style: normal;
+  line-height: @baseLineHeight;
+}

+ 30 - 0
static/sora/css/bootstrap/utilities.less

@@ -0,0 +1,30 @@
+//
+// Utility classes
+// --------------------------------------------------
+
+
+// Quick floats
+.pull-right {
+  float: right;
+}
+.pull-left {
+  float: left;
+}
+
+// Toggling content
+.hide {
+  display: none;
+}
+.show {
+  display: block;
+}
+
+// Visibility
+.invisible {
+  visibility: hidden;
+}
+
+// For Affix plugin
+.affix {
+  position: fixed;
+}

+ 279 - 0
static/sora/css/bootstrap/variables.less

@@ -0,0 +1,279 @@
+//
+// Variables
+// --------------------------------------------------
+
+
+// Global values
+// --------------------------------------------------
+
+
+// Grays
+// -------------------------
+@black:                 #000;
+@grayDarker:            #222;
+@grayDark:              #333;
+@gray:                  #555;
+@grayLight:             #999;
+@grayLighter:           #eee;
+@white:                 #fff;
+
+
+// Accent colors
+// -------------------------
+@blue:                  #049cdb;
+@blueDark:              #0064cd;
+@green:                 #46a546;
+@red:                   #9d261d;
+@yellow:                #ffc40d;
+@orange:                #f89406;
+@pink:                  #c3325f;
+@purple:                #7a43b6;
+
+
+// Scaffolding
+// -------------------------
+@bodyBackground:        @white;
+@textColor:             @grayDark;
+
+
+// Links
+// -------------------------
+@linkColor:             #08c;
+@linkColorHover:        darken(@linkColor, 15%);
+
+
+// Typography
+// -------------------------
+@sansFontFamily:        "Helvetica Neue", Helvetica, Arial, sans-serif;
+@serifFontFamily:       Georgia, "Times New Roman", Times, serif;
+@monoFontFamily:        Monaco, Menlo, Consolas, "Courier New", monospace;
+
+@baseFontSize:          14px;
+@baseFontFamily:        @sansFontFamily;
+@baseLineHeight:        20px;
+@altFontFamily:         @serifFontFamily;
+
+@headingsFontFamily:    inherit; // empty to use BS default, @baseFontFamily
+@headingsFontWeight:    bold;    // instead of browser default, bold
+@headingsColor:         inherit; // empty to use BS default, @textColor
+
+// Tables
+// -------------------------
+@tableBackground:                   transparent; // overall background-color
+@tableBackgroundAccent:             #f9f9f9; // for striping
+@tableBackgroundHover:              #f5f5f5; // for hover
+@tableBorder:                       #ddd; // table and cell border
+
+// Buttons
+// -------------------------
+@btnBackground:                     @white;
+@btnBackgroundHighlight:            darken(@white, 10%);
+@btnBorder:                         #bbb;
+
+@btnPrimaryBackground:              @linkColor;
+@btnPrimaryBackgroundHighlight:     spin(@btnPrimaryBackground, 20%);
+
+@btnInfoBackground:                 #5bc0de;
+@btnInfoBackgroundHighlight:        #2f96b4;
+
+@btnSuccessBackground:              #62c462;
+@btnSuccessBackgroundHighlight:     #51a351;
+
+@btnWarningBackground:              lighten(@orange, 15%);
+@btnWarningBackgroundHighlight:     @orange;
+
+@btnDangerBackground:               #ee5f5b;
+@btnDangerBackgroundHighlight:      #bd362f;
+
+@btnInverseBackground:              #444;
+@btnInverseBackgroundHighlight:     @grayDarker;
+
+
+// Forms
+// -------------------------
+@inputBackground:               @white;
+@inputBorder:                   #ccc;
+@inputBorderRadius:             3px;
+@inputDisabledBackground:       @grayLighter;
+@formActionsBackground:         #f5f5f5;
+
+// Dropdowns
+// -------------------------
+@dropdownBackground:            @white;
+@dropdownBorder:                rgba(0,0,0,.2);
+@dropdownDividerTop:            #e5e5e5;
+@dropdownDividerBottom:         @white;
+
+@dropdownLinkColor:             @grayDark;
+@dropdownLinkColorHover:        @white;
+@dropdownLinkColorActive:       @dropdownLinkColor;
+
+@dropdownLinkBackgroundActive:  @linkColor;
+@dropdownLinkBackgroundHover:   @dropdownLinkBackgroundActive;
+
+
+
+// COMPONENT VARIABLES
+// --------------------------------------------------
+
+// Z-index master list
+// -------------------------
+// Used for a bird's eye view of components dependent on the z-axis
+// Try to avoid customizing these :)
+@zindexDropdown:          1000;
+@zindexPopover:           1010;
+@zindexTooltip:           1030;
+@zindexFixedNavbar:       1030;
+@zindexModalBackdrop:     1040;
+@zindexModal:             1050;
+
+
+// Sprite icons path
+// -------------------------
+@iconSpritePath:          "../img/glyphicons-halflings.png";
+@iconWhiteSpritePath:     "../img/glyphicons-halflings-white.png";
+
+
+// Input placeholder text color
+// -------------------------
+@placeholderText:         @grayLight;
+
+
+// Hr border color
+// -------------------------
+@hrBorder:                @grayLighter;
+
+
+// Horizontal forms & lists
+// -------------------------
+@horizontalComponentOffset:       180px;
+
+
+// Wells
+// -------------------------
+@wellBackground:                  #f5f5f5;
+
+
+// Navbar
+// -------------------------
+@navbarCollapseWidth:             979px;
+
+@navbarHeight:                    40px;
+@navbarBackgroundHighlight:       #ffffff;
+@navbarBackground:                darken(@navbarBackgroundHighlight, 5%);
+@navbarBorder:                    darken(@navbarBackground, 12%);
+
+@navbarText:                      #777;
+@navbarLinkColor:                 #777;
+@navbarLinkColorHover:            @grayDark;
+@navbarLinkColorActive:           @gray;
+@navbarLinkBackgroundHover:       transparent;
+@navbarLinkBackgroundActive:      darken(@navbarBackground, 5%);
+
+@navbarBrandColor:                @navbarLinkColor;
+
+// Inverted navbar
+@navbarInverseBackground:                #111111;
+@navbarInverseBackgroundHighlight:       #222222;
+@navbarInverseBorder:                    #252525;
+
+@navbarInverseText:                      @grayLight;
+@navbarInverseLinkColor:                 @grayLight;
+@navbarInverseLinkColorHover:            @white;
+@navbarInverseLinkColorActive:           @navbarInverseLinkColorHover;
+@navbarInverseLinkBackgroundHover:       transparent;
+@navbarInverseLinkBackgroundActive:      @navbarInverseBackground;
+
+@navbarInverseSearchBackground:          lighten(@navbarInverseBackground, 25%);
+@navbarInverseSearchBackgroundFocus:     @white;
+@navbarInverseSearchBorder:              @navbarInverseBackground;
+@navbarInverseSearchPlaceholderColor:    #ccc;
+
+@navbarInverseBrandColor:                @navbarInverseLinkColor;
+
+
+// Pagination
+// -------------------------
+@paginationBackground:                #fff;
+@paginationBorder:                    #ddd;
+@paginationActiveBackground:          #f5f5f5;
+
+
+// Hero unit
+// -------------------------
+@heroUnitBackground:              @grayLighter;
+@heroUnitHeadingColor:            inherit;
+@heroUnitLeadColor:               inherit;
+
+
+// Form states and alerts
+// -------------------------
+@warningText:             #c09853;
+@warningBackground:       #fcf8e3;
+@warningBorder:           darken(spin(@warningBackground, -10), 3%);
+
+@errorText:               #b94a48;
+@errorBackground:         #f2dede;
+@errorBorder:             darken(spin(@errorBackground, -10), 3%);
+
+@successText:             #468847;
+@successBackground:       #dff0d8;
+@successBorder:           darken(spin(@successBackground, -10), 5%);
+
+@infoText:                #3a87ad;
+@infoBackground:          #d9edf7;
+@infoBorder:              darken(spin(@infoBackground, -10), 7%);
+
+
+// Tooltips and popovers
+// -------------------------
+@tooltipColor:            #fff;
+@tooltipBackground:       #000;
+@tooltipArrowWidth:       5px;
+@tooltipArrowColor:       @tooltipBackground;
+
+@popoverBackground:       #fff;
+@popoverArrowWidth:       10px;
+@popoverArrowColor:       #fff;
+@popoverTitleBackground:  darken(@popoverBackground, 3%);
+
+// Special enhancement for popovers
+@popoverArrowOuterWidth:  @popoverArrowWidth + 1;
+@popoverArrowOuterColor:  rgba(0,0,0,.25);
+
+
+
+// GRID
+// --------------------------------------------------
+
+
+// Default 940px grid
+// -------------------------
+@gridColumns:             12;
+@gridColumnWidth:         60px;
+@gridGutterWidth:         20px;
+@gridRowWidth:            (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
+
+// 1200px min
+@gridColumnWidth1200:     70px;
+@gridGutterWidth1200:     30px;
+@gridRowWidth1200:        (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
+
+// 768px-979px
+@gridColumnWidth768:      42px;
+@gridGutterWidth768:      20px;
+@gridRowWidth768:         (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
+
+
+// Fluid grid
+// -------------------------
+@fluidGridColumnWidth:    percentage(@gridColumnWidth/@gridRowWidth);
+@fluidGridGutterWidth:    percentage(@gridGutterWidth/@gridRowWidth);
+
+// 1200px min
+@fluidGridColumnWidth1200:     percentage(@gridColumnWidth1200/@gridRowWidth1200);
+@fluidGridGutterWidth1200:     percentage(@gridGutterWidth1200/@gridRowWidth1200);
+
+// 768px-979px
+@fluidGridColumnWidth768:      percentage(@gridColumnWidth768/@gridRowWidth768);
+@fluidGridGutterWidth768:      percentage(@gridGutterWidth768/@gridRowWidth768);

+ 29 - 0
static/sora/css/bootstrap/wells.less

@@ -0,0 +1,29 @@
+//
+// Wells
+// --------------------------------------------------
+
+
+// Base class
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: @wellBackground;
+  border: 1px solid darken(@wellBackground, 7%);
+  .border-radius(4px);
+  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
+  blockquote {
+    border-color: #ddd;
+    border-color: rgba(0,0,0,.15);
+  }
+}
+
+// Sizes
+.well-large {
+  padding: 24px;
+  .border-radius(6px);
+}
+.well-small {
+  padding: 9px;
+  .border-radius(3px);
+}

+ 917 - 0
static/sora/css/sora.css

@@ -0,0 +1,917 @@
+article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
+audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
+audio:not([controls]){display:none;}
+html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
+a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+a:hover,a:active{outline:0;}
+sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
+sup{top:-0.5em;}
+sub{bottom:-0.25em;}
+img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
+#map_canvas img{max-width:none;}
+button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
+button,input{*overflow:visible;line-height:normal;}
+button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
+button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
+input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;}
+input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
+textarea{overflow:auto;vertical-align:top;}
+.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0;}
+.clearfix:after{clear:both;}
+.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
+.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
+body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333333;background-color:#fcfcfc;}
+a{color:#0088cc;text-decoration:none;}
+a:hover{color:#00aaff;text-decoration:underline;}
+.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);}
+.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px;}
+.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;}
+.row:after{clear:both;}
+[class*="span"]{float:left;min-height:1px;margin-left:20px;}
+.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.span12{width:940px;}
+.span11{width:860px;}
+.span10{width:780px;}
+.span9{width:700px;}
+.span8{width:620px;}
+.span7{width:540px;}
+.span6{width:460px;}
+.span5{width:380px;}
+.span4{width:300px;}
+.span3{width:220px;}
+.span2{width:140px;}
+.span1{width:60px;}
+.offset12{margin-left:980px;}
+.offset11{margin-left:900px;}
+.offset10{margin-left:820px;}
+.offset9{margin-left:740px;}
+.offset8{margin-left:660px;}
+.offset7{margin-left:580px;}
+.offset6{margin-left:500px;}
+.offset5{margin-left:420px;}
+.offset4{margin-left:340px;}
+.offset3{margin-left:260px;}
+.offset2{margin-left:180px;}
+.offset1{margin-left:100px;}
+.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;}
+.row-fluid:after{clear:both;}
+.row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;}
+.row-fluid [class*="span"]:first-child{margin-left:0;}
+.row-fluid .span12{width:100%;*width:99.94680851063829%;}
+.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%;}
+.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%;}
+.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%;}
+.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%;}
+.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%;}
+.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%;}
+.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%;}
+.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%;}
+.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%;}
+.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%;}
+.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%;}
+.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%;}
+.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%;}
+.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%;}
+.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%;}
+.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%;}
+.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%;}
+.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%;}
+.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%;}
+.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%;}
+.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%;}
+.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%;}
+.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%;}
+.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%;}
+.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%;}
+.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%;}
+.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%;}
+.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%;}
+.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%;}
+.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%;}
+.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%;}
+.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%;}
+.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%;}
+.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%;}
+.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%;}
+[class*="span"].hide,.row-fluid [class*="span"].hide{display:none;}
+[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right;}
+.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";line-height:0;}
+.container:after{clear:both;}
+.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0;}
+.container-fluid:after{clear:both;}
+p{margin:0 0 10px;}
+.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px;}
+small{font-size:85%;}
+strong{font-weight:bold;}
+em{font-style:italic;}
+cite{font-style:normal;}
+.muted{color:#999999;}
+.text-warning{color:#c09853;}
+.text-error{color:#b94a48;}
+.text-info{color:#3a87ad;}
+.text-success{color:#468847;}
+h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:1;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999999;}
+h1{font-size:36px;line-height:40px;}
+h2{font-size:30px;line-height:40px;}
+h3{font-size:24px;line-height:40px;}
+h4{font-size:18px;line-height:20px;}
+h5{font-size:14px;line-height:20px;}
+h6{font-size:12px;line-height:20px;}
+h1 small{font-size:24px;}
+h2 small{font-size:18px;}
+h3 small{font-size:14px;}
+h4 small{font-size:14px;}
+.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eeeeee;}
+ul,ol{padding:0;margin:0 0 10px 25px;}
+ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
+li{line-height:20px;}
+ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
+dl{margin-bottom:20px;}
+dt,dd{line-height:20px;}
+dt{font-weight:bold;}
+dd{margin-left:10px;}
+.dl-horizontal{*zoom:1;}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0;}
+.dl-horizontal:after{clear:both;}
+.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
+.dl-horizontal dd{margin-left:180px;}
+hr{margin:20px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
+abbr[title]{cursor:help;border-bottom:1px dotted #999999;}
+abbr.initialism{font-size:90%;text-transform:uppercase;}
+blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px;}
+blockquote small{display:block;line-height:20px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
+blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
+blockquote.pull-right small:before{content:'';}
+blockquote.pull-right small:after{content:'\00A0 \2014';}
+q:before,q:after,blockquote:before,blockquote:after{content:"";}
+address{display:block;margin-bottom:20px;font-style:normal;line-height:20px;}
+code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
+pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;}
+pre code{padding:0;color:inherit;background-color:transparent;border:0;}
+.pre-scrollable{max-height:340px;overflow-y:scroll;}
+form{margin:0 0 20px;}
+fieldset{padding:0;margin:0;border:0;}
+legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;}
+label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px;}
+input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
+label{display:block;margin-bottom:5px;}
+select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:9px;font-size:14px;line-height:20px;color:#555555;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+input,textarea,.uneditable-input{width:206px;}
+textarea{height:auto;}
+textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear .2s, box-shadow linear .2s;-moz-transition:border linear .2s, box-shadow linear .2s;-o-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
+input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal;cursor:pointer;}
+input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto;}
+select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px;}
+select{width:220px;border:1px solid #cccccc;background-color:#ffffff;}
+select[multiple],select[size]{height:auto;}
+select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+.uneditable-input,.uneditable-textarea{color:#999999;background-color:#fcfcfc;border-color:#cccccc;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
+.uneditable-input{overflow:hidden;white-space:nowrap;}
+.uneditable-textarea{width:auto;height:auto;}
+input:-moz-placeholder,textarea:-moz-placeholder{color:#999999;}
+input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999999;}
+input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999999;}
+.radio,.checkbox{min-height:18px;padding-left:18px;}
+.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
+.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
+.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
+.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
+.input-mini{width:60px;}
+.input-small{width:90px;}
+.input-medium{width:150px;}
+.input-large{width:210px;}
+.input-xlarge{width:270px;}
+.input-xxlarge{width:530px;}
+input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;}
+.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block;}
+input,textarea,.uneditable-input{margin-left:0;}
+.controls-row [class*="span"]+[class*="span"]{margin-left:20px;}
+input.span12, textarea.span12, .uneditable-input.span12{width:926px;}
+input.span11, textarea.span11, .uneditable-input.span11{width:846px;}
+input.span10, textarea.span10, .uneditable-input.span10{width:766px;}
+input.span9, textarea.span9, .uneditable-input.span9{width:686px;}
+input.span8, textarea.span8, .uneditable-input.span8{width:606px;}
+input.span7, textarea.span7, .uneditable-input.span7{width:526px;}
+input.span6, textarea.span6, .uneditable-input.span6{width:446px;}
+input.span5, textarea.span5, .uneditable-input.span5{width:366px;}
+input.span4, textarea.span4, .uneditable-input.span4{width:286px;}
+input.span3, textarea.span3, .uneditable-input.span3{width:206px;}
+input.span2, textarea.span2, .uneditable-input.span2{width:126px;}
+input.span1, textarea.span1, .uneditable-input.span1{width:46px;}
+.controls-row{*zoom:1;}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0;}
+.controls-row:after{clear:both;}
+.controls-row [class*="span"]{float:left;}
+input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;}
+input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;}
+.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
+.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;}
+.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;}
+.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
+.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
+.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;}
+.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;}
+.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
+.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
+.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;}
+.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;}
+.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
+.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad;}
+.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad;}
+.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;}
+.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad;}
+input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
+.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";line-height:0;}
+.form-actions:after{clear:both;}
+.help-block,.help-inline{color:#595959;}
+.help-block{display:block;margin-bottom:10px;}
+.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
+.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap;}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;font-size:14px;vertical-align:top;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2;}
+.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#eeeeee;border:1px solid #ccc;}
+.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546;}
+.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
+.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-append .add-on,.input-append .btn{margin-left:-1px;}
+.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
+.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
+.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
+.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
+.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;vertical-align:middle;}
+.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
+.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block;}
+.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
+.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
+.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;}
+.control-group{margin-bottom:10px;}
+legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate;}
+.form-horizontal .control-group{margin-bottom:20px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";line-height:0;}
+.form-horizontal .control-group:after{clear:both;}
+.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right;}
+.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0;}.form-horizontal .controls:first-child{*padding-left:180px;}
+.form-horizontal .help-block{margin-bottom:0;}
+.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px;}
+.form-horizontal .form-actions{padding-left:180px;}
+table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}
+.table{width:100%;margin-bottom:20px;}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
+.table th{font-weight:bold;}
+.table thead th{vertical-align:bottom;}
+.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
+.table tbody+tbody{border-top:2px solid #dddddd;}
+.table-condensed th,.table-condensed td{padding:4px 5px;}
+.table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
+.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
+.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;}
+.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px;}
+.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;}
+.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;}
+.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;}
+.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topleft:4px;}
+.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f7f7f7;}
+.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f2f2f2;}
+table [class*=span],.row-fluid table [class*=span]{display:table-cell;float:none;margin-left:0;}
+.table .span1{float:none;width:44px;margin-left:0;}
+.table .span2{float:none;width:124px;margin-left:0;}
+.table .span3{float:none;width:204px;margin-left:0;}
+.table .span4{float:none;width:284px;margin-left:0;}
+.table .span5{float:none;width:364px;margin-left:0;}
+.table .span6{float:none;width:444px;margin-left:0;}
+.table .span7{float:none;width:524px;margin-left:0;}
+.table .span8{float:none;width:604px;margin-left:0;}
+.table .span9{float:none;width:684px;margin-left:0;}
+.table .span10{float:none;width:764px;margin-left:0;}
+.table .span11{float:none;width:844px;margin-left:0;}
+.table .span12{float:none;width:924px;margin-left:0;}
+.table .span13{float:none;width:1004px;margin-left:0;}
+.table .span14{float:none;width:1084px;margin-left:0;}
+.table .span15{float:none;width:1164px;margin-left:0;}
+.table .span16{float:none;width:1244px;margin-left:0;}
+.table .span17{float:none;width:1324px;margin-left:0;}
+.table .span18{float:none;width:1404px;margin-left:0;}
+.table .span19{float:none;width:1484px;margin-left:0;}
+.table .span20{float:none;width:1564px;margin-left:0;}
+.table .span21{float:none;width:1644px;margin-left:0;}
+.table .span22{float:none;width:1724px;margin-left:0;}
+.table .span23{float:none;width:1804px;margin-left:0;}
+.table .span24{float:none;width:1884px;margin-left:0;}
+.table tbody tr.success td{background-color:#dff0d8;}
+.table tbody tr.error td{background-color:#f2dede;}
+.table tbody tr.warning td{background-color:#fcf8e3;}
+.table tbody tr.info td{background-color:#d9edf7;}
+.table-hover tbody tr.success:hover td{background-color:#d0e9c6;}
+.table-hover tbody tr.error:hover td{background-color:#ebcccc;}
+.table-hover tbody tr.warning:hover td{background-color:#faf2cc;}
+.table-hover tbody tr.info:hover td{background-color:#c4e3f3;}
+[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px;}
+.icon-white,.nav-tabs>.active>a>[class^="icon-"],.nav-tabs>.active>a>[class*=" icon-"],.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png");}
+.icon-glass{background-position:0 0;}
+.icon-music{background-position:-24px 0;}
+.icon-search{background-position:-48px 0;}
+.icon-envelope{background-position:-72px 0;}
+.icon-heart{background-position:-96px 0;}
+.icon-star{background-position:-120px 0;}
+.icon-star-empty{background-position:-144px 0;}
+.icon-user{background-position:-168px 0;}
+.icon-film{background-position:-192px 0;}
+.icon-th-large{background-position:-216px 0;}
+.icon-th{background-position:-240px 0;}
+.icon-th-list{background-position:-264px 0;}
+.icon-ok{background-position:-288px 0;}
+.icon-remove{background-position:-312px 0;}
+.icon-zoom-in{background-position:-336px 0;}
+.icon-zoom-out{background-position:-360px 0;}
+.icon-off{background-position:-384px 0;}
+.icon-signal{background-position:-408px 0;}
+.icon-cog{background-position:-432px 0;}
+.icon-trash{background-position:-456px 0;}
+.icon-home{background-position:0 -24px;}
+.icon-file{background-position:-24px -24px;}
+.icon-time{background-position:-48px -24px;}
+.icon-road{background-position:-72px -24px;}
+.icon-download-alt{background-position:-96px -24px;}
+.icon-download{background-position:-120px -24px;}
+.icon-upload{background-position:-144px -24px;}
+.icon-inbox{background-position:-168px -24px;}
+.icon-play-circle{background-position:-192px -24px;}
+.icon-repeat{background-position:-216px -24px;}
+.icon-refresh{background-position:-240px -24px;}
+.icon-list-alt{background-position:-264px -24px;}
+.icon-lock{background-position:-287px -24px;}
+.icon-flag{background-position:-312px -24px;}
+.icon-headphones{background-position:-336px -24px;}
+.icon-volume-off{background-position:-360px -24px;}
+.icon-volume-down{background-position:-384px -24px;}
+.icon-volume-up{background-position:-408px -24px;}
+.icon-qrcode{background-position:-432px -24px;}
+.icon-barcode{background-position:-456px -24px;}
+.icon-tag{background-position:0 -48px;}
+.icon-tags{background-position:-25px -48px;}
+.icon-book{background-position:-48px -48px;}
+.icon-bookmark{background-position:-72px -48px;}
+.icon-print{background-position:-96px -48px;}
+.icon-camera{background-position:-120px -48px;}
+.icon-font{background-position:-144px -48px;}
+.icon-bold{background-position:-167px -48px;}
+.icon-italic{background-position:-192px -48px;}
+.icon-text-height{background-position:-216px -48px;}
+.icon-text-width{background-position:-240px -48px;}
+.icon-align-left{background-position:-264px -48px;}
+.icon-align-center{background-position:-288px -48px;}
+.icon-align-right{background-position:-312px -48px;}
+.icon-align-justify{background-position:-336px -48px;}
+.icon-list{background-position:-360px -48px;}
+.icon-indent-left{background-position:-384px -48px;}
+.icon-indent-right{background-position:-408px -48px;}
+.icon-facetime-video{background-position:-432px -48px;}
+.icon-picture{background-position:-456px -48px;}
+.icon-pencil{background-position:0 -72px;}
+.icon-map-marker{background-position:-24px -72px;}
+.icon-adjust{background-position:-48px -72px;}
+.icon-tint{background-position:-72px -72px;}
+.icon-edit{background-position:-96px -72px;}
+.icon-share{background-position:-120px -72px;}
+.icon-check{background-position:-144px -72px;}
+.icon-move{background-position:-168px -72px;}
+.icon-step-backward{background-position:-192px -72px;}
+.icon-fast-backward{background-position:-216px -72px;}
+.icon-backward{background-position:-240px -72px;}
+.icon-play{background-position:-264px -72px;}
+.icon-pause{background-position:-288px -72px;}
+.icon-stop{background-position:-312px -72px;}
+.icon-forward{background-position:-336px -72px;}
+.icon-fast-forward{background-position:-360px -72px;}
+.icon-step-forward{background-position:-384px -72px;}
+.icon-eject{background-position:-408px -72px;}
+.icon-chevron-left{background-position:-432px -72px;}
+.icon-chevron-right{background-position:-456px -72px;}
+.icon-plus-sign{background-position:0 -96px;}
+.icon-minus-sign{background-position:-24px -96px;}
+.icon-remove-sign{background-position:-48px -96px;}
+.icon-ok-sign{background-position:-72px -96px;}
+.icon-question-sign{background-position:-96px -96px;}
+.icon-info-sign{background-position:-120px -96px;}
+.icon-screenshot{background-position:-144px -96px;}
+.icon-remove-circle{background-position:-168px -96px;}
+.icon-ok-circle{background-position:-192px -96px;}
+.icon-ban-circle{background-position:-216px -96px;}
+.icon-arrow-left{background-position:-240px -96px;}
+.icon-arrow-right{background-position:-264px -96px;}
+.icon-arrow-up{background-position:-289px -96px;}
+.icon-arrow-down{background-position:-312px -96px;}
+.icon-share-alt{background-position:-336px -96px;}
+.icon-resize-full{background-position:-360px -96px;}
+.icon-resize-small{background-position:-384px -96px;}
+.icon-plus{background-position:-408px -96px;}
+.icon-minus{background-position:-433px -96px;}
+.icon-asterisk{background-position:-456px -96px;}
+.icon-exclamation-sign{background-position:0 -120px;}
+.icon-gift{background-position:-24px -120px;}
+.icon-leaf{background-position:-48px -120px;}
+.icon-fire{background-position:-72px -120px;}
+.icon-eye-open{background-position:-96px -120px;}
+.icon-eye-close{background-position:-120px -120px;}
+.icon-warning-sign{background-position:-144px -120px;}
+.icon-plane{background-position:-168px -120px;}
+.icon-calendar{background-position:-192px -120px;}
+.icon-random{background-position:-216px -120px;width:16px;}
+.icon-comment{background-position:-240px -120px;}
+.icon-magnet{background-position:-264px -120px;}
+.icon-chevron-up{background-position:-288px -120px;}
+.icon-chevron-down{background-position:-313px -119px;}
+.icon-retweet{background-position:-336px -120px;}
+.icon-shopping-cart{background-position:-360px -120px;}
+.icon-folder-close{background-position:-384px -120px;}
+.icon-folder-open{background-position:-408px -120px;width:16px;}
+.icon-resize-vertical{background-position:-432px -119px;}
+.icon-resize-horizontal{background-position:-456px -118px;}
+.icon-hdd{background-position:0 -144px;}
+.icon-bullhorn{background-position:-24px -144px;}
+.icon-bell{background-position:-48px -144px;}
+.icon-certificate{background-position:-72px -144px;}
+.icon-thumbs-up{background-position:-96px -144px;}
+.icon-thumbs-down{background-position:-120px -144px;}
+.icon-hand-right{background-position:-144px -144px;}
+.icon-hand-left{background-position:-168px -144px;}
+.icon-hand-up{background-position:-192px -144px;}
+.icon-hand-down{background-position:-216px -144px;}
+.icon-circle-arrow-right{background-position:-240px -144px;}
+.icon-circle-arrow-left{background-position:-264px -144px;}
+.icon-circle-arrow-up{background-position:-288px -144px;}
+.icon-circle-arrow-down{background-position:-312px -144px;}
+.icon-globe{background-position:-336px -144px;}
+.icon-wrench{background-position:-360px -144px;}
+.icon-tasks{background-position:-384px -144px;}
+.icon-filter{background-position:-408px -144px;}
+.icon-briefcase{background-position:-432px -144px;}
+.icon-fullscreen{background-position:-456px -144px;}
+.dropup,.dropdown{position:relative;}
+.dropdown-toggle{*margin-bottom:-3px;}
+.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
+.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";}
+.dropdown .caret{margin-top:8px;margin-left:2px;}
+.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.dropdown-menu.pull-right{right:0;left:auto;}
+.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
+.dropdown-menu a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333333;white-space:nowrap;}
+.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{text-decoration:none;color:#ffffff;background-color:#0088cc;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);}
+.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;outline:0;background-color:#0088cc;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);}
+.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999999;}
+.dropdown-menu .disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;}
+.open{*z-index:1000;}.open >.dropdown-menu{display:block;}
+.pull-right>.dropdown-menu{right:0;left:auto;}
+.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"";}
+.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
+.dropdown-submenu{position:relative;}
+.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
+.dropdown-submenu:hover>.dropdown-menu{display:block;}
+.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:#cccccc;margin-top:5px;margin-right:-10px;}
+.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
+.dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px;}
+.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
+.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}.fade.in{opacity:1;}
+.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}.collapse.in{height:auto;}
+.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);}
+button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;}
+.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 14px;margin-bottom:0;font-size:14px;line-height:20px;*line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333333;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#dedede;background-image:-moz-linear-gradient(top, #dcdcdc, #e1e1e1);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#dcdcdc), to(#e1e1e1));background-image:-webkit-linear-gradient(top, #dcdcdc, #e1e1e1);background-image:-o-linear-gradient(top, #dcdcdc, #e1e1e1);background-image:linear-gradient(to bottom, #dcdcdc, #e1e1e1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdcdcdc', endColorstr='#ffe1e1e1', GradientType=0);border-color:#e1e1e1 #e1e1e1 #bbbbbb;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e1e1e1;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #cccccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333333;background-color:#e1e1e1;*background-color:#d4d4d4;}
+.btn:active,.btn.active{background-color:#c8c8c8 \9;}
+.btn:first-child{*margin-left:0;}
+.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
+.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.btn-large{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.btn-large [class^="icon-"]{margin-top:2px;}
+.btn-small{padding:3px 9px;font-size:12px;line-height:18px;}
+.btn-small [class^="icon-"]{margin-top:0;}
+.btn-mini{padding:2px 6px;font-size:11px;line-height:17px;}
+.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
+.btn-block+.btn-block{margin-top:5px;}
+input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%;}
+.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
+.btn{border-color:#c5c5c5;border-color:rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);}
+.btn-primary{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0093dc;background-image:-moz-linear-gradient(top, #0088cc, #00a3f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#00a3f5));background-image:-webkit-linear-gradient(top, #0088cc, #00a3f5);background-image:-o-linear-gradient(top, #0088cc, #00a3f5);background-image:linear-gradient(to bottom, #0088cc, #00a3f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff00a3f5', GradientType=0);border-color:#00a3f5 #00a3f5 #0070a8;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#00a3f5;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#ffffff;background-color:#00a3f5;*background-color:#0092db;}
+.btn-primary:active,.btn-primary.active{background-color:#0081c2 \9;}
+.btn-warning{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#f39919;background-image:-moz-linear-gradient(top, #ee8e06, #faa937);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee8e06), to(#faa937));background-image:-webkit-linear-gradient(top, #ee8e06, #faa937);background-image:-o-linear-gradient(top, #ee8e06, #faa937);background-image:linear-gradient(to bottom, #ee8e06, #faa937);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee8e06', endColorstr='#fffaa937', GradientType=0);border-color:#faa937 #faa937 #df8505;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#faa937;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#ffffff;background-color:#faa937;*background-color:#fa9f1e;}
+.btn-warning:active,.btn-warning.active{background-color:#f89406 \9;}
+.btn-danger{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#d74439;background-image:-moz-linear-gradient(top, #d13327, #e05f55);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#d13327), to(#e05f55));background-image:-webkit-linear-gradient(top, #d13327, #e05f55);background-image:-o-linear-gradient(top, #d13327, #e05f55);background-image:linear-gradient(to bottom, #d13327, #e05f55);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd13327', endColorstr='#ffe05f55', GradientType=0);border-color:#e05f55 #e05f55 #c42f24;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e05f55;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#ffffff;background-color:#e05f55;*background-color:#dc4a3f;}
+.btn-danger:active,.btn-danger.active{background-color:#d8362a \9;}
+.btn-success{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#54b054;background-image:-moz-linear-gradient(top, #46a546, #69bf69);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#46a546), to(#69bf69));background-image:-webkit-linear-gradient(top, #46a546, #69bf69);background-image:-o-linear-gradient(top, #46a546, #69bf69);background-image:linear-gradient(to bottom, #46a546, #69bf69);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff46a546', endColorstr='#ff69bf69', GradientType=0);border-color:#69bf69 #69bf69 #419a41;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#69bf69;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#ffffff;background-color:#69bf69;*background-color:#57b857;}
+.btn-success:active,.btn-success.active{background-color:#49ac49 \9;}
+.btn-info{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#5d8aa1;background-image:-moz-linear-gradient(top, #507e95, #709cb2);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#507e95), to(#709cb2));background-image:-webkit-linear-gradient(top, #507e95, #709cb2);background-image:-o-linear-gradient(top, #507e95, #709cb2);background-image:linear-gradient(to bottom, #507e95, #709cb2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff507e95', endColorstr='#ff709cb2', GradientType=0);border-color:#709cb2 #709cb2 #4b768b;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#709cb2;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#ffffff;background-color:#709cb2;*background-color:#6091a9;}
+.btn-info:active,.btn-info.active{background-color:#54849c \9;}
+.btn-inverse{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#656565;background-image:-moz-linear-gradient(top, #555555, #7e7e7e);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#7e7e7e));background-image:-webkit-linear-gradient(top, #555555, #7e7e7e);background-image:-o-linear-gradient(top, #555555, #7e7e7e);background-image:linear-gradient(to bottom, #555555, #7e7e7e);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff555555', endColorstr='#ff7e7e7e', GradientType=0);border-color:#7e7e7e #7e7e7e #585858;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#7e7e7e;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#ffffff;background-color:#7e7e7e;*background-color:#717171;}
+.btn-inverse:active,.btn-inverse.active{background-color:#646464 \9;}
+button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
+button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
+button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
+button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
+.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.btn-link{border-color:transparent;cursor:pointer;color:#0088cc;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-link:hover{color:#00aaff;text-decoration:underline;background-color:transparent;}
+.btn-link[disabled]:hover{color:#333333;text-decoration:none;}
+.btn-group{position:relative;font-size:0;vertical-align:middle;white-space:nowrap;*margin-left:.3em;}.btn-group:first-child{*margin-left:0;}
+.btn-group+.btn-group{margin-left:5px;}
+.btn-toolbar{font-size:0;margin-top:10px;margin-bottom:10px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
+.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px;}
+.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-group>.btn+.btn{margin-left:-1px;}
+.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px;}
+.btn-group>.btn-mini{font-size:11px;}
+.btn-group>.btn-small{font-size:12px;}
+.btn-group>.btn-large{font-size:16px;}
+.btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
+.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
+.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
+.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
+.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;}
+.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
+.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 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 .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn-group.open .btn.dropdown-toggle{background-color:#e1e1e1;}
+.btn-group.open .btn-primary.dropdown-toggle{background-color:#00a3f5;}
+.btn-group.open .btn-warning.dropdown-toggle{background-color:#faa937;}
+.btn-group.open .btn-danger.dropdown-toggle{background-color:#e05f55;}
+.btn-group.open .btn-success.dropdown-toggle{background-color:#69bf69;}
+.btn-group.open .btn-info.dropdown-toggle{background-color:#709cb2;}
+.btn-group.open .btn-inverse.dropdown-toggle{background-color:#7e7e7e;}
+.btn .caret{margin-top:8px;margin-left:0;}
+.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px;}
+.btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px;}
+.dropup .btn-large .caret{border-bottom:5px solid #000000;border-top:0;}
+.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.btn-group-vertical{display:inline-block;*display:inline;*zoom:1;}
+.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-group-vertical .btn+.btn{margin-left:0;margin-top:-1px;}
+.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
+.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
+.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;}
+.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
+.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
+.alert h4{margin:0;}
+.alert .close{position:relative;top:-2px;right:-21px;line-height:20px;}
+.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
+.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
+.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
+.alert-block{padding-top:14px;padding-bottom:14px;}
+.alert-block>p,.alert-block>ul{margin-bottom:0;}
+.alert-block p+p{margin-top:5px;}
+.nav{margin-left:0;margin-bottom:20px;list-style:none;}
+.nav>li>a{display:block;}
+.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
+.nav>.pull-right{float:right;}
+.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
+.nav li+.nav-header{margin-top:9px;}
+.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
+.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
+.nav-list>li>a{padding:3px 15px;}
+.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
+.nav-list [class^="icon-"]{margin-right:2px;}
+.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
+.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0;}
+.nav-tabs:after,.nav-pills:after{clear:both;}
+.nav-tabs>li,.nav-pills>li{float:left;}
+.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
+.nav-tabs{border-bottom:1px solid #ddd;}
+.nav-tabs>li{margin-bottom:-1px;}
+.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
+.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
+.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
+.nav-stacked>li{float:none;}
+.nav-stacked>li>a{margin-right:0;}
+.nav-tabs.nav-stacked{border-bottom:0;}
+.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
+.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
+.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
+.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
+.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
+.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
+.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.nav .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
+.nav .dropdown-toggle:hover .caret{border-top-color:#00aaff;border-bottom-color:#00aaff;}
+.nav-tabs .dropdown-toggle .caret{margin-top:8px;}
+.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff;}
+.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;}
+.nav>.dropdown.active>a:hover{cursor:pointer;}
+.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
+.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
+.tabs-stacked .open>a:hover{border-color:#999999;}
+.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0;}
+.tabbable:after{clear:both;}
+.tab-content{overflow:auto;}
+.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;}
+.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
+.tab-content>.active,.pill-content>.active{display:block;}
+.tabs-below>.nav-tabs{border-top:1px solid #ddd;}
+.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;}
+.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below>.nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
+.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;}
+.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;}
+.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
+.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
+.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
+.tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
+.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
+.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
+.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
+.tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
+.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
+.nav>.disabled>a{color:#999999;}
+.nav>.disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;}
+.navbar{overflow:visible;margin-bottom:20px;color:#555555;*position:relative;*z-index:2;}
+.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#ededed;background-image:-moz-linear-gradient(top, #e3e3e3, #fcfcfc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e3e3e3), to(#fcfcfc));background-image:-webkit-linear-gradient(top, #e3e3e3, #fcfcfc);background-image:-o-linear-gradient(top, #e3e3e3, #fcfcfc);background-image:linear-gradient(to bottom, #e3e3e3, #fcfcfc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe3e3e3', endColorstr='#fffcfcfc', GradientType=0);border:1px solid #ffffff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;}
+.navbar-inner:after{clear:both;}
+.navbar .container{width:auto;}
+.nav-collapse.collapse{height:auto;}
+.navbar .brand{float:left;display:block;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#555555;text-shadow:0 1px 0 #e3e3e3;}.navbar .brand:hover{text-decoration:none;}
+.navbar-text{margin-bottom:0;line-height:40px;}
+.navbar-link{color:#555555;}.navbar-link:hover{color:#333333;}
+.navbar .divider-vertical{height:40px;margin:0 9px;border-left:1px solid #fcfcfc;border-right:1px solid #e3e3e3;}
+.navbar .btn,.navbar .btn-group{margin-top:5px;}
+.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0;}
+.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0;}
+.navbar-form:after{clear:both;}
+.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
+.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0;}
+.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
+.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
+.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0;}.navbar-search .search-query{margin-bottom:0;padding:4px 14px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.navbar-static-top{position:static;width:100%;margin-bottom:0;}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
+.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px;}
+.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0;}
+.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.navbar-fixed-top{top:0;}
+.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);}
+.navbar-fixed-bottom{bottom:0;}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);}
+.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
+.navbar .nav.pull-right{float:right;margin-right:0;}
+.navbar .nav>li{float:left;}
+.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#555555;text-decoration:none;text-shadow:0 1px 0 #e3e3e3;}
+.navbar .nav .dropdown-toggle .caret{margin-top:8px;}
+.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:#f0f0f0;color:#333333;text-decoration:none;}
+.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555555;text-decoration:none;background-color:#fcfcfc;-webkit-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);-moz-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);}
+.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#e0e0e0;background-image:-moz-linear-gradient(top, #d6d6d6, #f0f0f0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#d6d6d6), to(#f0f0f0));background-image:-webkit-linear-gradient(top, #d6d6d6, #f0f0f0);background-image:-o-linear-gradient(top, #d6d6d6, #f0f0f0);background-image:linear-gradient(to bottom, #d6d6d6, #f0f0f0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd6d6d6', endColorstr='#fff0f0f0', GradientType=0);border-color:#f0f0f0 #f0f0f0 #c9c9c9;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#f0f0f0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#ffffff;background-color:#f0f0f0;*background-color:#e3e3e3;}
+.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#d6d6d6 \9;}
+.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
+.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
+.navbar .nav>li>.dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
+.navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
+.navbar-fixed-bottom .nav>li>.dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
+.navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
+.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:#fcfcfc;color:#555555;}
+.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;}
+.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;}
+.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{left:auto;right:0;}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{left:auto;right:12px;}
+.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px;}
+.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{left:auto;right:100%;margin-left:0;margin-right:-1px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
+.navbar-inverse{color:#ffffff;}.navbar-inverse .navbar-inner{background-color:#0fafff;background-image:-moz-linear-gradient(top, #1ab2ff, #00aaff);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#1ab2ff), to(#00aaff));background-image:-webkit-linear-gradient(top, #1ab2ff, #00aaff);background-image:-o-linear-gradient(top, #1ab2ff, #00aaff);background-image:linear-gradient(to bottom, #1ab2ff, #00aaff);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1ab2ff', endColorstr='#ff00aaff', GradientType=0);border-color:#0fafff;}
+.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#ffffff;}
+.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:#ffffff;color:#ffffff;}
+.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#ffffff;background-color:#00aaff;}
+.navbar-inverse .navbar-link{color:#ffffff;}.navbar-inverse .navbar-link:hover{color:#ffffff;}
+.navbar-inverse .divider-vertical{border-left-color:#00aaff;border-right-color:#1ab2ff;}
+.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{background-color:#00aaff;color:#ffffff;}
+.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.navbar-inverse .navbar-search .search-query{color:#ffffff;background-color:#80d4ff;border-color:#00aaff;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none;}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#cccccc;}
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#cccccc;}
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
+.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
+.navbar-inverse .btn-navbar{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#00a3f5;background-image:-moz-linear-gradient(top, #00aaff, #0099e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#00aaff), to(#0099e6));background-image:-webkit-linear-gradient(top, #00aaff, #0099e6);background-image:-o-linear-gradient(top, #00aaff, #0099e6);background-image:linear-gradient(to bottom, #00aaff, #0099e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00aaff', endColorstr='#ff0099e6', GradientType=0);border-color:#0099e6 #0099e6 #006699;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#0099e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#ffffff;background-color:#0099e6;*background-color:#0088cc;}
+.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#0077b3 \9;}
+.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}
+.breadcrumb .divider{padding:0 5px;color:#ccc;}
+.breadcrumb .active{color:#999999;}
+.pagination{height:40px;margin:20px 0;}
+.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
+.pagination ul>li{display:inline;}
+.pagination ul>li>a,.pagination ul>li>span{float:left;padding:0 14px;line-height:38px;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0;}
+.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5;}
+.pagination ul>.active>a,.pagination ul>.active>span{color:#999999;cursor:default;}
+.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999999;background-color:transparent;cursor:default;}
+.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.pagination-centered{text-align:center;}
+.pagination-right{text-align:right;}
+.pager{margin:20px 0;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";line-height:0;}
+.pager:after{clear:both;}
+.pager li{display:inline;}
+.pager a,.pager span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
+.pager .next a,.pager .next span{float:right;}
+.pager .previous a{float:left;}
+.pager .disabled a,.pager .disabled a:hover,.pager .disabled span{color:#999999;background-color:#fff;cursor:default;}
+.modal-open .modal .dropdown-menu{z-index:2050;}
+.modal-open .modal .dropdown.open{*z-index:2050;}
+.modal-open .modal .popover{z-index:2060;}
+.modal-open .modal .tooltip{z-index:2080;}
+.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
+.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
+.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
+.modal.fade.in{top:50%;}
+.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
+.modal-header h3{margin:0;line-height:30px;}
+.modal-body{overflow-y:auto;max-height:400px;padding:15px;}
+.modal-form{margin-bottom:0;}
+.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0;}
+.modal-footer:after{clear:both;}
+.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
+.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
+.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
+.tooltip.top{margin-top:-3px;}
+.tooltip.right{margin-left:3px;}
+.tooltip.bottom{margin-top:3px;}
+.tooltip.left{margin-left:-3px;}
+.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid;}
+.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000;}
+.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000;}
+.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000;}
+.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000;}
+.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#ffffff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);}.popover.top{margin-bottom:10px;}
+.popover.right{margin-left:10px;}
+.popover.bottom{margin-top:10px;}
+.popover.left{margin-right:10px;}
+.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;}
+.popover-content{padding:9px 14px;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
+.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid;}
+.popover .arrow:after{content:"";z-index:-1;}
+.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-width:10px 10px 0;border-top-color:#ffffff;}.popover.top .arrow:after{border-width:11px 11px 0;border-top-color:rgba(0, 0, 0, 0.25);bottom:-1px;left:-11px;}
+.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-width:10px 10px 10px 0;border-right-color:#ffffff;}.popover.right .arrow:after{border-width:11px 11px 11px 0;border-right-color:rgba(0, 0, 0, 0.25);bottom:-11px;left:-1px;}
+.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-width:0 10px 10px;border-bottom-color:#ffffff;}.popover.bottom .arrow:after{border-width:0 11px 11px;border-bottom-color:rgba(0, 0, 0, 0.25);top:-1px;left:-11px;}
+.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-width:10px 0 10px 10px;border-left-color:#ffffff;}.popover.left .arrow:after{border-width:11px 0 11px 11px;border-left-color:rgba(0, 0, 0, 0.25);bottom:-11px;right:-1px;}
+.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0;}
+.thumbnails:after{clear:both;}
+.row-fluid .thumbnails{margin-left:0;}
+.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px;}
+.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;}
+a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
+.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
+.thumbnail .caption{padding:9px;color:#555555;}
+.label,.badge{font-size:11.844px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;}
+.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
+a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
+.label-important,.badge-important{background-color:#b94a48;}
+.label-important[href],.badge-important[href]{background-color:#953b39;}
+.label-warning,.badge-warning{background-color:#f89406;}
+.label-warning[href],.badge-warning[href]{background-color:#c67605;}
+.label-success,.badge-success{background-color:#468847;}
+.label-success[href],.badge-success[href]{background-color:#356635;}
+.label-info,.badge-info{background-color:#3a87ad;}
+.label-info[href],.badge-info[href]{background-color:#2d6987;}
+.label-inverse,.badge-inverse{background-color:#333333;}
+.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}
+.btn .label,.btn .badge{position:relative;top:-1px;}
+.btn-mini .label,.btn-mini .badge{top:0;}
+@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(to bottom, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.progress .bar{width:0%;height:100%;color:#ffffff;float:left;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(to bottom, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
+.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);}
+.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
+.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
+.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(to bottom, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);}
+.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(to bottom, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);}
+.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(to bottom, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);}
+.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);}
+.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.accordion{margin-bottom:20px;}
+.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.accordion-heading{border-bottom:0;}
+.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
+.accordion-toggle{cursor:pointer;}
+.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
+.carousel{position:relative;margin-bottom:20px;line-height:1;}
+.carousel-inner{overflow:hidden;width:100%;position:relative;}
+.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
+.carousel .item>img{display:block;line-height:1;}
+.carousel .active,.carousel .next,.carousel .prev{display:block;}
+.carousel .active{left:0;}
+.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
+.carousel .next{left:100%;}
+.carousel .prev{left:-100%;}
+.carousel .next.left,.carousel .prev.right{left:0;}
+.carousel .active.left{left:-100%;}
+.carousel .active.right{left:100%;}
+.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:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
+.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
+.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333333;background:rgba(0, 0, 0, 0.75);}
+.carousel-caption h4,.carousel-caption p{color:#ffffff;line-height:20px;}
+.carousel-caption h4{margin:0 0 5px;}
+.carousel-caption p{margin-bottom:0;}
+.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
+.hero-unit p{font-size:18px;font-weight:200;line-height:30px;color:inherit;}
+.pull-right{float:right;}
+.pull-left{float:left;}
+.hide{display:none;}
+.show{display:block;}
+.invisible{visibility:hidden;}
+.affix{position:fixed;}
+@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:20px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%;} .row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%;} .row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%;} .row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%;} .row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%;} .row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%;} .row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%;} .row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%;} .row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%;} .row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%;} .row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%;} .row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%;} .row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%;} .row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%;} .row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%;} .row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%;} .row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%;} .row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%;} .row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%;} .row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%;} .row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%;} .row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%;} .row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%;} .row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%;} .row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%;} .row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%;} .row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%;} .row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%;} .row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%;} .row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%;} .row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%;} .row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%;} .row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%;} .row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%;} .row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:20px;} input.span12, textarea.span12, .uneditable-input.span12{width:710px;} input.span11, textarea.span11, .uneditable-input.span11{width:648px;} input.span10, textarea.span10, .uneditable-input.span10{width:586px;} input.span9, textarea.span9, .uneditable-input.span9{width:524px;} input.span8, textarea.span8, .uneditable-input.span8{width:462px;} input.span7, textarea.span7, .uneditable-input.span7{width:400px;} input.span6, textarea.span6, .uneditable-input.span6{width:338px;} input.span5, textarea.span5, .uneditable-input.span5{width:276px;} input.span4, textarea.span4, .uneditable-input.span4{width:214px;} input.span3, textarea.span3, .uneditable-input.span3{width:152px;} input.span2, textarea.span2, .uneditable-input.span2{width:90px;} input.span1, textarea.span1, .uneditable-input.span1{width:28px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:30px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%;} .row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%;} .row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%;} .row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%;} .row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%;} .row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%;} .row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%;} .row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%;} .row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%;} .row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%;} .row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%;} .row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%;} .row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%;} .row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%;} .row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%;} .row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%;} .row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%;} .row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%;} .row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%;} .row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%;} .row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%;} .row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%;} .row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%;} .row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%;} .row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%;} .row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%;} .row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%;} .row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%;} .row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%;} .row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%;} .row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%;} .row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%;} .row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%;} .row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%;} .row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:30px;} input.span12, textarea.span12, .uneditable-input.span12{width:1156px;} input.span11, textarea.span11, .uneditable-input.span11{width:1056px;} input.span10, textarea.span10, .uneditable-input.span10{width:956px;} input.span9, textarea.span9, .uneditable-input.span9{width:856px;} input.span8, textarea.span8, .uneditable-input.span8{width:756px;} input.span7, textarea.span7, .uneditable-input.span7{width:656px;} input.span6, textarea.span6, .uneditable-input.span6{width:556px;} input.span5, textarea.span5, .uneditable-input.span5{width:456px;} input.span4, textarea.span4, .uneditable-input.span4{width:356px;} input.span3, textarea.span3, .uneditable-input.span3{width:256px;} input.span2, textarea.span2, .uneditable-input.span2{width:156px;} input.span1, textarea.span1, .uneditable-input.span1{width:56px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}footer{padding-top:16px;padding-bottom:32px;color:#b0b0b0;}footer a,footer a:link,footer a:active,footer a:visited{color:#b0b0b0;text-decoration:underline;}
+footer a:hover{color:#7d7d7d;}
+footer .go-to-top{float:right;}footer .go-to-top,footer .go-to-top:link,footer .go-to-top:active,footer .go-to-top:visited{text-decoration:none;}footer .go-to-top i,footer .go-to-top:link i,footer .go-to-top:active i,footer .go-to-top:visited i{opacity:0.4;filter:alpha(opacity=40);}
+footer .go-to-top:hover i{opacity:0.65;filter:alpha(opacity=65);}
+form label{color:#555555;font-weight:bold;cursor:pointer;}
+form fieldset{border-top:1px solid #e8e8e8;margin:0px;padding:0px;padding-top:16px;padding-bottom:8px;}form fieldset legend{margin:0px;margin-bottom:-8px;padding:0px;padding-top:8px;}
+form fieldset .control-group{padding-bottom:4px;}
+form fieldset .control-group:last-child{padding-bottom:0px;}
+form fieldset:first-child{border-top:none;padding-top:0px;}
+form fieldset:last-child{padding-bottom:0px;}
+textarea{resize:vertical;}
+.radio-group,.select-multiple,.yes-no-switch{margin-bottom:8px;}.radio-group label,.select-multiple label,.yes-no-switch label{color:#000000;font-weight:normal;}
+.checkbox{font-weight:normal;}
+.table-footer{background:none;margin-bottom:0px;padding:0px 8px;position:relative;bottom:20px;}.table-footer .pager{margin:0px 0px;margin-top:9px;padding:0px;margin-right:6px;}.table-footer .pager>li{margin-right:6px;}.table-footer .pager>li>a:link,.table-footer .pager>li>a:active,.table-footer .pager>li>a:visited{background:#e8e8e8;border:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:2px 5px;}
+.table-footer .pager>li>a:hover{background-color:#0088cc;}.table-footer .pager>li>a:hover i{background-image:url("../img/glyphicons-halflings-white.png");}
+.table-footer .table-count{padding:11px 0px;color:#555555;}
+.table-footer .form-inline{margin:0px;padding:6px 0px;}
+td.check-cell,th.check-cell{width:32px;}
+td .checkbox,th .checkbox{margin-bottom:0px;position:relative;bottom:1px;}td .checkbox input,th .checkbox input{position:relative;left:9px;}
+td.lead-cell{color:#555555;font-weight:bold;}
+th.table-sort{padding:0px;}th.table-sort a:link,th.table-sort a:active,th.table-sort a:visited a:hover{display:block;padding:8px;}
+th.table-sort.sort-active-asc a:link,th.table-sort.sort-active-asc a:active,th.table-sort.sort-active-asc a:visited{border-bottom:3px solid #049cdb;padding-bottom:5px;}
+th.table-sort.sort-active-asc a:hover{border-bottom:3px solid #e4776f;padding-bottom:5px;text-decoration:none;}
+th.table-sort.sort-active-desc a:link,th.table-sort.sort-active-desc a:active,th.table-sort.sort-active-desc a:visited{border-bottom:3px solid #dc4e44;padding-bottom:5px;}
+th.table-sort.sort-active-desc a:hover{border-bottom:3px solid #17b8fb;padding-bottom:5px;text-decoration:none;}
+th.table-sort.sort-asc a:hover{border-bottom:3px solid #ade6fe;padding-bottom:5px;text-decoration:none;}
+th.table-sort.sort-desc a:hover{border-bottom:3px solid #eca09a;padding-bottom:5px;text-decoration:none;}
+.well{background-color:#ffffff;border:1px solid #d6d6d6;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0px 0px 0px 3px #f0f0f0;-moz-box-shadow:0px 0px 0px 3px #f0f0f0;box-shadow:0px 0px 0px 3px #f0f0f0;padding:24px 12px;margin:0px -12px;}.well .alert{border-width:0px 0px 1px 0px;-webkit-border-radius:2px 2px 0px 0px;-moz-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px;margin:-24px -12px;margin-bottom:12px;padding:12px;font-weight:bold;}
+.well .form-actions{background-color:#fafafa;-webkit-border-radius:0px 0px 3px 3px;-moz-border-radius:0px 0px 3px 3px;border-radius:0px 0px 3px 3px;margin:-44px -12px;margin-top:12px;}
+.well .form-horizontal .form-actions{padding-left:192px;}
+.list-tiny{display:block;margin:0px -6px;margin-bottom:16px;padding:4px 0px;overflow:auto;}.list-tiny>li{background:#e8e8e8;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin:3px 6px;padding:4px 7px;padding-bottom:6px;float:left;list-style:none;font-weight:bold;}
+.btn{background:#dcdcdc;border:1px solid #dcdcdc;*border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;padding:4px 10px;color:#6f6f6f;font-weight:bold;text-shadow:none;}.btn:hover,.btn:active{background:#e1e1e1;border:1px solid #e1e1e1;*border:0;box-shadow:none;color:#0088cc;}
+.btn i{opacity:0.7;filter:alpha(opacity=70);}
+.btn:hover i,.btn:active i{opacity:1;filter:alpha(opacity=100);}
+.btn.btn-primary,.btn.btn-info,.btn.btn-success,.btn.btn-warning,.btn.btn-danger,.btn.btn-inverse{color:#f2f2f2;}.btn.btn-primary i,.btn.btn-info i,.btn.btn-success i,.btn.btn-warning i,.btn.btn-danger i,.btn.btn-inverse i{opacity:0.9;filter:alpha(opacity=90);}
+.btn.btn-primary:hover i,.btn.btn-info:hover i,.btn.btn-success:hover i,.btn.btn-warning:hover i,.btn.btn-danger:hover i,.btn.btn-inverse:hover i,.btn.btn-primary:active i,.btn.btn-info:active i,.btn.btn-success:active i,.btn.btn-warning:active i,.btn.btn-danger:active i,.btn.btn-inverse:active i{opacity:1;filter:alpha(opacity=100);}
+.btn.btn-primary:hover,.btn.btn-info:hover,.btn.btn-success:hover,.btn.btn-warning:hover,.btn.btn-danger:hover,.btn.btn-inverse:hover,.btn.btn-primary:active,.btn.btn-info:active,.btn.btn-success:active,.btn.btn-warning:active,.btn.btn-danger:active,.btn.btn-inverse:active{color:#ffffff;}
+.btn.btn-primary{background:#0088cc;border:1px solid #0088cc;*border:0;background:#0088cc;border:1px solid #0088cc;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #0077b3;}.btn.btn-primary:hover,.btn.btn-primary:active{background:#00a3f5;border:1px solid #00a3f5;*border:0;}
+.btn.btn-info{background:#507e95;border:1px solid #507e95;*border:0;background:#507e95;border:1px solid #507e95;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #477085;}.btn.btn-info:hover,.btn.btn-info:active{background:#709cb2;border:1px solid #709cb2;*border:0;}
+.btn.btn-success{background:#46a546;border:1px solid #46a546;*border:0;background:#46a546;border:1px solid #46a546;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #3e933e;}.btn.btn-success:hover,.btn.btn-success:active{background:#69bf69;border:1px solid #69bf69;*border:0;}
+.btn.btn-warning{background:#ee8e06;border:1px solid #ee8e06;*border:0;background:#ee8e06;border:1px solid #ee8e06;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #d57f05;}.btn.btn-warning:hover,.btn.btn-warning:active{background:#faa937;border:1px solid #faa937;*border:0;}
+.btn.btn-danger{background:#d13327;border:1px solid #d13327;*border:0;background:#d13327;border:1px solid #d13327;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #bb2d23;}.btn.btn-danger:hover,.btn.btn-danger:active{background:#e05f55;border:1px solid #e05f55;*border:0;}
+.btn.btn-inverse{background:#555555;border:1px solid #555555;*border:0;background:#555555;border:1px solid #555555;*border:0;color:#f7f7f7;text-shadow:0px 1px 0px #484848;}.btn.btn-inverse:hover,.btn.btn-inverse:active{background:#7e7e7e;border:1px solid #7e7e7e;*border:0;}
+.btn.btn-link{background:none;border:none;}.btn.btn-link:hover,.btn.btn-link:active{color:#00aaff;text-decoration:none;}
+.alerts-global{margin-top:16px;}
+.alert-form{margin:0px;margin-bottom:16px;font-weight:bold;}
+.alert-inline{margin:0px;padding:0px;}
+.alert-icon{float:left;}.alert-icon span{-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;padding:1px 2px;}
+.alert .alert-icon span{background:#f89406;}
+.alert-error .alert-icon span{background:#9d261d;}
+.alert-success .alert-icon span{background:#46a546;}
+.alert-info .alert-icon span{background:#049cdb;}
+.alert{padding:10px 11px;}.alert p{margin-left:28px;}
+.alert p:last-child{margin-bottom:0px;}
+.alert p.protip{font-size:100%;}
+.avatar-big{border-radius:3px;width:180px;height:180px;}
+.avatar-normal{border-radius:3px;width:64px;height:64px;}
+.avatar-small{border-radius:3px;width:28px;height:28px;}
+.avatar-tiny{border-radius:3px;width:16px;height:16px;}
+.navbar .avatar-small{margin:-10px 0px;position:relative;bottom:2px;}
+.page-header .avatar{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.page-header h1 .avatar{width:46px;height:46px;}
+.page-header h2 .avatar{width:40px;height:40px;}
+.page-header h3 .avatar{width:28px;height:28px;}
+.page-header h4 .avatar{width:22px;height:22px;}
+.header-tabbed{border-bottom:none;padding-bottom:0px;margin-bottom:0px;}.header-tabbed .nav-tabs{margin-bottom:0px;}
+.nav-tabs li a:link,.nav-tabs li a:active,.nav-tabs li a:visited{opacity:0.6;filter:alpha(opacity=60);color:#333333;font-weight:bold;}
+.nav-tabs li i{margin-right:4px;}
+.nav-tabs li.active a:link,.nav-tabs li.active a:active,.nav-tabs li.active a:visited,.nav-tabs li.active a:hover{background-color:#fcfcfc;}.nav-tabs li.active a:link i,.nav-tabs li.active a:active i,.nav-tabs li.active a:visited i,.nav-tabs li.active a:hover i{background-image:url("../img/glyphicons-halflings.png");}
+.nav-tabs li.active a:link,.nav-tabs li.active a:active,.nav-tabs li.active a:visited,.nav-tabs li.active a:hover,.nav-tabs li a:hover{opacity:1;filter:alpha(opacity=100);}
+.nav-tabs li.fallback{float:right;}.nav-tabs li.fallback a:link,.nav-tabs li.fallback a:active,.nav-tabs li.fallback a:visited,.nav-tabs li.fallback a:hover{border-bottom:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin-top:4px;padding:4px 12px;}
+.navbar-fixed-top{position:static;}
+.navbar-userbar .navbar-inner{background:none;background-color:#fcfcfc;border-bottom:4px solid #e3e3e3;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;overflow:auto;}
+.navbar-userbar li a,.navbar-userbar li a:link,.navbar-userbar li a:active,.navbar-userbar li a:visited,.navbar-userbar li button.btn-link{opacity:0.5;filter:alpha(opacity=50);color:#000000;font-weight:bold;}.navbar-userbar li a i,.navbar-userbar li a:link i,.navbar-userbar li a:active i,.navbar-userbar li a:visited i,.navbar-userbar li button.btn-link i{opacity:1;filter:alpha(opacity=100);}
+.navbar-userbar li.user-profile a,.navbar-userbar li.user-profile a:link,.navbar-userbar li.user-profile a:visited{opacity:0.8;filter:alpha(opacity=80);}
+.navbar-userbar li button{padding-top:5px;}
+.navbar-userbar li a:hover,.navbar-userbar li a:active,.navbar-userbar li button.btn-link:hover,.navbar-userbar li button.btn-link:active{opacity:1;filter:alpha(opacity=100);color:#000000;}
+.navbar-userbar li i{background-image:url("../img/glyphicons-halflings.png");opacity:1;filter:alpha(opacity=100);}
+.navbar-userbar li form{margin:0px;padding:0px;}
+.navbar-userbar li span{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:2px 6px;margin:-1px 0px;margin-left:4px;color:#ffffff;font-size:90%;text-shadow:1px 1px 0px #000000;}.navbar-userbar li span.stat{background:#049cdb;}
+.navbar-userbar li span.stat.att{background:#9d261d;}
+.navbar-header{border-bottom:1px solid #0077b3;}.navbar-header .navbar-inner{background:none;background-color:#00aaff;border-bottom:4px solid #0099e6;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.navbar-header .container{height:auto;}
+.navbar-header a.brand{margin:24px 0px;padding:0px;font-size:230%;}.navbar-header a.brand span{color:#005580;text-shadow:0px 1px 0px #80d4ff;}
+.navbar-header i{background-image:url("../img/glyphicons-halflings-white.png");}
+.navbar-header .search-form{background-color:#ffffff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0px 0px 3px #0077b3;-moz-box-shadow:0px 0px 3px #0077b3;box-shadow:0px 0px 3px #0077b3;margin:19px 0px;margin-right:8px;padding:1px;float:right;}.navbar-header .search-form .btn{background:#46a546;border:none;margin:0px;margin-left:1px;padding-left:7px;padding-right:7px;}.navbar-header .search-form .btn i{opacity:1;filter:alpha(opacity=100);}
+.navbar-header .search-form .btn:hover,.navbar-header .search-form .btn:active{background:#74c474;}
+.navbar-header .search-form input{background:none;border:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.navbar-header .nav{float:right;margin:19px 0px;}.navbar-header .nav li a{background-color:#0088cc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin-left:8px;opacity:0.9;filter:alpha(opacity=90);padding:5px 8px;}
+.navbar-header .nav li a:hover{background-color:#ffffff;-webkit-box-shadow:0px 0px 3px #0077b3;-moz-box-shadow:0px 0px 3px #0077b3;box-shadow:0px 0px 3px #0077b3;opacity:1;filter:alpha(opacity=100);}.navbar-header .nav li a:hover i{background-image:url("../img/glyphicons-halflings.png");}
+.clickable{cursor:pointer;}

+ 84 - 0
static/sora/css/sora.less

@@ -0,0 +1,84 @@
+/*!
+ * Bootstrap v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+// CSS Reset
+@import "bootstrap/reset.less";
+
+// Core variables and mixins
+@import "variables.less";
+@import "bootstrap/mixins.less";
+
+// Grid system and page structure
+@import "bootstrap/scaffolding.less";
+@import "bootstrap/grid.less";
+@import "bootstrap/layouts.less";
+
+// Base CSS
+@import "bootstrap/type.less";
+@import "bootstrap/code.less";
+@import "bootstrap/forms.less";
+@import "bootstrap/tables.less";
+
+// Components: common
+@import "bootstrap/sprites.less";
+@import "bootstrap/dropdowns.less";
+@import "bootstrap/wells.less";
+@import "bootstrap/component-animations.less";
+@import "bootstrap/close.less";
+
+// Components: Buttons & Alerts
+@import "bootstrap/buttons.less";
+@import "bootstrap/button-groups.less";
+@import "bootstrap/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
+
+// Components: Nav
+@import "bootstrap/navs.less";
+@import "bootstrap/navbar.less";
+@import "bootstrap/breadcrumbs.less";
+@import "bootstrap/pagination.less";
+@import "bootstrap/pager.less";
+
+// Components: Popovers
+@import "bootstrap/modals.less";
+@import "bootstrap/tooltip.less";
+@import "bootstrap/popovers.less";
+
+// Components: Misc
+@import "bootstrap/thumbnails.less";
+@import "bootstrap/labels-badges.less";
+@import "bootstrap/progress-bars.less";
+@import "bootstrap/accordion.less";
+@import "bootstrap/carousel.less";
+@import "bootstrap/hero-unit.less";
+
+// Utility classes
+@import "bootstrap/utilities.less"; // Has to be last to override when necessary
+
+// Responsiveness
+@import "bootstrap/responsive-768px-979px.less";
+@import "bootstrap/responsive-1200px-min.less";
+
+// Sora theme
+@import "sora/scaffolding.less";
+
+@import "sora/forms.less";
+@import "sora/tables.less";
+
+@import "sora/wells.less";
+@import "sora/users-lists.less";
+
+@import "sora/buttons.less";
+@import "sora/alerts.less";
+
+@import "sora/avatars.less";
+@import "sora/navs.less";
+@import "sora/navbar.less";
+
+@import "sora/utilities.less";

+ 62 - 0
static/sora/css/sora/alerts.less

@@ -0,0 +1,62 @@
+// Misago alerts
+// -------------------------
+.alerts-global {
+  margin-top: 16px;
+}
+
+.alert-form {
+  margin: 0px;
+  margin-bottom: 16px;
+  
+  font-weight: bold;
+}
+
+.alert-inline {
+  margin: 0px;
+  padding: 0px;
+}
+
+// Alerts icons
+// -------------------------
+.alert-icon {
+  float: left;
+  
+  span {
+    .border-radius(2px);
+    padding: 1px 2px; 
+  }
+}
+
+.alert .alert-icon span {
+  background: @orange;
+}
+
+.alert-error .alert-icon span {
+  background: @red;  
+}
+
+.alert-success .alert-icon span {
+  background: @green;  
+}
+
+.alert-info .alert-icon span {
+  background: @blue;  
+}
+
+// Alerts paddings
+// -------------------------
+.alert {
+  padding: 10px 11px;
+  
+  p {
+    margin-left: 28px;
+  }
+  
+  p:last-child {
+    margin-bottom: 0px;
+  }
+  
+  p.protip {
+    font-size: 100%;
+  }
+}

+ 61 - 0
static/sora/css/sora/avatars.less

@@ -0,0 +1,61 @@
+// Misago avatars sizes
+// -------------------------
+.avatar-big {
+  border-radius: 3px;
+  width: 180px;
+  height: 180px;
+}
+
+.avatar-normal {
+  border-radius: 3px;
+  width: 64px;
+  height: 64px;
+}
+
+.avatar-small {
+  border-radius: 3px;
+  width: 28px;
+  height: 28px;
+}
+
+.avatar-tiny {
+  border-radius: 3px;
+  width: 16px;
+  height: 16px;
+}
+
+// Handle small avatar within different places
+// -------------------------
+.navbar {
+  .avatar-small {
+    margin: -(@baseLineHeight / 2) 0px;
+    position: relative;
+    bottom: (((@navbarHeight - @baseLineHeight - 14) / 2) - 1);
+  }
+}
+
+.page-header {
+  .avatar {
+    .border-radius(3px);
+  }
+  
+  h1 .avatar {
+    width: 46px;
+    height: 46px;
+  }
+  
+  h2 .avatar {
+    width: 40px;
+    height: 40px;
+  }
+  
+  h3 .avatar {
+    width: 28px;
+    height: 28px;
+  }
+  
+  h4 .avatar {
+    width: 22px;
+    height: 22px;
+  }
+}

+ 105 - 0
static/sora/css/sora/buttons.less

@@ -0,0 +1,105 @@
+.button-colors (@btnColor) {
+  background: @btnColor;
+  border: 1px solid @btnColor;
+  *border: 0;
+}
+
+// Misago buttons
+// -------------------------
+.button-icon (@default, @hover: 100) {
+  i {
+    .opacity(@default);
+  }
+  
+  &:hover, &:active {
+    i {
+      .opacity(@hover);
+    }
+  }
+}
+
+.button-style (@background, @highlight) {
+  .button-colors(@background);
+
+  color: darken(@white, 3%);
+  text-shadow: 0px 1px 0px darken(@background, 5%);
+
+  &:hover, &:active {
+    .button-colors(@highlight);
+  }
+}
+
+.btn {
+  .button-colors(@btnBackground);
+  .box-shadow(none);
+  padding: 4px 10px;
+  
+  color: lighten(@gray, 10%);
+  font-weight: bold;
+  text-shadow: none;
+  
+  &:hover, &:active {
+    .button-colors(@btnBackgroundHighlight);
+    box-shadow: none;
+  
+    color: @linkColor;
+  }
+  
+  .button-icon(70);
+  
+  &.btn-primary,
+  &.btn-info,
+  &.btn-success,
+  &.btn-warning,
+  &.btn-danger,
+  &.btn-inverse {
+    .button-icon(90);
+  
+    color: darken(@white, 5%);
+  
+    &:hover, &:active {  
+      color: @white;
+    }
+  }
+  
+  &.btn-primary {
+    .button-colors(@btnPrimaryBackground);
+    .button-style(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
+  }
+  
+  &.btn-info {
+    .button-colors(@btnInfoBackground);
+    .button-style(@btnInfoBackground, @btnInfoBackgroundHighlight);
+  }
+  
+  &.btn-success {
+    .button-colors(@btnSuccessBackground);
+    .button-style(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
+  }
+  
+  &.btn-warning {
+    .button-colors(@btnWarningBackground);
+    .button-style(@btnWarningBackground, @btnWarningBackgroundHighlight);
+  }
+  
+  &.btn-danger {
+    .button-colors(@btnDangerBackground);
+    .button-style(@btnDangerBackground, @btnDangerBackgroundHighlight);
+  }
+  
+  &.btn-inverse {
+    .button-colors(@btnInverseBackground);
+    .button-style(@btnInverseBackground, @btnInverseBackgroundHighlight);
+  }
+  
+  &.btn-link {
+    background: none;
+    border: none;
+  
+    &:hover, &:active {
+      color: @linkColorHover;
+      
+      text-decoration: none;
+    }
+  }
+}

+ 62 - 0
static/sora/css/sora/forms.less

@@ -0,0 +1,62 @@
+// Misago forms
+// -------------------------
+form {
+  label {
+    color: @gray;
+    font-weight: bold;
+    cursor: pointer;
+  }
+      
+  fieldset {
+    border-top: 1px solid darken(@bodyBackground, 8%);
+    margin: 0px;
+    padding: 0px;
+    padding-top: 16px;
+    padding-bottom: 8px;
+  
+    legend {
+      margin: 0px;
+      margin-bottom: -8px;
+      padding: 0px;
+      padding-top: 8px;
+    }
+        
+    .control-group {
+      padding-bottom: 4px;
+    }
+    
+    .control-group:last-child {
+      padding-bottom: 0px;
+    }
+  }
+  
+  fieldset:first-child {
+    border-top: none;
+    padding-top: 0px;
+  }
+  
+  fieldset:last-child {
+    padding-bottom: 0px;
+  }
+}
+
+// Make textarea resizeable vertically only
+// -------------------------
+textarea {
+  resize: vertical;
+}
+
+// Lists styling
+// -------------------------
+.radio-group, .select-multiple, .yes-no-switch {
+  label {
+    color: @black;
+    font-weight: normal;
+  }
+  
+  margin-bottom: 8px;
+}
+
+.checkbox {
+  font-weight: normal;
+}

+ 162 - 0
static/sora/css/sora/navbar.less

@@ -0,0 +1,162 @@
+// Misago navbars
+// -------------------------
+.navbar-fixed-top {
+  position: static;
+}
+
+.navbar-userbar {
+  .navbar-inner {
+    background: none;
+    background-color: @navbarBackground;
+    border-bottom: 4px solid darken(@navbarBackground, 10%);
+    .box-shadow(none);
+    overflow: auto;
+  }
+  
+  li {
+    a, a:link, a:active, a:visited, button.btn-link {
+      .opacity(50);
+      
+      color: @black;
+      font-weight: bold;
+       
+      i {
+        .opacity(100);
+      }
+    }
+    
+    &.user-profile {
+      a, a:link, a:visited {
+        .opacity(80);
+      }
+    }
+    
+    button {
+      padding-top: 5px;
+    }
+     
+    a:hover, a:active, button.btn-link:hover, button.btn-link:active {
+      .opacity(100);
+      
+      color: @black;
+    }
+      
+    i {
+      background-image: url("@{iconSpritePath}");
+      .opacity(100);
+    }
+    
+    form {
+      margin: 0px;
+      padding: 0px;
+    }
+    
+    span {
+      .border-radius(3px);
+      padding: 2px 6px;
+      margin: -1px 0px;
+      margin-left: 4px;
+      
+      color: @white;
+      font-size: 90%;
+      text-shadow: 1px 1px 0px @black;
+      
+      &.stat {
+        background: @blue;
+      }
+      
+      &.stat.att {
+        background: @red;
+      }
+    }
+  }
+}
+
+.navbar-header {
+  border-bottom: 1px solid darken(@navbarInverseBackground, 15%);
+  
+  .navbar-inner {
+    background: none;
+    background-color: @navbarInverseBackground;
+    border-bottom: 4px solid darken(@navbarInverseBackground, 5%);
+    .box-shadow(none);
+  }
+  
+  .container {
+    height: auto;
+  }
+  
+  a.brand {
+    margin: 24px 0px;
+    padding: 0px;
+    
+    font-size: 230%;
+    
+    span {
+      color: darken(@navbarInverseBackground, 25%);
+      text-shadow: 0px 1px 0px lighten(@navbarInverseBackground, 25%);
+    }
+  }
+       
+  i {
+    background-image: url("@{iconWhiteSpritePath}");
+  }
+        
+  .search-form {
+    background-color: @white;
+    .border-radius(4px);
+    .box-shadow(0px 0px 3px darken(@navbarInverseBackground, 15%));
+    margin: 19px 0px;
+    margin-right: 8px;
+    padding: 1px;
+    float: right;
+    
+    .btn {
+      background: @green;
+      border: none;
+      margin: 0px;
+      margin-left: 1px;
+      padding-left: 7px;
+      padding-right: 7px;
+      
+      i {
+        .opacity(100);
+      }
+      
+      &:hover, &:active {
+        background: lighten(@green, 15%);
+      }
+    }
+    
+    input {
+      background: none;
+      border: none;
+      .box-shadow(none);
+    }
+  }
+  
+  .nav {
+    float: right;
+    margin: 19px 0px;
+    
+    li {
+      a {
+        background-color: @navbarInverseLinkBackground;
+        .border-radius(3px);
+        margin-left: 8px;
+        .opacity(90);
+        padding: 5px 8px;
+      }
+      
+      a:hover {
+        background-color: @navbarInverseLinkBackgroundHover;
+        .box-shadow(0px 0px 3px darken(@navbarInverseBackground, 15%));
+        .opacity(100);
+       
+        i {
+          background-image: url("@{iconSpritePath}");
+        }
+      }
+    }
+  }
+}

+ 50 - 0
static/sora/css/sora/navs.less

@@ -0,0 +1,50 @@
+// Misago Navigations
+// -------------------------
+.header-tabbed {
+  border-bottom: none;
+  padding-bottom: 0px;
+  margin-bottom: 0px;
+  
+  .nav-tabs {
+    margin-bottom: 0px;
+  }
+}
+
+.nav-tabs li {
+  a:link, a:active, a:visited {
+    .opacity(60);
+    
+    color: @textColor;
+    font-weight: bold;
+  }
+  
+  i {
+    margin-right: 4px;
+  }
+  
+  &.active a:link, &.active a:active,
+  &.active a:visited, &.active a:hover {
+    background-color: @bodyBackground;
+        
+    i {
+      background-image: url("@{iconSpritePath}");
+    }
+  }
+  
+  &.active a:link, &.active a:active,
+  &.active a:visited, &.active a:hover, a:hover {
+    .opacity(100);
+  }
+  
+  &.fallback {
+    float:right;
+    
+    a:link, a:active,
+    a:visited, a:hover {
+      border-bottom: none;
+      .border-radius(3px);
+      margin-top: 4px;
+      padding: 4px 12px;
+    }
+  }
+}

+ 37 - 0
static/sora/css/sora/scaffolding.less

@@ -0,0 +1,37 @@
+// Layout elements that dont deserve their own file go there
+// -------------------------
+
+// Footer
+footer {
+  padding-top: 16px;
+  padding-bottom: 32px;
+  
+  color: darken(@bodyBackground, 30%);
+  
+  a, a:link, a:active, a:visited {
+    color: darken(@bodyBackground, 30%);
+    text-decoration: underline;
+  }
+  
+  a:hover {
+    color: darken(@bodyBackground, 50%);
+  }
+  
+  .go-to-top {
+    float: right;
+    
+    &, &:link, &:active, &:visited {
+      text-decoration: none;
+      
+      i {
+        .opacity(40);
+      }
+    }
+    
+    &:hover {
+      i {
+        .opacity(65);
+      }
+    }
+  }
+}

+ 128 - 0
static/sora/css/sora/tables.less

@@ -0,0 +1,128 @@
+// Tables
+// --------------------------------------------------
+.table-footer {
+  background: none;
+  margin-bottom: 0px;
+  padding: 0px 8px;
+  position: relative;
+  bottom: 20px;
+  
+  .pager {
+    margin: 0px 0px;
+    margin-top: 9px;
+    padding: 0px;
+    margin-right: 6px;
+    
+    &>li {
+      margin-right: 6px;
+    
+      &>a {
+        &:link, &:active, &:visited {
+          background: darken(@bodyBackground, 8%);
+          border: none;
+          .border-radius(3px);
+          padding: 2px 5px;
+        }
+      
+        &:hover {
+          background-color: @linkColor;
+          
+          i {
+            background-image: url("@{iconWhiteSpritePath}");          
+          }
+        }
+      }
+    }
+  }
+  
+  .table-count {
+    padding: 11px 0px;
+    
+    color: @gray;
+  }
+  
+  .form-inline {
+    margin: 0px;
+    padding: 6px 0px;
+  }
+}
+
+// Checkbox cell
+td, th {
+  &.check-cell {
+    width: 32px;
+  }
+    
+  .checkbox {
+    margin-bottom: 0px;
+    position: relative;
+    bottom: 1px;
+    
+    input {
+      position: relative;
+      left: 9px;
+    }
+  }
+}
+
+// Lead cell
+td.lead-cell {
+  color: @gray;
+  font-weight: bold;
+}
+
+// Table sorting styles
+th.table-sort {
+  padding: 0px;
+  
+  a:link, a:active, a:visited a:hover{
+    display: block;
+    padding: 8px;
+  }
+  
+  &.sort-active-asc {
+    a:link, a:active, a:visited {
+      border-bottom: 3px solid @blue;
+      padding-bottom: 5px;
+    }
+    
+    a:hover {
+      border-bottom: 3px solid lighten(@red, 30%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-active-desc {
+    a:link, a:active, a:visited {
+      border-bottom: 3px solid lighten(@red, 20%);
+      padding-bottom: 5px;
+    }
+    
+    a:hover {
+      border-bottom: 3px solid lighten(@blue, 10%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-asc {
+    a:hover {
+      border-bottom: 3px solid lighten(@blue, 40%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-desc {    
+    a:hover {
+      border-bottom: 3px solid lighten(@red, 40%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }    
+  }
+}

+ 21 - 0
static/sora/css/sora/users-lists.less

@@ -0,0 +1,21 @@
+// Users list
+// --------------------------------------------------
+.list-tiny {
+  display: block;
+  margin: 0px -6px;
+  margin-bottom: 16px;
+  padding: 4px 0px;
+  overflow: auto;
+  
+  &>li {
+    background: darken(@bodyBackground, 8%);
+    .border-radius(3px);
+    margin: 3px 6px;
+    padding: 4px 7px;
+    padding-bottom: 6px;
+    float: left;
+    list-style: none;
+    
+    font-weight: bold;
+  }
+}

+ 5 - 0
static/sora/css/sora/utilities.less

@@ -0,0 +1,5 @@
+// Utility classes
+// --------------------------------------------------
+.clickable {
+  cursor: pointer;
+}

+ 36 - 0
static/sora/css/sora/wells.less

@@ -0,0 +1,36 @@
+// Fancy Well
+// --------------------------------------------------
+.well {
+  background-color: lighten(@navbarBackground, 3%);
+  border: 1px solid darken(@navbarBackground, 15%);
+  .border-radius(4px);
+  .box-shadow(0px 0px 0px 3px darken(@navbarBackground, 5%));
+  padding: 24px 12px;
+  margin: 0px -12px;
+   
+  // Alerts are displayed other way inside wells 
+  .alert{
+    border-width: 0px 0px 1px 0px;
+    .border-radius(2px 2px 0px 0px);
+    
+    margin: -24px -12px;
+    margin-bottom: 12px;
+    padding: 12px;
+    font-weight: bold;
+  }
+  
+  // Fancy form actions
+  .form-actions {
+    background-color: lighten(@formActionsBackground, 2%);
+    .border-radius(0px 0px 3px 3px);
+    margin: -44px -12px;
+    margin-top: 12px;
+  }
+  
+  // Fix form button offset
+  .form-horizontal {
+    .form-actions {
+      padding-left: @horizontalComponentOffset + 12px;
+    }
+  }
+}

+ 282 - 0
static/sora/css/variables.less

@@ -0,0 +1,282 @@
+//
+// Variables
+// --------------------------------------------------
+
+
+// Global values
+// --------------------------------------------------
+
+
+// Grays
+// -------------------------
+@black:                 #000;
+@grayDarker:            #222;
+@grayDark:              #333;
+@gray:                  #555;
+@grayLight:             #999;
+@grayLighter:           #eee;
+@white:                 #fff;
+
+
+// Accent colors
+// -------------------------
+@blue:                  #049cdb;
+@blueDark:              #0064cd;
+@green:                 #46a546;
+@red:                   #9d261d;
+@yellow:                #ffc40d;
+@orange:                #f89406;
+@pink:                  #c3325f;
+@purple:                #7a43b6;
+
+
+// Scaffolding
+// -------------------------
+@bodyBackground:        darken(@white, 1%);
+@textColor:             @grayDark;
+
+
+// Links
+// -------------------------
+@linkColor:             #08c;
+@linkColorHover:        lighten(@linkColor, 10%);
+
+
+// Typography
+// -------------------------
+@sansFontFamily:        "Helvetica Neue", Helvetica, Arial, sans-serif;
+@serifFontFamily:       Georgia, "Times New Roman", Times, serif;
+@monoFontFamily:        Monaco, Menlo, Consolas, "Courier New", monospace;
+
+@baseFontSize:          14px;
+@baseFontFamily:        @sansFontFamily;
+@baseLineHeight:        20px;
+@altFontFamily:         @serifFontFamily;
+
+@headingsFontFamily:    inherit; // empty to use BS default, @baseFontFamily
+@headingsFontWeight:    bold;    // instead of browser default, bold
+@headingsColor:         inherit; // empty to use BS default, @textColor
+
+// Tables
+// -------------------------
+@tableBackground:                   transparent; // overall background-color
+@tableBackgroundAccent:             darken(@bodyBackground, 2%); // for striping
+@tableBackgroundHover:              darken(@bodyBackground, 4%); // for hover
+@tableBorder:                       #ddd; // table and cell border
+
+
+// Buttons
+// -------------------------
+@btnBackground:                     darken(@grayLighter, 7%);
+@btnBackgroundHighlight:            darken(@grayLighter, 5%);
+@btnBorder:                         #ccc;
+
+@btnPrimaryBackground:              @linkColor;
+@btnPrimaryBackgroundHighlight:     lighten(@btnPrimaryBackground, 8%);
+
+@btnInfoBackground:                 lighten(desaturate(@linkColor, 70%), 5%);
+@btnInfoBackgroundHighlight:        lighten(@btnInfoBackground, 12%);
+
+@btnSuccessBackground:              @green;
+@btnSuccessBackgroundHighlight:     lighten(@btnSuccessBackground, 12%);
+
+@btnWarningBackground:              darken(@orange, 2%);
+@btnWarningBackgroundHighlight:     lighten(@btnWarningBackground, 12%);
+
+@btnDangerBackground:               lighten(@red, 12%);
+@btnDangerBackgroundHighlight:      lighten(@btnDangerBackground, 12%);
+
+@btnInverseBackground:              @gray;
+@btnInverseBackgroundHighlight:     lighten(@btnInverseBackground, 16%);
+
+
+// Forms
+// -------------------------
+@inputBackground:               @white;
+@inputBorder:                   #ccc;
+@inputBorderRadius:             3px;
+@inputDisabledBackground:       @grayLighter;
+@formActionsBackground:         #f5f5f5;
+
+// Dropdowns
+// -------------------------
+@dropdownBackground:            @white;
+@dropdownBorder:                rgba(0,0,0,.2);
+@dropdownDividerTop:            #e5e5e5;
+@dropdownDividerBottom:         @white;
+
+@dropdownLinkColor:             @grayDark;
+@dropdownLinkColorHover:        @white;
+@dropdownLinkColorActive:       @dropdownLinkColor;
+
+@dropdownLinkBackgroundActive:  @linkColor;
+@dropdownLinkBackgroundHover:   @dropdownLinkBackgroundActive;
+
+
+
+// COMPONENT VARIABLES
+// --------------------------------------------------
+
+// Z-index master list
+// -------------------------
+// Used for a bird's eye view of components dependent on the z-axis
+// Try to avoid customizing these :)
+@zindexDropdown:          1000;
+@zindexPopover:           1010;
+@zindexTooltip:           1030;
+@zindexFixedNavbar:       1030;
+@zindexModalBackdrop:     1040;
+@zindexModal:             1050;
+
+
+// Sprite icons path
+// -------------------------
+@iconSpritePath:          "../img/glyphicons-halflings.png";
+@iconWhiteSpritePath:     "../img/glyphicons-halflings-white.png";
+
+
+// Input placeholder text color
+// -------------------------
+@placeholderText:         @grayLight;
+
+
+// Hr border color
+// -------------------------
+@hrBorder:                @grayLighter;
+
+
+// Horizontal forms & lists
+// -------------------------
+@horizontalComponentOffset:       180px;
+
+
+// Wells
+// -------------------------
+@wellBackground:                  #f5f5f5;
+
+
+// Navbar
+// -------------------------
+@navbarCollapseWidth:             979px;
+
+@navbarHeight:                    40px;
+@navbarBackground:                @bodyBackground;
+@navbarBackgroundHighlight:       darken(@bodyBackground, 10%);
+@navbarBorder:                    @white;
+
+@navbarText:                      @gray;
+@navbarLinkColor:                 @gray;
+@navbarLinkColorHover:            @grayDark;
+@navbarLinkColorActive:           @gray;
+@navbarLinkBackgroundHover:       darken(@bodyBackground, 5%);
+@navbarLinkBackgroundActive:      @navbarBackground;
+
+@navbarBrandColor:                @navbarLinkColor;
+
+// Inverted navbar
+@navbarInverseBackground:                lighten(@linkColor, 10%);
+@navbarInverseHeight:                    64px;
+@navbarInverseBackgroundHighlight:       lighten(@navbarInverseBackground, 5%);
+@navbarInverseBorder:                    lighten(@navbarInverseBackground, 3%);
+
+@navbarInverseText:                      @white;
+@navbarInverseLinkColor:                 @white;
+@navbarInverseLinkColorHover:            @white;
+@navbarInverseLinkColorActive:           @navbarInverseLinkColorHover;
+@navbarInverseLinkBackground:            darken(@navbarInverseBackground, 10%);
+@navbarInverseLinkBackgroundHover:       lighten(@navbarInverseBackground, 60%);
+@navbarInverseLinkBackgroundActive:      @navbarInverseBackground;
+
+@navbarInverseSearchBackground:          lighten(@navbarInverseBackground, 25%);
+@navbarInverseSearchBackgroundFocus:     @white;
+@navbarInverseSearchBorder:              @navbarInverseBackground;
+@navbarInverseSearchPlaceholderColor:    #ccc;
+
+@navbarInverseBrandColor:                @navbarInverseLinkColor;
+
+
+// Pagination
+// -------------------------
+@paginationBackground:                #fff;
+@paginationBorder:                    #ddd;
+@paginationActiveBackground:          #f5f5f5;
+
+
+// Hero unit
+// -------------------------
+@heroUnitBackground:              @grayLighter;
+@heroUnitHeadingColor:            inherit;
+@heroUnitLeadColor:               inherit;
+
+
+// Form states and alerts
+// -------------------------
+@warningText:             #c09853;
+@warningBackground:       #fcf8e3;
+@warningBorder:           darken(spin(@warningBackground, -10), 3%);
+
+@errorText:               #b94a48;
+@errorBackground:         #f2dede;
+@errorBorder:             darken(spin(@errorBackground, -10), 3%);
+
+@successText:             #468847;
+@successBackground:       #dff0d8;
+@successBorder:           darken(spin(@successBackground, -10), 5%);
+
+@infoText:                #3a87ad;
+@infoBackground:          #d9edf7;
+@infoBorder:              darken(spin(@infoBackground, -10), 7%);
+
+
+// Tooltips and popovers
+// -------------------------
+@tooltipColor:            #fff;
+@tooltipBackground:       #000;
+@tooltipArrowWidth:       5px;
+@tooltipArrowColor:       @tooltipBackground;
+
+@popoverBackground:       #fff;
+@popoverArrowWidth:       10px;
+@popoverArrowColor:       #fff;
+@popoverTitleBackground:  darken(@popoverBackground, 3%);
+
+// Special enhancement for popovers
+@popoverArrowOuterWidth:  @popoverArrowWidth + 1;
+@popoverArrowOuterColor:  rgba(0,0,0,.25);
+
+
+
+// GRID
+// --------------------------------------------------
+
+
+// Default 940px grid
+// -------------------------
+@gridColumns:             12;
+@gridColumnWidth:         60px;
+@gridGutterWidth:         20px;
+@gridRowWidth:            (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
+
+// 1200px min
+@gridColumnWidth1200:     70px;
+@gridGutterWidth1200:     30px;
+@gridRowWidth1200:        (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
+
+// 768px-979px
+@gridColumnWidth768:      42px;
+@gridGutterWidth768:      20px;
+@gridRowWidth768:         (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
+
+
+// Fluid grid
+// -------------------------
+@fluidGridColumnWidth:    percentage(@gridColumnWidth/@gridRowWidth);
+@fluidGridGutterWidth:    percentage(@gridGutterWidth/@gridRowWidth);
+
+// 1200px min
+@fluidGridColumnWidth1200:     percentage(@gridColumnWidth1200/@gridRowWidth1200);
+@fluidGridGutterWidth1200:     percentage(@gridGutterWidth1200/@gridRowWidth1200);
+
+// 768px-979px
+@fluidGridColumnWidth768:      percentage(@gridColumnWidth768/@gridRowWidth768);
+@fluidGridGutterWidth768:      percentage(@gridGutterWidth768/@gridRowWidth768);

BIN
static/sora/img/glyphicons-halflings-white.png


BIN
static/sora/img/glyphicons-halflings.png


+ 6 - 0
static/sora/js/bootstrap.min.js

@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){e(function(){"use strict";e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(function(){e("body").on("click.alert.data-api",t,n.prototype.close)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(function(){e("body").on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f=e.Event("slide",{relatedTarget:i[0]});this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u]();if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(function(){e("body").on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=!i.data("modal")&&e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(function(){e("body").on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})})}(window.jQuery),!function(e){"use strict";function r(){i(e(t)).removeClass("open")}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a<r.length-1&&a++,~a||(a=0),r.eq(a).focus()}},e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e(function(){e("html").on("click.dropdown.data-api touchstart.dropdown.data-api",r),e("body").on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;e("body").addClass("modal-open"),this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1).focus(),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.trigger("shown")}):t.$element.trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,e("body").removeClass("modal-open"),this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(e){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(function(){e("body").on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.remove().css({top:0,left:0,display:"block"}).appendTo(t?this.$element:document.body),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.css(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).remove()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.remove()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.remove(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0,html:!0}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active a").last()[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(function(){e("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),(e.browser.chrome||e.browser.webkit||e.browser.msie)&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e(function(){e("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);

+ 4 - 0
static/sora/js/jquery-1.7.2.min.js

@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);

+ 138 - 0
static/sora/js/jquery.toggle.buttons.js

@@ -0,0 +1,138 @@
+!function ($) {
+  "use strict";
+  // version: 1.6
+  // by Mattia Larentis - follow me on twitter! @SpiritualGuru
+
+  $.fn.toggleButtons = function (method) {
+    var $element
+      , $labelEnabled
+      , options
+      , active
+      , styleActive
+      , styleDisabled
+      , animationCss
+      , transitionSpeed = 0.05
+      , defaultSpeed = 0.05
+      , methods = {
+        init: function (opt) {
+          this.each(function () {
+            $element = $(this);
+
+            options = $.extend({}, $.fn.toggleButtons.defaults, opt);
+
+            $element.attr("data-enabled", options.label.enabled === undefined ? "ON" : options.label.enabled);
+            $element.attr("data-disabled", options.label.disabled === undefined ? "OFF " : options.label.disabled);
+
+            $element.addClass('toggle-button');
+
+            $labelEnabled = $('<label></label>').attr('for', $element.find('input').attr('id'));
+            $element.append($labelEnabled);
+
+            if (options.animated) {
+              $element.addClass('toggle-button-animated');
+
+              if (options.transitionSpeed !== undefined)
+                if (/^(\d*%$)/.test(options.transitionSpeed))  // is a percent value?
+                  transitionSpeed = defaultSpeed * parseInt(options.transitionSpeed) / 100;
+                else
+                  transitionSpeed = options.transitionSpeed;
+
+              animationCss = ["-webkit-", "-moz-", "-o-", ""];
+              $(animationCss).each(function () {
+                $element.find('label').css(this + 'transition', 'all ' + transitionSpeed + 's');
+              });
+            }
+
+            $element.css('width', options.width);
+
+            active = $element.find('input').is(':checked');
+
+            if (!active)
+              $element.addClass('disabled');
+
+            if($element.find('input').is(':disabled'))
+              $element.addClass('deactivate');
+
+            styleActive = options.style.enabled === undefined ? "" : options.style.enabled;
+            styleDisabled = options.style.disabled === undefined ? "" : options.style.disabled;
+
+            if (active && styleActive !== undefined)
+              $element.addClass(styleActive);
+            if (!active && styleDisabled !== undefined)
+              $element.addClass(styleDisabled);
+
+            $element.on('click', function (e) {
+              if ($(e.target).is('input'))
+                return true;
+
+              e.stopPropagation();
+              $(this).find('label').click();
+            });
+
+            $element.find('input').on('change', function(e) {
+              e.stopPropagation();
+              e.preventDefault();
+
+              $element.toggleButtons("toggleState", true);
+            });
+
+            $element.find('label').on('click', function (e) {
+              e.stopPropagation();
+              e.preventDefault();
+
+              if($element.is('.deactivate'))
+                return true;
+
+              $element = $(this).parent();
+
+              $element
+                .delay(transitionSpeed * 500).queue(function () {
+                  $(this).toggleClass('disabled')
+                    .toggleClass(styleActive)
+                    .toggleClass(styleDisabled)
+                    .dequeue();
+                });
+
+              active = !($element.find('input').is(':checked'));
+
+              $element.find('input').attr('checked', active);
+              options.onChange($element, active, e);
+            });
+          });
+        },
+        toggleActivation: function () {
+          $(this).toggleClass('deactivate');
+        },
+        toggleState: function(clickOnAnotherLabel) {
+          if(clickOnAnotherLabel !== undefined)
+            $(this).toggleClass('disabled');
+          else
+            $(this).find('label').click();
+        }
+      };
+
+    if (methods[method]) {
+      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+    } else if (typeof method === 'object' || !method) {
+      return methods.init.apply(this, arguments);
+    } else {
+      $.error('Method ' + method + ' does not exist on jQuery.tooltip');
+    }
+  };
+
+  $.fn.toggleButtons.defaults = {
+    onChange: function () {
+    },
+    width: 100,
+    animated: true,
+    transitionSpeed: undefined,
+    label: {
+      enabled: undefined,
+      disabled: undefined
+    },
+    style: {
+      enabled: undefined,
+      disabled: undefined
+    }
+  };
+}($);

+ 57 - 0
static/sora/js/sora.js

@@ -0,0 +1,57 @@
+$(function () {
+	// Register tooltips
+	$('.tooltip-top').tooltip({placement: 'top'})
+	$('.tooltip-bottom').tooltip({placement: 'bottom'})
+	$('.tooltip-left').tooltip({placement: 'left'})
+	$('.tooltip-right').tooltip({placement: 'right'})
+	
+	// Register popovers
+	$('.popover-top').popover({placement: 'top'})
+	$('.popover-bottom').popover({placement: 'bottom'})
+	$('.popover-left').popover({placement: 'left'})
+	$('.popover-right').popover({placement: 'right'})
+	
+	// Start all dropdowns
+	$('.dropdown-toggle').dropdown()
+	
+	// Dont hide clickable dropdowns
+	$('.dropdown-clickable').on('click', function (e) {
+	  e.stopPropagation()
+	});
+	
+	// Checkbox Group Master
+	$('input.checkbox-master').live('click', function(){
+		if($(this).is(':checked')){
+			$('input.checkbox-member').attr("checked" ,"checked");
+		}
+		else
+		{
+			$('input.checkbox-member').removeAttr('checked');
+		}
+	});
+	
+	// Checkbox Group Member
+	$('input.checkbox-member').live('click', function(){
+		if(!$(this).is(':checked')){
+			$('input.checkbox-master').removeAttr('checked');
+		}
+	});
+	
+	// Check Confirmation on links
+	$('a.confirm').live('click', function(){
+		var decision = confirm(jQuery.data(this, 'jsconfirm'));
+		return decision
+	});
+	
+	// Check Confirmation on forms
+	$('form.confirm').live('submit', function(){
+		data = $(this).data();
+		var decision = confirm(data.jsconfirm);
+		return decision
+	});
+	
+	// Go back one page
+	$('.go-back').on('click', function (e) {
+	  history.go(-1)
+	})
+})

+ 76 - 0
templates/500.html

@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Whoops! Something has gone wrong...</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <style type="text/css">
+	    /*!
+		 * Bootstrap v2.0.4
+		 *
+		 * Copyright 2012 Twitter, Inc
+		 * Licensed under the Apache License v2.0
+		 * http://www.apache.org/licenses/LICENSE-2.0
+		 *
+		 * Designed and built with all the love in the world @twitter by @mdo and @fat.
+		 */
+		.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
+		.clearfix:after{clear:both;}
+		.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
+		.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
+		html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
+		a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+		a:hover,a:active{outline:0;}
+		img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
+		#map_canvas img{max-width:none;}
+		body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#E1E2E3;background-color:#292B2C;text-shadow:1px 1px 0px #000}
+		a{color:#2FB8CD;text-decoration:none;}
+		a:hover{color:#4DD9EF;text-decoration:underline;}
+		.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
+		.row:after{clear:both;}
+		[class*="span"]{float:left;margin-left:20px;}
+		.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+		.span8{width:620px;}
+		.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
+		.container:after{clear:both;}
+		p{margin:0 0 9px;}p small{font-size:11px;color:#999999;}
+		.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
+		h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
+		h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
+		h2{margin-top:32px;font-size:24px;line-height:36px;}h2 small{font-size:18px;}
+		@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.762430939%;*margin-left:2.709239449638298%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:99.999999993%;*width:99.9468085036383%;} .row-fluid .span11{width:91.436464082%;*width:91.38327259263829%;} .row-fluid .span10{width:82.87292817100001%;*width:82.8197366816383%;} .row-fluid .span9{width:74.30939226%;*width:74.25620077063829%;} .row-fluid .span8{width:65.74585634900001%;*width:65.6926648596383%;} .row-fluid .span7{width:57.182320438000005%;*width:57.129128948638304%;} .row-fluid .span6{width:48.618784527%;*width:48.5655930376383%;} .row-fluid .span5{width:40.055248616%;*width:40.0020571266383%;} .row-fluid .span4{width:31.491712705%;*width:31.4385212156383%;} .row-fluid .span3{width:22.928176794%;*width:22.874985304638297%;} .row-fluid .span2{width:14.364640883%;*width:14.311449393638298%;} .row-fluid .span1{width:5.801104972%;*width:5.747913482638298%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564%;*margin-left:2.510911074638298%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145300001%;*width:91.3997999636383%;} .row-fluid .span10{width:82.905982906%;*width:82.8527914166383%;} .row-fluid .span9{width:74.358974359%;*width:74.30578286963829%;} .row-fluid .span8{width:65.81196581200001%;*width:65.7587743226383%;} .row-fluid .span7{width:57.264957265%;*width:57.2117657756383%;} .row-fluid .span6{width:48.717948718%;*width:48.6647572286383%;} .row-fluid .span5{width:40.170940171000005%;*width:40.117748681638304%;} .row-fluid .span4{width:31.623931624%;*width:31.5707401346383%;} .row-fluid .span3{width:23.076923077%;*width:23.0237315876383%;} .row-fluid .span2{width:14.529914530000001%;*width:14.4767230406383%;} .row-fluid .span1{width:5.982905983%;*width:5.929714493638298%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}
+		.page-head{background-color:#0C0D0E;border-bottom:1px solid #36393A;padding:64px 0px}
+		.page-message{padding-top:8px}
+		footer{margin-top:24px;color:#666;text-shadow:1px 1px 0px #1E1F20}
+		footer a:link, footer a:active, footer a:visited{color:#125B66}
+		footer a:hover{color:#1E7E8C}
+    </style>
+  </head>
+  <body>
+  <div class="page-head">
+  	<div class="container">
+  	  <div class="row">
+  	  	<div class="span8 offset2">
+          <h1>Whoops! Something has gone wrong...</h1>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="page-message">
+  	<div class="container">
+  	  <div class="row">
+  	  	<div class="span8 offset2">
+          <h2>What should I do now?</h2>
+          <p>The problem may be temporary, so let&acute;s start with trying to <a href="javascript:location.reload(true)">refresh the page</a>.</p>
+          <h2>I still see this page!</h2>
+          <p>Our servers have been attacked by Error Beasts. We are doing our best to catch them all, but it will take some time before we chase down every single Error Beast.</p>
+          <p>We apologize for the inconvenience.</p>
+          <footer>
+            <p>This page has been generated by <a href="http:\\misago-project.org">Misago forum software</a></p>
+          </footer>
+        </div>
+      </div>
+    </div>
+  </div>
+  </body>
+</html>

+ 9 - 0
templates/_bootstrap/field_snippet.html

@@ -0,0 +1,9 @@
+{% if field.widget == "text" %}
+{% include "_bootstrap/fields/text.html" %}
+{% endif %}
+{% if field.widget == "recaptcha" %}
+{% include "_bootstrap/fields/recaptcha.html" %}
+{% endif %}
+{% if field.widget == "checkbox" %}
+{% include "_bootstrap/fields/checkbox.html" %}
+{% endif %}

+ 4 - 0
templates/_bootstrap/fields/checkbox.html

@@ -0,0 +1,4 @@
+<label class="checkbox">
+  <input id="{{ field.html_id }}" name="{{ field.html_name }}" type="checkbox"{% if field.required%} required="required"{% endif %}{% if field.value %} checked="checked"{% endif %}>
+  {{ field.help_text }}
+</label>

+ 8 - 0
templates/_bootstrap/fields/recaptcha.html

@@ -0,0 +1,8 @@
+<div class="recaptcha-wrapper">
+  <script type="text/javascript" src="{{ field.attrs.api_server }}/challenge?k={{ field.attrs.public_key }}{{ field.attrs.error_param }}"></script>
+  <noscript>
+    <iframe src="{{ field.attrs.api_server }}/noscript?k={{ field.attrs.public_key }}{{ field.attrs.error_param }}" height="300" width="500" frameborder="0"></iframe><br>
+    <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
+    <input type='hidden' name='recaptcha_response_field' value='manual_challenge'>
+  </noscript>
+</div>

+ 6 - 0
templates/_bootstrap/fields/text.html

@@ -0,0 +1,6 @@
+<input id="{{ field.html_id }}" name="{{ field.html_name }}" type="{{ field.attrs.type }}" class="span{% spaceless %}
+{% if horizontal %}
+  {% widthratio field.width 140 max_width %}
+{% else %}
+  {% widthratio field.width 100 max_width %}
+{% endif %}{% endspaceless %}"{%if field.attrs.max_length %} maxlength="{{ field.attrs.max_length }}"{% endif %}{% if field.placeholder %} placeholder="{{ field.placeholder }}"{% endif %}{% if field.has_value %} value="{{ field.value }}"{% endif %}{% if field.required%} required="required"{% endif %}>

+ 0 - 0
templates/_bootstrap/fields/textarea.html


+ 12 - 0
templates/_bootstrap/form_snippet.html

@@ -0,0 +1,12 @@
+            <fieldset>
+              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">{% for field in form.hidden %}
+              <input type="hidden" name="{{ field.id }}" value="{{ field.value }}">{% endfor %}{% for fieldset in form.fieldsets %}{% if fieldset.legend %}
+              <legend><div>{{ fieldset.legend }}{% if fieldset.help %} <span>{{ fieldset.help }}</span>{% endif %}</div></legend>{% endif %}
+              {% for field in fieldset.fields %}
+              	{% include "_bootstrap/row_snippet.html" %}
+              	{% if field.endrow %}
+              {% if not field.last %}
+              {% endif %}{% endif %}{% endfor %}{% if not fieldset.last %}
+              <hr>
+            {% endif %}</fieldset>{% if not fieldset.last %}
+            <fieldset>{% endif %}{% endfor %}

+ 17 - 0
templates/_bootstrap/row_snippet.html

@@ -0,0 +1,17 @@
+                <div class="control-group{% if field.errors %} error{% endif %}">{% if field.label %}
+                  <label class="control-label" for="{{ field.html_id }}">{{ field.label }}:</label>{% endif %}{% if field.nested %}
+                  <div class="controls controls-nested">
+                  	<div class="row">
+                    {% for subfield in field.nested %}{% with field=subfield nested=1 %}                     	
+                  	  <div class="span{% widthratio field.width 100 max_width %}">{% include "_bootstrap/field_snippet.html" %}</div>
+                    {% endwith %}{% endfor %}
+                    </div>{% for error in field.errors %}
+                    <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.widget != "checkbox" and field.help_text %}
+                    <p class="help-block">{{ field.help_text }}</p>{% endif %}
+                  </div>{% else %}
+                  <div class="controls">
+					{% include "_bootstrap/field_snippet.html" %}{% for error in field.errors %}
+                    <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.widget != "checkbox" and field.help_text %}
+                    <p class="help-block">{{ field.help_text }}</p>{% endif %}
+                  </div>{% endif %}
+                </div>

+ 17 - 0
templates/_email/auth/activation_0_html.html

@@ -0,0 +1,17 @@
+{% extends "_email/base_html.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}You are receiving this message because you have used this email address to sign up on our forums.{% endtrans %}</p>
+
+<h2 style="margin: 12px 0px; padding: 0px;">Your Account Data</h2>
+<div style="background-color: #EFEFEF; border-radius: 3px; margin: 0px; padding: 8px;">
+  <strong>{% trans %}Username{% endtrans %}:</strong> {{ user.username }}<br>
+  <strong>{% trans %}E-mail{% endtrans %}:</strong> {{ user.email }}<br>
+  <strong>{% trans %}Password{% endtrans %}:</strong> {{ password }}
+</div>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}This is only time you will receive your current password. Due to security reasons we dont store members passwords as they are entered on registration, instead we encrypt those password in un-reversible manner to keep them safe.{% endtrans %}</p>
+{% block activation_instructions %}{% endblock %}
+<p style="margin: 12px 0px; margin-bottom 0px; padding: 0px;">{% trans %}Welcome aboard!{% endtrans %}<br><a href="{{ board_address }}">{% if settings.board_index_title %}{{ settings.board_index_title }}{% else %}{{ settings.board_name }}{% endif %}</a></p>
+{% endblock %}

+ 14 - 0
templates/_email/auth/activation_0_plain.html

@@ -0,0 +1,14 @@
+{% extends "_email/base_plain.html" %}
+{% load i18n %}
+{% load url from future %}
+{% block content %}
+{% trans %}You are receiving this message because you have used this email address to sign up on our forums.{% endtrans %}
+
+Your Account Data
+-----------------
+{% trans %}Username{% endtrans %}:    {{ user.username }}
+{% trans %}E-mail{% endtrans %}:    {{ user.email }}
+{% trans %}Password{% endtrans %}:    {{ password }}
+
+{% trans %}This is only time you will receive your current password. Due to security reasons we dont store members passwords as they are entered on registration, instead we encrypt those password in un-reversible manner to keep them safe.{% endtrans %}{% block activation_instructions %}{% endblock %}
+{% endblock %}

+ 11 - 0
templates/_email/auth/activation_1_html.html

@@ -0,0 +1,11 @@
+{% extends "_email/auth/activation_0_html.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block activation_instructions %}
+<h2 style="margin: 12px 0px; padding: 0px;">Account Activation</h2>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}We require our members to prove validity of e-mail address used during registration. To prove that you are owner of e-mail address used to create this account, click the link below:{% endtrans %}</p>
+<div style="background-color: #EFEFEF; border-radius: 3px; margin: 0px; padding: 8px; text-align: center;">
+  <a href="{{ board_address }}{% url 'activate' username=user.username_slug user=user.id token=user.token %}" style="color: #3465a4; font-size: 150%;">{% trans %}Activate my account{% endtrans %}!</a>
+</div>
+{% endblock %}

+ 11 - 0
templates/_email/auth/activation_1_plain.html

@@ -0,0 +1,11 @@
+{% extends "_email/auth/activation_0_plain.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block activation_instructions %}
+
+Account Activation
+------------------
+{% trans %}We require our members to prove validity of e-mail address used during registration. To prove that you are owner of e-mail address used to create this account, click the link below:{% endtrans %}
+{{ board_address }}{% url 'activate' username=user.username user=user.id token=user.token %}
+{% endblock %}

+ 8 - 0
templates/_email/auth/activation_2_html.html

@@ -0,0 +1,8 @@
+{% extends "_email/auth/activation_0_html.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block activation_instructions %}
+<h2 style="margin: 12px 0px; padding: 0px;">Account Activation</h2>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}Your account will remain inactive until Board Administrator accepts it. Depending on amount of new registrations this may take few minutes or few days. Thanks for patience!{% endtrans %}</p>
+{% endblock %}

+ 10 - 0
templates/_email/auth/activation_2_plain.html

@@ -0,0 +1,10 @@
+{% extends "_email/auth/activation_0_plain.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block activation_instructions %}
+
+Account Activation
+------------------
+{% trans %}Your account will remain inactive until Board Administrator accepts it. Depending on amount of new registrations this may take few minutes or few days. Thanks for patience!{% endtrans %}
+{% endblock %}

+ 12 - 0
templates/_email/auth/activation_resend_html.html

@@ -0,0 +1,12 @@
+{% extends "_email/base_html.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}You are receiving this message because you have requested new activation e-mail.{% endtrans %}</p>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}To activate your account, click the link below:{% endtrans %}</p>
+<div style="background-color: #EFEFEF; border-radius: 3px; margin: 0px; padding: 8px; text-align: center;">
+  <a href="{{ board_address }}{% url 'activate' username=user.username_slug user=user.id token=user.token %}" style="color: #3465a4; font-size: 150%;">{% trans %}Activate my account{% endtrans %}!</a>
+</div>
+<p style="margin: 12px 0px; margin-bottom 0px; padding: 0px;">{% trans %}Welcome aboard!{% endtrans %}<br><a href="{{ board_address }}">{% if settings.board_index_title %}{{ settings.board_index_title }}{% else %}{{ settings.board_name }}{% endif %}</a></p>
+{% endblock %}

+ 10 - 0
templates/_email/auth/activation_resend_plain.html

@@ -0,0 +1,10 @@
+{% extends "_email/base_plain.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+{% trans %}You are receiving this message because you have requested new activation e-mail.{% endtrans %}
+
+{% trans %}To activate your account, click the link below:{% endtrans %}
+{{ board_address }}{% url 'activate' username=user.username_slug user=user.id token=user.token %}
+{% endblock %}

+ 12 - 0
templates/_email/auth/reset_confirm_html.html

@@ -0,0 +1,12 @@
+{% extends "_email/base_html.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}You are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}</p>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}To confirm that you want to reset your account's password with new one click the link below:{% endtrans %}</p>
+<div style="background-color: #EFEFEF; border-radius: 3px; margin: 0px; padding: 8px; text-align: center;">
+  <a href="{{ board_address }}{% url 'reset_password' username=user.username_slug user=user.id token=user.token %}" style="color: #3465a4; font-size: 150%;">{% trans %}Reset my password{% endtrans %}</a>
+</div>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}Your new password will be sent back to you in next message once you click confirmation link.{% endtrans %}</p>
+{% endblock %}

+ 11 - 0
templates/_email/auth/reset_confirm_plain.html

@@ -0,0 +1,11 @@
+{% extends "_email/base_plain.html" %}
+{% load i18n %}
+{% load url from future %}
+{% block content %}
+{% trans %}You are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}
+
+{% trans %}To confirm that you want to reset your account's password with new one click the link below:{% endtrans %}
+{{ board_address }}{% url 'reset_password' username=user.username_slug user=user.id token=user.token %}
+
+{% trans %}Your new password will be sent back to you in next message once you click confirmation link.{% endtrans %}
+{% endblock %}

+ 13 - 0
templates/_email/auth/reset_new_html.html

@@ -0,0 +1,13 @@
+{% extends "_email/base_html.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}You are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}</p>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}Your new password:{% endtrans %}</p>
+<div style="background-color: #EFEFEF; border-radius: 3px; margin: 0px; padding: 8px; text-align: center; font-size: 200%;">
+  {{ password }}
+</div>
+<p style="margin: 12px 0px; padding: 0px;">{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
+<a href="{{ board_address }}{% url 'sign_in' %}" style="color: #3465a4; font-size: 150%;">{% trans %}Sign In{% endtrans %}</a>
+{% endblock %}

+ 11 - 0
templates/_email/auth/reset_new_plain.html

@@ -0,0 +1,11 @@
+{% extends "_email/base_plain.html" %}
+{% load i18n %}
+{% load url from future %}
+{% block content %}
+{% trans %}You are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}
+
+{% trans %}Your new password:{% endtrans %} {{ password }}
+
+{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
+{{ board_address }}{% url 'sign_in' %}
+{% endblock %}

+ 26 - 0
templates/_email/base_html.html

@@ -0,0 +1,26 @@
+{% load url from future %}
+{% load i18n %}
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title></title>
+  </head>
+  <body style="background: #F0F0F0; padding: 24px 18px; color: #333;">
+    <div style="max-width: 600px; margin: 0px auto;">
+      <div style="background: #FEFEFE; border-radius: 3px;">
+        <div style="padding: 8px 16px; background: #3465a4; border: 1px solid #204a87; border-radius: 3px 3px 0px 0px;">
+          <h1 style="margin: 0px; padding: 0px; font-weight: normal;"><a href="{{ board_address }}" style="color: #FEFEFE; text-decoration: none;">{{ settings.board_name }} <span style="color: #729fcf;">{{ board_address }}</span></a></h1>
+        </div>
+        <div style="border: 1px solid #babdb6; border-top: none; border-radius: 0px 0px 3px 3px; padding: 12px 16px;">
+          <h1 style="margin: 0px; padding: 0px; margin-bottom: 8px; color: #454545;">{% block title %}{% trans username=user.username %}Hello, {{ username }}{% endtrans %}!{% endblock %}</h1>
+  	      {% block content %}{% endblock %}
+        </div>
+      </div>
+      <div style="color: #999; padding: 12px 16px; padding-bottom: 0px;">{% if settings.email_footnote %}
+        <p style="margin: 0px; padding: 0px; color: #666;">{{ settings.email_footnote }}</p>{% endif %}
+        <p style="margin: 0px; padding: 0px;">{% trans %}This is an automatically generated email.{% endtrans %}</p>
+      </div>
+    </div>
+  </body>
+</html>

+ 13 - 0
templates/_email/base_plain.html

@@ -0,0 +1,13 @@
+{% load url from future %}
+{% load i18n %}
+
+{% trans username=user.username %}Hello, {{ username }}{% endtrans %}
+
+{% block content %}{% endblock %}
+
+========================================{% if settings.email_footnote %}
+{{ settings.email_footnote }}
+----------------------------------------
+{% endif %}
+{{ settings.board_name }} - {{ board_address }}
+{% trans %}This is an automatically generated email.{% endtrans %}

+ 187 - 0
templates/_forms.html

@@ -0,0 +1,187 @@
+{% load i18n %}
+
+{# Render whole form macro #}
+{%- macro form_widget(form, horizontal=false, width=12) -%}
+<fieldset>
+  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">{% for field in form.hidden %}
+  <input type="hidden" name="{{ field.id }}" value="{{ field.value }}">{% endfor %}{% for fieldset in form.fieldsets %}{% if fieldset.legend %}
+  <legend><div>{{ fieldset.legend }}{% if fieldset.help %} <span>{{ fieldset.help }}</span>{% endif %}</div></legend>{% endif %}
+  {% for field in fieldset.fields %}
+    {{ row_widget(field, horizontal=horizontal, width=width) }}
+  {% endfor %}
+</fieldset>{% if not fieldset.last %}
+<fieldset>{% endif %}{% endfor %}
+{%- endmacro -%}
+
+
+{# Render form row macro #}
+{%- macro row_widget(field, horizontal=false, width=12) -%}
+  <div class="control-group{% if field.errors %} error{% endif %}">{% if field.label %}
+    <label class="control-label" for="{{ field.html_id }}">{{ field.label }}:</label>{% endif %}{% if field.nested %}
+    <div class="controls controls-nested">
+    	<div class="row">
+      {% for subfield in field.nested %}                    	
+    	  <div class="span{{ widthratio(subfield.width, 100, width) }}">{{ field_widget(subfield, horizontal=horizontal, width=width, nested=true) }}</div>
+      {% endfor %}
+      </div>{% for error in field.errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.widget != "checkbox" and field.help_text %}
+      <p class="help-block">{{ field.help_text }}</p>{% endif %}
+    </div>{% else %}
+    <div class="controls">
+      {{ field_widget(field, horizontal=horizontal, width=width) }}{% for error in field.errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.widget != "checkbox" and field.help_text %}
+      <p class="help-block">{{ field.help_text }}</p>{% endif %}
+    </div>{% endif %}
+  </div>
+{%- endmacro -%}
+
+{# Render form field macro #}
+{%- macro field_widget(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+
+{%- if field.widget == "checkbox" -%}
+{{ input_checkbox(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "date" -%}
+{{ input_date(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif %}
+
+{%- if field.widget == "recaptcha" -%}
+{{ input_recaptcha(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "radio_select" -%}
+{{ input_radio_select(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "select" -%}
+{{ input_select(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "checkbox_select_multiple" -%}
+{{ input_checkbox_select_multiple(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "text" -%}
+{{ input_text(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "textarea" -%}
+{{ input_textarea(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "yes_no_switch" -%}
+{{ input_yes_no_switch(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif %}
+{%- endmacro -%}
+
+
+{# Render form field attributes macro #}
+{%- macro field_attrs(attrs={}, extras=[]) -%}
+{% for attribute in attrs %} {{ attribute }}="{{ attrs[attribute] }}"{% endfor %}{% for extra in extras %} {{ extra }}{% endfor %}
+{%- endmacro -%}
+
+
+{# Render form field class attribute macro #}
+{%- macro field_classes(classes=[]) -%}
+{% if classes %} class="{% for class in classes %}{% if not loop.first %} {% endif %}{{ class }}{% endfor %}"{% endif %}
+{%- endmacro -%}
+
+
+{# Checkbox input #}
+{%- macro input_checkbox(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+<label class="checkbox">
+  <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}" value="1"{% if field.value %} checked="checked"{% endif %}>
+  {% if field.inline is defined %}{{ field.inline }}{% else %}{{ field.label }}{% endif %}
+</label>
+{%- endmacro -%}
+
+
+{# Date input #}
+{%- macro input_date(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 140, width)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<input type="text"{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}{% if field.has_value %} value="{{ field.value }}"{% endif %}>
+{%- endmacro -%}
+
+{# Multiple Checkbox input #}
+{%- macro input_checkbox_select_multiple(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- do classes.append('select-multiple') -%}
+<div{{ field_classes(classes) }}>{% for choice in field.choices %}
+  <label class="checkbox">
+    <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}_{{ choice[0] }}" value="{{ choice[0] }}"{% if field.value and choice[0] in field.value %} checked="checked"{% endif %}>
+    {{ choice[1] }}
+  </label>{% endfor %}
+</div>
+{%- endmacro -%}
+
+
+{# Recaptcha input #}
+{%- macro input_recaptcha(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+RECAPTCHA!
+{%- endmacro -%}
+
+
+{# RadioSelect input #}
+{%- macro input_radio_select(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- do classes.append('radio-group') -%}
+<div{{ field_classes(classes) }}>{% for choice in field.choices %}
+  <label class="radio">
+    <input type="radio" name="{{ field.html_name }}" id="{{ field.html_id }}{{ choice[0] }}" value="{{ choice[0] }}"{% if field.value == choice[0] %} checked="checked"{% endif %}>
+    {{ choice[1] }}
+  </label>{% endfor %}
+</div>
+{%- endmacro -%}
+
+
+{# Select input #}
+{%- macro input_select(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal %}
+  {%- do classes.append('span' ~ widthratio(field.width, 140, width)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<select{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}>{% for choice in field.choices %}
+  <option value="{{ choice[0] }}"{% if field.value == choice[0] %} selected="selected"{% endif %}>{{ choice[1] }}</option>{% endfor %}
+</select>
+{%- endmacro -%}
+
+
+{# Text/password input #}
+{%- macro input_text(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 140, width)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<input{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}{% if field.attrs.type != 'password' and field.has_value %} value="{{ field.value }}"{% endif %}>
+{%- endmacro -%}
+
+
+{# Textarea input #}
+{%- macro input_textarea(field, attrs={'rows': 4}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 140, width)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<textarea{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}>{% if field.has_value %}{{ field.value }}{% endif %}</textarea>
+{%- endmacro -%}
+
+{# YesNoSwitch input #}
+{%- macro input_yes_no_switch(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- do classes.append('yes-no-switch') -%}
+<div{{ field_classes(classes) }}>
+  <input name="{{ field.html_name }}" id="{{ field.html_id }}" type="checkbox" value="1"{% if field.value %} checked="checked"{% endif %}>
+</div>
+{%- endmacro -%}

+ 8 - 0
templates/_message/auth/activated_new.html

@@ -0,0 +1,8 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p>{% trans username=message.user.username %}Welcome aboard, {{ username }}!{% endtrans %}</p>
+  <p class="protip">{% trans %}Your account has been successfully activated.{% endtrans %}</p>
+{% endblock %}

+ 8 - 0
templates/_message/auth/activated_password.html

@@ -0,0 +1,8 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p>{% trans username=message.user.username %}Welcome back, {{ username }}!{% endtrans %}</p>
+  <p class="protip">{% trans %}Your account has been successfully activated.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/auth/activation_admin.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p>{% trans username=message.user.username %}{{ username }}, Board Administrator has not yet activated your account. Please try again later.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/auth/activation_not_required.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p>{% trans username=message.user.username %}{{ username }}, your account is already active.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/auth/activation_only_by_admin.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p>{% trans username=message.user.username %}{{ username }}, only administrator can activate your account.{% endtrans %}</p>
+{% endblock %}

+ 9 - 0
templates/_message/auth/activation_required.html

@@ -0,0 +1,9 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}{{ username }}, your account has to be activated in order for you to be able to request new password.{% endtrans %}</strong></p>
+  <p class="protip"><a href="{% url 'send_activation' %}">{% trans %}Click here if you haven't received activation e-mail.{% endtrans %}</a></p>
+{% endblock %}

+ 8 - 0
templates/_message/auth/activation_resent.html

@@ -0,0 +1,8 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans %}Check your inbox!{% endtrans %}</strong></p>
+  <p>{% trans username=message.user.username, email=message.user.email %}{{ username }}, we have mailed new activation instructions to your email at {{ email }}.{% endtrans %}</p>
+{% endblock %}

+ 9 - 0
templates/_message/auth/activation_user.html

@@ -0,0 +1,9 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}{{ username }}, you have to activate your account before you will be able to sign in.{% endtrans %}</strong></p>
+  <p class="protip"><a href="{% url 'send_activation' %}">{% trans %}Click here if you haven't received activation e-mail.{% endtrans %}</a></p>
+{% endblock %}

+ 6 - 0
templates/_message/auth/invalid_confirmation_activation.html

@@ -0,0 +1,6 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <p>{% trans %}Activation link is invalid. Please check your inbox and request new activation e-mail if necessary.{% endtrans %}</p>
+{% endblock %}

+ 6 - 0
templates/_message/auth/invalid_confirmation_password.html

@@ -0,0 +1,6 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <p>{% trans %}Confirmation link is invalid. Please check your inbox and request new confirmation e-mail if necessary.{% endtrans %}</p>
+{% endblock %}

+ 8 - 0
templates/_message/auth/password_reset_confirm.html

@@ -0,0 +1,8 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans email=message.user.email %}Password reset confirmation has been sent to {{ email }}.{% endtrans %}</strong></p>
+  <p>{% trans %}You have to confirm your request for new password by clicking confirmation link in message we have sent to you.{% endtrans %}</p>
+{% endblock %}

+ 9 - 0
templates/_message/auth/password_reset_done.html

@@ -0,0 +1,9 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p><strong>{% trans email=message.user.email %}Your new password has been sent to {{ email }}!{% endtrans %}</strong></p>
+  <p>{% trans %}We have generated new password on your account and have sent it to you.{% endtrans %}</p>
+  <p>{% trans username=message.user.username %}Check your inbox, {{ username }}!{% endtrans %}</p>
+{% endblock %}

+ 10 - 0
templates/_message/auth/registered_activation_admin.html

@@ -0,0 +1,10 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}Welcome aboard, {{ username }}!{% endtrans %}</strong></p>
+  <p>{% trans %}Your account has been registered, but you won't be able to sign in before Board Administrator accepts your account.{% endtrans %}</p>
+  <p>{% trans %}Usually new member accounts are activated on same day, but sometimes this may take little longer.{% endtrans %}</p>
+  <p>{% trans %}Thanks for your patience!{% endtrans %}</p>
+{% endblock %}

+ 8 - 0
templates/_message/auth/registered_activation_none.html

@@ -0,0 +1,8 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}Welcome aboard, {{ username }}!{% endtrans %}</strong></p>
+  <p>{% trans %}Your account has been registered successfully.{% endtrans %}</p>
+{% endblock %}

+ 9 - 0
templates/_message/auth/registered_activation_user.html

@@ -0,0 +1,9 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}Welcome aboard, {{ username }}!{% endtrans %}</strong></p>
+  <p>{% trans %}Your account has been registered, but you will have to activate it before you will be able to sign-in. We have sent you an e-mail with activation link.{% endtrans %}</p>
+  <p>{% trans %}Thanks for your patience!{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/auth/registrations_off.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans %}We are sorry but new members registrations are currently not available.{% endtrans %}</strong></p>
+{% endblock %}

+ 6 - 0
templates/_message/banning/banned_ip.html

@@ -0,0 +1,6 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <p>{% trans %}Your IP address has been black-listed by Board Administrator. You will not be able to sign in or register.{% endtrans %}</p>
+{% endblock %}

+ 14 - 0
templates/_message/banning/banned_user.html

@@ -0,0 +1,14 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  {% if message.user.ban_reason_public %}
+  <p>{% trans username=message.user.username %}{{ username }}, your account has been locked for following reason:{% endtrans %}</p>
+  <p>{{ if user.ban_reason_public }}</p>
+  {% else %}
+  <p>{% trans username=message.user.username %}{{ username }}, your account has been locked by board administrator.{% endtrans %}</p>
+  {% endif %}
+  {% if message.user.ban_expires %}
+  <p>{% trans %}Your ban will expire on{% endtrans %} <em>{{ message.user.ban_expires|date:"DATE_FORMAT" }}</em></p>
+  {% endif %}
+{% endblock %}

+ 1 - 0
templates/_message/base.html

@@ -0,0 +1 @@
+{% block content %}{{ message.message }}{% endblock %}

+ 7 - 0
templates/_message/form_contains_errors.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}Form contains errors. Please try again.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/invalid_request.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}Request authorization is invalid. Resubmit your form.{% endtrans %}</p>
+{% endblock %}

+ 9 - 0
templates/_message/security/bad_credentials.html

@@ -0,0 +1,9 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}Your e-mail address or password is incorrect. Please try again.{% endtrans %}</p>
+  <p class="protip"><a href="{% url 'forgot_password' %}">{% trans %}Click here if you forgot your sign in credentials.{% endtrans %}</a></p>
+{% endblock %}

+ 6 - 0
templates/_message/security/forbidden_authenticated.html

@@ -0,0 +1,6 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <p>{% trans username=user.username %}{{ username }}, this page is not available to signed in users.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/security/forbidden_guest.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <p>{% trans %}Dear Guest, only signed in members are allowed to access this page.{% endtrans %}</p>
+  <p>{% trans %}Please sign in or register and try again.{% endtrans %}</p>
+{% endblock %}

+ 11 - 0
templates/_message/security/forbidden_jammed.html

@@ -0,0 +1,11 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  {% if settings.jams_lifetime > 0 %}
+  <p>{% trans %}You have used up allowed sign-in attempts quota and we temporarily banned you from signing in .{% endtrans %}</p>
+  <p>{% trans %}Please try again later.{% endtrans %}</p>
+  {% else %}
+  <p>{% trans %}You have used up allowed sign-in attempts quota and we banned you from signing in .{% endtrans %}</p>
+  {% endif %}
+{% endblock %}

+ 7 - 0
templates/_message/security/forbidden_request.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <p>{% trans %}Request authorization is invalid.{% endtrans %}</p>
+  <p>{% trans %}Please try again.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/security/signed_in.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}Welcome back, {{ username }}!{% endtrans %}</strong> {% trans %}You have signed in successfully.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/_message/security/signed_out.html

@@ -0,0 +1,7 @@
+{% extends "_message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans %}You have been signed out.{% endtrans %}</strong></p>
+{% endblock %}

+ 23 - 0
templates/admin/admin/form.html

@@ -0,0 +1,23 @@
+{% extends "admin/admin/layout.html" %}
+{% load i18n %}
+{% load l10n %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+{% import "admin/messages.html" as messages_theme %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action_body %}
+<form action="{{ url }}" method="post">
+  {{ form_theme.form_widget(form) }}
+  <div class="form-actions">
+  	<button name="save" type="submit" class="btn btn-primary">{{ action.submit_button }}</button>
+  	{% if action.get_edit_url -%}
+  	<button name="save_edit" type="submit" class="btn btn-warning">{% trans %}Save and Edit{% endtrans %}</button>
+  	{%- endif %}
+  	{% if action.get_new_url -%}
+  	<button name="save_new" type="submit" class="btn btn-success">{% trans %}Save and Add New{% endtrans %}</button>
+  	{%- endif %}
+  	{% if fallback %}<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>{% endif %}
+  </div>
+</form>
+{% endblock %}

+ 31 - 0
templates/admin/admin/layout.html

@@ -0,0 +1,31 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load l10n %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+{% import "admin/messages.html" as messages_theme %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% if admin.actions[0].id != action.id and action.name -%}
+{% if target %}{{ page_title(parent=action.name, title=target) }}{% else %}{{ page_title(title=action.name) }}{% endif %}
+{%- else -%}
+{{ page_title(title=admin.name) }}
+{%- endif %}{% endblock %}
+
+{% block content %}
+<div class="page-header{% if admin.actions|length > 1 %} tabs-header{% endif %}">
+  <h1>{{ admin.name }}{% if admin.help %} <small>{{ admin.help }}</small>{% endif %}</h1>
+  {% if admin.actions|length > 1 %}<ul class="nav nav-tabs">{% for item in admin.actions %}
+  	<li{% if action.id == item.id %} class="active"{% endif %}><a href="{{ item.route|url() }}"{% if action.id != item.id and item.help %} class="tooltip-bottom" title="{{ item.help }}"{% endif %}><i class="icon-{{ item.icon }}"></i> {{ item.name }}</a></li>{% endfor %}
+  </ul>{% endif %}
+</div>
+{% if admin.actions[0].id != action.id -%}
+<h2>{% if target %}{{ target }} <small>{{ action.name }}</small>{% else %}{{ action.name }}{% if action.help %} <small>{{ action.help }}</small>{% endif %}{% endif %}</h2>
+{% endif %}
+{% if message %}
+{{ messages_theme.draw_message(message, 'alert-form') }}
+{% endif %}
+{% block page_help %}{% endblock %}
+{% block action_body %}
+{% endblock %}
+{% endblock %}

+ 145 - 0
templates/admin/admin/list.html

@@ -0,0 +1,145 @@
+{% extends "admin/admin/layout.html" %}
+{% load i18n %}
+{% load l10n %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+{% import "admin/messages.html" as messages_theme %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action_body %}
+{%- if search_form %}
+<div class="row">
+  <div class="span9">{% endif -%}
+{% if items|length > 0 -%}
+<table class="table table-striped">
+  <thead>
+    <tr>{% block table_head scoped %}{% for column in action.columns %}
+      <th{{ th_class(column, sorting, sorting_method) }}>
+      {%- if column[0] in sorting %}<a href="
+      {%- if column[0] == sorting_method[0]-%}
+      {% if sorting_method[1] %}{{ url ~ query(sort=column[0],dir=0) }}{% else %}{{ url ~ query(sort=column[0],dir=1) }}{% endif %}
+      {%- else -%}
+      {{ url ~ query(sort=column[0],dir=sorting[column[0]]) }}
+      {%- endif %}">{{ column[1] }}</a>{% else %}{{ column[1] }}{% endif %}</th>{% endfor %}
+      <th>{% trans %}Actions{% endtrans %}</th>
+      {% if list_form %}<th class="check-cell"><label class="checkbox"><input type="checkbox" class="checkbox-master"></label></th>{% endif %}
+    {% endblock %}</tr>
+  </thead>
+  <tbody>{% for item in items %}
+    <tr>{% block table_row scoped %}{% for column in action.columns %}
+      <td{% if loop.first %} class="lead-cell{% if column[3] is defined %} span{{ widthratio(column[3], 100, 12) }}{% endif %}"{% elif column[3] is defined %} class="span{{ widthratio(column[3], 100, 12) }}"{% endif %}>{{ item[column[0]] }}</td>{% endfor %}{% endblock %}
+      {%- if not hide_actions%}
+      <td class="span2">
+      	{%- set item_actions = action.get_item_actions(request, item) -%}
+      	{% if item_actions -%}
+      	<ul class="list-actions">
+      	  {% for action in item_actions %}
+      	  <li>{% if action.post -%}
+      	  <form action="{{ action.url }}" method="post"{% if action.prompt %} class="confirm" data-jsconfirm="{{ action.prompt }}"{% endif %}>
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      	    <button type="submit" class="tooltip-top" title="{{ action.name }}"><i class="icon-{{ action.icon }}"></i></button>
+      	  </form>
+      	  {%- else -%}
+      	  <a href="{{ action.url }}" class="tooltip-top{% if action.prompt %} confirm{% endif %}"{% if action.prompt %} data-jsconfirm="{{ action.prompt }}"{% endif %} title="{{ action.name }}"><i class="icon-{{ action.icon }}"></i></a>
+      	  {%- endif %}</li>
+      	  {% endfor %}
+      	</ul>
+      	{%- else -%}
+      	<em>{% trans %}Not available{% endtrans %}</em>
+      	{%- endif %}
+      </td>{% endif %}
+      {% if list_form %}<td class="check-cell"><label class="checkbox"><input form="list_form" name="{{ list_form.fields['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ item.pk }}"{% if list_form.fields['list_items']['has_value'] and item.pk in list_form.fields['list_items']['value'] %} checked="checked"{% endif %}></label></td>{% endif %}
+    </tr>{% endfor %}
+  </tbody>
+</table>
+<div class="form-actions table-footer">{% if pagination and (pagination['prev'] > 0 or pagination['next'] > 0)%}
+  <ul class="pager pull-left">
+    {%- if pagination['prev'] > 0 %}<li><a href="{{ action.get_pagination_url(pagination['prev']) }}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ action.get_pagination_url(pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+  <div class="table-count pull-left">{%- trans current_page=pagination['page'], pages=pagination['total'] -%}
+  Page {{ current_page }} of {{ pages }}
+  {%- endtrans -%}</div>{% else %}
+  <div class="table-count pull-left">{% trans count=items_total, total=items_total|intcomma, shown=items.count()|intcomma -%}Showing all items
+{%- pluralize -%}
+Showing {{ shown }} of {{ total }} items
+{%- endtrans %}</div>{% endif %}
+  {% if list_form -%}
+  <form id="list_form" class="form-inline pull-right" action="{{ url }}" method="POST">
+    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+    <input type="hidden" name="origin" value="list">
+    {{ form_theme.input_select(list_form.fields['list_action'],width=20) }}
+    <button type="submit" class="btn btn-primary">{% trans %}Go{% endtrans %}</button>
+  </form>
+{%- endif %}
+</div>
+{%- else -%}
+<div class="alert alert-{% if action.is_filtering %}error{% else %}info{% endif %} alert-form">
+  <div class="alert-icon"><span><i class="icon-{% if action.is_filtering %}remove{% else %}info-sign{% endif %} icon-white"></i></span></div>
+  <p>{% if action.is_filtering %}{{ action.empty_search_message }}{% else %}{{ action.empty_message }}{% endif %}</p>
+</div>
+{% endif -%}
+{%- if search_form %}
+  </div>
+  <div class="span3 side-search">
+  	<h4>{% if search_form.fieldsets[0].legend %}{{ search_form.fieldsets[0].legend }}{% else %}{% trans %}Search Items{% endtrans %}{% endif %}</h4>
+    <form id="search_form" action="{{ url }}" method="post">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="origin" value="search">
+      {% for field in search_form.fieldsets[0].fields %}
+      {{ form_theme.row_widget(field, width=3) }}
+      {% endfor %}
+    </form>
+    <div class="form-actions">
+      <div class="row">
+        <button type="submit" form="search_form" class="btn btn-primary span1"><i class="icon-search icon-white"></i></button>{% if action.is_filtering %}
+        <button type="submit" form="search_clear" class="btn btn-inverse span1 offset1"><i class="icon-remove icon-white"></i></button>{% endif %}
+      </div>
+    </div>
+  </div>
+</div>{% if action.is_filtering %}
+<form id="search_clear" class="form-inline" action="{{ url }}" method="post">
+  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+  <input type="hidden" name="origin" value="clear">
+</form>{% endif %}{% endif -%}
+{% endblock %}
+
+{% block javascripts -%}
+{{ super() }}
+{%- if action.prompt_select %}
+  <script type="text/javascript">
+    $(function () {
+      $('#list_form').submit(function() {
+        if ($('.check-cell[]:checked').length == 0) {
+          alert('{{ action.nothing_checked_message }}');
+          return false;
+        }
+        {%- for item in action.actions %}{% if item.2 %}
+        if ($('#id_list_action').val() == '{{ item.0 }}') {
+          var decision = confirm('{{ item.2 }}');
+          return decision;
+        }
+        {%- endif %}{% endfor %}
+        return true;
+      });
+    });
+  </script>{% endif %}
+{%- endblock %}
+
+{#- COLUMN CLASS -#}
+{%- macro th_class(column, sorting, sorting_method) -%}
+{%- if column[2] or column[0] in sorting %} class="
+{#- COLUMN WIDTH (ex. "span4") -#}
+{%- if column[2] %}span{{ widthratio(column[2], 100, 12) }}{% endif -%}
+{#- SEPARATOR -#}
+{%- if column[2] and column[0] in sorting %} {% endif -%}
+{#- COLUMN SORTING -#}
+{%- if column[0] in sorting -%}table-sort sort-
+{%- if column[0] == sorting_method[0] -%}
+active-{% if sorting_method[1] %}asc{% else %}desc{% endif -%}
+{%- else -%}
+{%- if sorting[column[0]] -%}asc{% else %}desc{% endif -%}
+{%- endif -%}
+{%- endif -%}
+"{%- endif -%}
+{%- endmacro -%}

+ 17 - 0
templates/admin/base.html

@@ -0,0 +1,17 @@
+{% from "admin/macros.html" import page_title -%}
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>{% block title %}{{ page_title() }}{% endblock %}</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link href="{{ STATIC_URL }}admin/css/admin.css" rel="stylesheet">{% block stylesheets %}{% endblock %}
+  </head>
+  <body{% block body_class %}{% endblock %}>
+  	{% block body %}{% endblock %}
+  	<script src="{{ STATIC_URL }}admin/js/jquery-1.7.2.min.js"></script>
+  	<script src="{{ STATIC_URL }}admin/js/bootstrap.min.js"></script>
+  	<script src="{{ STATIC_URL }}admin/js/jquery.toggle.buttons.js"></script>
+  	<script src="{{ STATIC_URL }}admin/js/admin.js"></script>{% block javascripts %}{% endblock %}
+  </body>
+</html>

+ 23 - 0
templates/admin/error403.html

@@ -0,0 +1,23 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Permission denied{% endtrans %} - {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="page-header">
+      <h2>{% if title %}{{ title }}{% else %}{% trans %}Permission denied{% endtrans %}{% endif %} <small>403</small></h2>
+    </div>
+{% if message %}
+    {% if message.is_basic() %}<p>{{ message.message }}</p>{% else %}{% include message.tpl %}{% endif %}
+{% else %}
+    <p>{% trans %}You dont have permission to see this page.{% endtrans %}</p>
+{% endif %}
+    <ul class="unstyled">
+      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
+{% endblock %}

+ 23 - 0
templates/admin/error404.html

@@ -0,0 +1,23 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Page not found{% endtrans %} - {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="page-header">
+      <h2>{% if title %}{{ title }}{% else %}{% trans %}Page not found{% endtrans %}{% endif %} <small>404</small></h2>
+    </div>
+{% if message %}
+    {% if message.is_basic() %}<p>{{ message.message }}</p>{% else %}{% include message.tpl %}{% endif %}
+{% else %}
+    <p>{% trans %}The page you are looking for could not be found.{% endtrans %}</p>
+{% endif %}
+    <ul class="unstyled">
+      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
+{% endblock %}

+ 11 - 0
templates/admin/index.html

@@ -0,0 +1,11 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Unimplemented Admin Action!{% endtrans %}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>This action is not implemented!</h1>
+</div>
+{% endblock %}

+ 47 - 0
templates/admin/layout.html

@@ -0,0 +1,47 @@
+{% extends "admin/base.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "admin/messages.html" as messages_theme %}
+
+{% block body %}
+<div id="page-top" class="navbar navbar-static-top navbar-sections">
+  <div class="navbar-inner">
+    <div class="container">
+      <a class="brand" href="{{ admin_index|url() }}">Misago <span>{{ version }}</span></a>
+      <ul class="nav">{% for section in sections %}
+      	<li{% if section.is_active and not exception_response %} class="active"{% endif %}><a href="{{ section.route|url() }}"><i class="icon-{{ section.icon }}"></i> {{ section.name }}</a></li>{% endfor %}
+      </ul>
+      <form class="navbar-form user-signout pull-right" action="{% url 'admin_sign_out' %}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn btn-link"><i class="icon-off"></i> {% trans %}Sign Out{% endtrans %}</button></form>
+      <ul class="nav pull-right">
+      	<li><a href="{% url 'index' %}"><i class="icon-home"></i> {% trans %}Forums Index{% endtrans %}</a></li>
+      </ul>
+      <div class="user-profile pull-right">
+        <img src="{{ user.get_avatar() }}" class="avatar-small" alt="{{ user.username }}" title="{{ user.username }}"> {{ user.username }}
+      </div>
+    </div>
+  </div>
+</div>{% if not exception_response%}
+<div class="navbar navbar-static-top navbar-inverse navbar-actions">
+  <div class="navbar-inner">
+    <div class="container">
+      <ul class="nav">{% for action in actions %}
+      	<li{% if action.is_active %} class="active"{% elif action.help %} class="tooltip-bottom" title="{{ action.help }}"{% endif %}><a href="{{ action.route|url() }}"><i class="icon-{{ action.icon }}"></i> {{ action.name }}</a></li>{% endfor %}
+      </ul>
+    </div>
+  </div>
+</div>{% endif %}
+<div class="container body-container">
+  
+  {% if messages %}
+  <div class="alerts-global">
+  	{{ messages_theme.messages_list(messages) }}
+  </div>{% endif %}
+  {% block content %}
+  {% endblock %}
+  
+  <footer>
+  	Powered by <a href="http://misago-project.org">Misago forum software</a> by Rafał Pitoń
+  	<a href="#page-top" class="go-to-top"><i class="icon-arrow-up"></i>{% trans %}Go to Top{% endtrans %}</a>
+  </footer>
+</div>
+{% endblock %}

+ 20 - 0
templates/admin/layout_compact.html

@@ -0,0 +1,20 @@
+{% extends "admin/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block body_class %} class="layout-compact"{% endblock %}
+
+{% block body %}
+    <div class="container">
+      <div class="row">
+      	<div class="span4 offset4">
+		  <h1>{% block header %}{% endblock %}</h1>
+		</div>
+      	<div class="span4 offset4">
+      	  <div class="block-bold">
+      	    {% block content %}{% endblock %}
+      	  </div>
+      	</div>
+      </div>
+    </div>
+{% endblock %}

+ 5 - 0
templates/admin/macros.html

@@ -0,0 +1,5 @@
+{% load i18n %}
+
+{% macro page_title(title='', parent='', page=0) -%}
+{% if parent %}{{ parent }}: {% endif %}{% if title %}{{ title }}{% if page > 1 %} ({% trans page=page %}{{ page }} page{% endtrans %}){% endif %} - {% endif %}{% trans %}Misago Admin{% endtrans %}
+{%- endmacro %}

+ 1 - 0
templates/admin/message/base.html

@@ -0,0 +1 @@
+{% block content %}{{ message.message }}{% endblock %}

+ 7 - 0
templates/admin/message/form_contains_errors.html

@@ -0,0 +1,7 @@
+{% extends "admin/message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}Form contains errors. Please try again.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/admin/message/invalid_request.html

@@ -0,0 +1,7 @@
+{% extends "admin/message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}Request authorization is invalid. Resubmit your form.{% endtrans %}</p>
+{% endblock %}

+ 9 - 0
templates/admin/message/security/bad_credentials.html

@@ -0,0 +1,9 @@
+{% extends "admin/message/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}Your e-mail address or password is incorrect. Please try again.{% endtrans %}</p>
+  <p class="protip"><a href="{% url 'forgot_password' %}">{% trans %}Click here if you forgot your sign in credentials.{% endtrans %}</a></p>
+{% endblock %}

+ 8 - 0
templates/admin/message/security/not_admin.html

@@ -0,0 +1,8 @@
+{% extends "admin/message/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-remove icon-white"></i></span></div>
+  <p>{% trans %}You need to be administrator to access admin control panel.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/admin/message/security/signed_in.html

@@ -0,0 +1,7 @@
+{% extends "admin/message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-ok icon-white"></i></span></div>
+  <p><strong>{% trans username=message.user.username %}Welcome back, {{ username }}!{% endtrans %}</strong> {% trans %}You have signed in successfully.{% endtrans %}</p>
+{% endblock %}

+ 7 - 0
templates/admin/message/security/signed_out.html

@@ -0,0 +1,7 @@
+{% extends "admin/message/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p>{% trans %}You have been signed out.{% endtrans %}</p>
+{% endblock %}

+ 17 - 0
templates/admin/messages.html

@@ -0,0 +1,17 @@
+{# Messages list marco #}
+{% macro messages_list(messages) %}{% if messages %}<div class="alerts-list">{% for message in messages %}
+  {{ draw_message(message) }}
+{% endfor %}</div>{% endif %}
+{% endmacro %}
+
+{# Render single message #}
+{% macro draw_message(message, class='') %}
+  <div class="alert alert-{{ message.type }}{% if class %} {{ class }}{% endif %}">{% if message.is_basic() %}
+  	<div class="alert-icon"><span><i class="icon-{% if message.type == 'error' -%}remove
+  		{%- elif message.type == 'success' -%}ok
+  		{%- elif message.type == 'info' -%}info-sign
+  		{%- else -%}warning-sign
+  		{%- endif %} icon-white"></i></span></div> <p>{{ message.message }}</p>{% else %}
+  		{% include message.tpl %}{% endif %}
+  </div>
+{%- endmacro %}

+ 76 - 0
templates/admin/overview/forums.html

@@ -0,0 +1,76 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load humanize %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+{% from "admin/overview/plot.html" import draw_plot_tooltips, draw_plot with context %}
+
+{% block title %}{{ page_title(title=_('Forums Overview')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Forums Overview{% endtrans %} <small></small></h1>
+</div>
+
+<div class="row">
+  <div class="span6">
+  	<h2 style="text-align: center;">{{ posts|intcomma }}
+  	<small>{% trans %}Posts{% endtrans %}</small></h2>
+  </div>
+  <div class="span6">
+  	<h2 style="text-align: center;">{{ threads|intcomma }}
+  	<small>{% trans %}Threads{% endtrans %}</small></h2>
+  </div>
+</div>
+
+<hr>
+
+<h2>New Posts</h2>
+<div class="graph">
+  <canvas id="graph-posts" height="150px"></canvas>
+  {{ draw_plot_tooltips('graph-posts', graph_posts) }}
+  <div class="timeline">
+    <div class="pull-left">{{ graph_posts.start|date }}</div>
+    <div class="pull-right">{{ graph_posts.end|date(graph_posts.format) }}</div>
+  </div>
+</div>
+<p class="sub-graph pull-right">{% trans count=graph_posts.total|int, total=graph_posts.total|intcomma -%}One new post found in period requested
+{%- pluralize -%}
+{{ total }} new posts found in period requested
+{%- endtrans %}</p>
+<ul class="nav nav-pills sub-graph">
+  <li{% if mode == "day" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview' %}">{% trans %}Last 24h{% endtrans %}</a></li>
+  <li{% if mode == "week" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview_week' %}">{% trans %}Last Week{% endtrans %}</a></li>
+  <li{% if mode == "month" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview_month' %}">{% trans %}Last Month{% endtrans %}</a></li>
+  <li{% if mode == "year" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview_year' %}">{% trans %}Last Year{% endtrans %}</a></li>
+</ul>
+
+<hr>
+
+<h2>New Threads</h2>
+<div class="graph">
+  <canvas id="graph-threads" height="150px"></canvas>
+  {{ draw_plot_tooltips('graph-threads', graph_threads) }}
+  <div class="timeline">
+    <div class="pull-left">{{ graph_threads.start|date }}</div>
+    <div class="pull-right">{{ graph_threads.end|date(graph_threads.format) }}</div>
+  </div>
+</div>
+<p class="sub-graph pull-right">{% trans count=graph_threads.total|int, total=graph_threads.total|intcomma -%}One new thread found in period requested
+{%- pluralize -%}
+{{ total }} new threads found in period requested
+{%- endtrans %}</p>
+<ul class="nav nav-pills sub-graph">
+  <li{% if mode == "day" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview' %}">{% trans %}Last 24h{% endtrans %}</a></li>
+  <li{% if mode == "week" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview_week' %}">{% trans %}Last Week{% endtrans %}</a></li>
+  <li{% if mode == "month" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview_month' %}">{% trans %}Last Month{% endtrans %}</a></li>
+  <li{% if mode == "year" %} class="active"{% endif %}><a href="{% url 'admin_forums_overview_year' %}">{% trans %}Last Year{% endtrans %}</a></li>
+</ul>
+{% endblock %}
+
+{% block javascripts %}
+<script type="text/javascript">
+  {{ draw_plot('draw_posts', 'graph-posts', graph_posts.max, graph_posts.stat) }}
+  {{ draw_plot('draw_threads', 'graph-threads', graph_threads.max, graph_threads.stat) }}
+</script>
+{% endblock %}

+ 107 - 0
templates/admin/overview/home.html

@@ -0,0 +1,107 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load humanize %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+
+{% block title %}{{ page_title(title=_('Admin Home')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Admin Home{% endtrans %} <small>{% trans %}Misago {{version}}{% endtrans %}</small></h1>
+</div>
+<div class="row">
+  <div class="span8">
+  	<h2>{% trans count=admins|length, total=admins|length|intcomma -%}
+One Administrator Online
+{%- pluralize -%}
+{{ total }} Administrators Online
+{%- endtrans %}</h2>
+	<ul class="list-tiny">{% for session in admins %}
+	  <li class="popover-admin-{{ loop.index }} clickable">
+	  	<img src="{{ session.user.get_avatar() }}" class="avatar-tiny"> {{ session.user.username }}
+	  </li>{% endfor %}
+	</ul>	
+    
+    <hr>
+    
+  	<h3>{% trans %}Board Statistics{% endtrans %}</h3>
+    <table class="table table-striped">
+      <tbody>
+      	<tr>
+      	  <td class="span2 stat-title"><strong>{% trans %}Users{% endtrans %}</strong></td>
+      	  <td>{{ users|intcomma }}</td>
+      	</tr>
+      	<tr>
+      	  <td class="span2 stat-title"><strong>{% trans %}Posts{% endtrans %}</strong></td>
+      	  <td>{{ posts|intcomma }}</td>
+      	</tr>
+      	<tr>
+      	  <td class="span2 stat-title"><strong>{% trans %}Threads{% endtrans %}</strong></td>
+      	  <td>{{ threads|intcomma }}</td>
+      	</tr>
+      </tbody>
+    </table>
+  </div>
+  <div class="span4">
+    <h3>{% trans %}Quick Action{% endtrans %}</h3>
+    <form>
+      <div class="side-search quick-action">
+        <div class="control-group">
+          <label class="control-label" for="inputEmail">User to find:</label>
+          <div class="controls">
+            <input type="text" id="inputEmail" class="span4" placeholder="Email">
+          </div>
+        </div>
+        <div class="control-group">
+          <label class="control-label">Actions to run:</label>
+          <select class="span4">
+            <option>Dont change account state</option>
+            <option>Activate user account</option>
+            <option>Reset user password</option>
+            <option>Ban user for 15 minutes</option>
+            <option>Ban user permanently</option>
+          </select>
+          <label class="checkbox">
+            <input type="checkbox" value="">
+            Remove and block user avatar
+          </label>
+          <label class="checkbox">
+            <input type="checkbox" value="">
+            Remove and block user signature
+          </label>
+        </div>
+        <div class="form-actions">
+          <button type="submit" class="btn btn-primary">Run Action</button>
+        </div>
+      </div>
+    </form>
+  </div>
+</div>
+{% endblock %}
+
+{% block javascripts %}{% if admins %}
+<script type="text/javascript">
+  $(function () {
+  	// Register popovers for admin list{% for session in admins %}
+	$('.popover-admin-{{ loop.index }}').popover({
+		placement: 'top',
+		trigger:   'click',
+		title:     '{{ popover_title(session)|escapejs }}',
+		content:   '{{ popover_content(session)|escapejs }}'
+	});{% endfor %}
+  });
+</script>{% endif %}
+{% endblock %}
+
+{% macro popover_title(session) -%}
+  <div class="user-card">
+  	<img src="{{ session.user.get_avatar() }}" class="avatar-small">
+    {{ session.user.username }}
+  </div>
+{%- endmacro %}
+
+{% macro popover_content(session) -%}
+    <strong>{% trans last_action=session.last|timesince, ip=session.ip %}{{ last_action }} ago from {{ ip }}{% endtrans %}</strong>
+    <p>{{ session.agent }}</p>
+{%- endmacro %}

+ 77 - 0
templates/admin/overview/plot.html

@@ -0,0 +1,77 @@
+{% macro draw_plot_tooltips(id, graph) -%}
+{%- for stop in graph.timeline -%}
+<div id="{{ id }}-tooltip-{{ loop.index0 }}" class="peak peak-{{ id }} tooltip-{% if loop.first %}right{% elif loop.last %}left{% else %}top{% endif %}" title="{{ stop|date(graph.format) }}<br><strong>{{ graph.stat[loop.index0]|intcomma }}</strong>"></div>
+{%- endfor -%}
+{%- endmacro %}
+
+{% macro draw_plot(name, id, max, variables=[], color='#729fcf') -%}
+      // Begin drawing graph for {{ id }}
+      function {{ name }}() {
+        var margin = 12;
+        var margin_end = margin * 2;
+        var canvas = document.getElementById('{{ id }}');
+        var canvas_parent = $(canvas).parent();
+        var offset = canvas_parent.offset();
+        canvas.width = canvas_parent.width();
+        var ctx = canvas.getContext('2d');
+
+        // Guidelines
+        ctx.beginPath();
+        ctx.lineWidth = 1;
+        ctx.strokeStyle = "#EEEEEE";
+        ctx.fillStyle = "#CCC";
+		ctx.font = "10pt Helvetica";
+        ctx.moveTo(margin, margin + 0.5);
+        ctx.lineTo(canvas.width - margin, margin + 0.5);
+{%- if max > 0 %}
+{%- for line in [25, 50, 75] %}
+{%- if max > 3 %}
+{% set y_scales = 4 %}
+{%- else -%}
+{% set y_scales = max %}
+{% endif -%}
+{% if loop.index < max and loop.index < 4 %}
+		{%- set y_label = max - (loop.index / y_scales * max)|int -%}
+        y_pos = margin + Math.round((canvas.height - margin_end) * ({{ loop.index }} / {{ max }})) + 0.5;
+        ctx.moveTo(margin, y_pos);
+        ctx.lineTo(canvas.width - margin, y_pos);
+        ctx.fillText("{{ y_label }}", margin + 4, y_pos - 4.5);
+        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.25), y_pos - 4.5);
+        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.5), y_pos - 4.5);
+        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.75), y_pos - 4.5);
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+        ctx.stroke();
+
+        // Final guide
+        ctx.beginPath();
+        ctx.strokeStyle = "#999999";
+        ctx.moveTo(margin, canvas.height - margin + 0.5);
+        ctx.lineTo(canvas.width - margin, canvas.height - margin + 0.5);
+        ctx.fillText("0", margin + 4, canvas.height - margin - 4);
+        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.25), canvas.height - margin - 4);
+        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.5), canvas.height - margin - 4);
+        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.75), canvas.height - margin - 4);
+        ctx.stroke();
+
+        // Graph line
+        ctx.beginPath();
+        ctx.lineWidth = 4;
+        ctx.strokeStyle = "{{ color }}";
+        step_size = (canvas.width - margin_end) / {{ variables|length - 1 }};
+{%- for stop in variables %}
+        pos_x = margin + ({{ loop.index0 }} * step_size);
+        pos_y = {% if max > 0 %}margin + Math.round((canvas.height - margin_end) * {{ ((max - stop) / max)|round(5) }}) + 0.5{% else %}canvas.height - margin{% endif %};
+		$('#{{ id }}-tooltip-{{ loop.index0 }}').offset({ top: offset.top + pos_y - 2, left: offset.left + pos_x - 1 });
+        ctx.{% if loop.index0 == 0 %}moveTo{% else %}lineTo{% endif -%}
+        (pos_x, pos_y);
+{%- endfor %}
+        ctx.stroke();
+      }
+
+      // Update graphs
+      $(function() { {{ name }}(); $(".peak-{{ id }}").css('border-color', '{{ color }}') });
+      $(window).resize(function() { {{ name }}(); });
+      // End drawing graph for {{ id }}
+{%- endmacro %}

+ 19 - 0
templates/admin/overview/stats/form.html

@@ -0,0 +1,19 @@
+{% extends "admin/overview/stats/layout.html" %}
+{% load i18n %}
+
+{% import "admin/messages.html" as messages_theme %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action %}<div class="row">
+  <div class="span8 offset2">
+  	<h2>{% trans %}New Report{% endtrans %}</h2>
+    <form action="{% url 'admin_overview_stats' %}" class="form-vertical" method="post">
+      <div class="form-container">
+        {{ form_theme.form_widget(form, width=8) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Generate Report{% endtrans %}</button>
+      </div>
+    </form>
+  </div>
+</div>{% endblock %}

+ 33 - 0
templates/admin/overview/stats/graph.html

@@ -0,0 +1,33 @@
+{% extends "admin/overview/stats/form.html" %}
+{% load i18n %}
+
+{% import "admin/messages.html" as messages_theme %}
+{% import "_forms.html" as form_theme with context %}
+{% from "admin/overview/plot.html" import draw_plot_tooltips, draw_plot with context %}
+
+{% block title %}{{ page_title(title=title, parent=_('Stats')) }}{% endblock %}
+
+{% block action %}
+<h2>{{ title }} <small>{% trans count=graph.total, total=graph.total|intcomma -%}One item found
+{%- pluralize -%}
+{{ total }} items found
+{%- endtrans %}</small></h2>
+<div class="graph">
+  <canvas id="graph-canvas" height="250px"></canvas>
+  {{ draw_plot_tooltips('graph-canvas', graph) }}
+  <div class="timeline">
+    <div class="pull-left">{{ graph.start|date }}</div>
+    <div class="pull-right">{{ graph.end|date }}</div>
+  </div>
+</div>
+
+<hr>
+
+{{ super() }}
+{% endblock %}
+
+{% block javascripts %}
+<script type="text/javascript">
+  {{ draw_plot('draw_graph', 'graph-canvas', graph.max, graph.stat) }}
+</script>
+{% endblock %}

+ 15 - 0
templates/admin/overview/stats/layout.html

@@ -0,0 +1,15 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% from "admin/macros.html" import page_title %}
+{% import "admin/messages.html" as messages_theme %}
+
+{% block title %}{{ page_title(title=_('Stats')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Stats{% endtrans %} <small>{% trans %}Create Statistics Reports{% endtrans %}</small></h1>
+</div>{% if message %}
+{{ messages_theme.draw_message(message, 'alert-form') }}
+{% endif %}
+{% block action %}{% endblock %}
+{% endblock %}

+ 9 - 0
templates/admin/overview/stats/not_available.html

@@ -0,0 +1,9 @@
+{% extends "admin/overview/stats/layout.html" %}
+{% load i18n %}
+
+{% block action %}<div class="alert">
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans %}No statistics providers could be found.{% endtrans %}</strong></p>
+  <p>{% trans %}This action is not avaiable because there are no models found that provide filter_overview method necessary to generate statistics reports.{% endtrans %}</p>
+  <p>{% trans %}Some of Misago models provide statistics so you should never see this page.{% endtrans %}</p>
+</div>{% endblock %}

+ 68 - 0
templates/admin/overview/system.html

@@ -0,0 +1,68 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load humanize %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+
+{% block title %}{{ page_title(title=_('System Overview')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}System Overview{% endtrans %} <small>{% trans %}Misago {{version}}{% endtrans %}</small></h1>
+</div>
+<div class="row">
+  <div class="span4">
+  	<div class="well well-small">
+  	  <h1 style="text-align: center;">{{ users|intcomma }}
+  	  <small>{% trans %}Users{% endtrans %}</small></h1>
+    </div>
+  </div>
+  <div class="span4">
+  	<div class="well well-small">
+  	  <h1 style="text-align: center;">{{ posts|intcomma }}
+  	  <small>{% trans %}Posts{% endtrans %}</small></h1>
+    </div>
+  </div>
+  <div class="span4">
+  	<div class="well well-small">
+  	  <h1 style="text-align: center;">{{ threads|intcomma }}
+  	  <small>{% trans %}Threads{% endtrans %}</small></h1>
+  	</div>
+  </div>
+</div>
+
+<hr>
+
+<h2>{% trans %}Administrators Online{% endtrans %} <small>{{ admins|length|intcomma }}</small></h2>
+<div class="row list-tiny">{% for session in admins %}
+  <div class="span2 popover-admin-{{ loop.index }} clickable">
+  	<img src="{{ session.user.get_avatar() }}" class="avatar-tiny"> {{ session.user.username }}
+  </div>{% endfor %}
+</div>
+{% endblock %}
+
+{% block javascripts %}{% if admins %}
+<script type="text/javascript">
+  $(function () {
+  	// Register popovers for admin list{% for session in admins %}
+	$('.popover-admin-{{ loop.index }}').popover({
+		placement: 'top',
+		trigger:   'click',
+		title:     '{{ popover_title(session)|escapejs }}',
+		content:   '{{ popover_content(session)|escapejs }}'
+	});{% endfor %}
+  });
+</script>{% endif %}
+{% endblock %}
+
+{% macro popover_title(session) -%}
+  <div class="user-card">
+  	<img src="{{ session.user.get_avatar() }}" class="avatar-small">
+    {{ session.user.username }}
+  </div>
+{%- endmacro %}
+
+{% macro popover_content(session) -%}
+    <strong>{% trans last_action=session.last|timesince, ip=session.ip %}{{ last_action }} ago from {{ ip }}{% endtrans %}</strong>
+    <p>{{ session.agent }}</p>
+{%- endmacro %}

+ 81 - 0
templates/admin/overview/users.html

@@ -0,0 +1,81 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load humanize %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+{% from "admin/overview/plot.html" import draw_plot_tooltips, draw_plot with context %}
+
+{% block title %}{{ page_title(title=_('Users Overview')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Users Overview{% endtrans %} <small>{% trans count=users|int, total=users|intcomma -%}One user registered
+{%- pluralize -%}
+{{ total }} users registered
+{%- endtrans %}</small></h1>
+</div>
+
+<div class="alert alert-error">
+  <div class="alert-icon"><span><i class="icon-fire icon-white"></i></span></div>
+  <p><strong>13 user accounts are awaiting activation.</strong></p>
+  <p class="protip"><a href="">{% trans %}Click here to go to inactive users list.{% endtrans %}</a></p>
+</div>
+
+<h2>User Registrations</h2>
+<div class="graph">
+  <canvas id="graph-users" height="150px"></canvas>
+  {{ draw_plot_tooltips('graph-users', graph) }}
+  <div class="timeline">
+    <div class="pull-left">{{ graph.start|date }}</div>
+    <div class="pull-right">{{ graph.end|date(graph.format) }}</div>
+  </div>
+</div>
+<p class="sub-graph pull-right">{% trans count=graph.total|int, total=graph.total|intcomma -%}One registration found in period requested
+{%- pluralize -%}
+{{ total }} registrations found in period requested
+{%- endtrans %}</p>
+<ul class="nav nav-pills sub-graph">
+  <li{% if mode == "day" %} class="active"{% endif %}><a href="{% url 'admin_users_overview' %}">{% trans %}Last 24h{% endtrans %}</a></li>
+  <li{% if mode == "week" %} class="active"{% endif %}><a href="{% url 'admin_users_overview_week' %}">{% trans %}Last Week{% endtrans %}</a></li>
+  <li{% if mode == "month" %} class="active"{% endif %}><a href="{% url 'admin_users_overview_month' %}">{% trans %}Last Month{% endtrans %}</a></li>
+  <li{% if mode == "year" %} class="active"{% endif %}><a href="{% url 'admin_users_overview_year' %}">{% trans %}Last Year{% endtrans %}</a></li>
+</ul>
+
+<hr>
+
+<h2>{% trans %}Staff Online{% endtrans %} <small>{{ staff|length|intcomma }}</small></h2>{% if staff %}
+<div class="row list-tiny">{% for session in staff %}
+  <div class="span2 popover-staff-{{ loop.index }} clickable">
+  	<img src="{{ session.user.get_avatar() }}" class="avatar-tiny"> {{ session.user.username }}
+  </div>{% endfor %}
+</div>{% else %}
+<p>{% trans %}No staff members are currently browsing forums.{% endtrans %}</p>
+{% endif %}
+{% endblock %}
+
+{% block javascripts %}
+<script type="text/javascript">{% if staff %}
+  $(function () {
+  	// Register popovers{% for session in staff %}
+	$('.popover-staff-{{ loop.index }}').popover({
+		placement: 'top',
+		trigger:   'click',
+		title:     '{{ popover_title(session)|escapejs }}',
+		content:   '{{ popover_content(session)|escapejs }}'
+	});{% endfor %}
+  });{% endif %}
+  {{ draw_plot('draw_users', 'graph-users', graph.max, graph.stat) }}
+</script>
+{% endblock %}
+
+{% macro popover_title(session) -%}
+  <div class="user-card">
+  	<img src="{{ session.user.get_avatar() }}" class="avatar-small">
+    {{ session.user.username }}
+  </div>
+{%- endmacro %}
+
+{% macro popover_content(session) -%}
+    <strong>{% trans last_action=session.last|timesince, ip=session.ip %}{{ last_action }} ago from {{ ip }}{% endtrans %}</strong>
+    <p>{{ session.agent }}</p>
+{%- endmacro %}

+ 17 - 0
templates/admin/settings/search_results.html

@@ -0,0 +1,17 @@
+{% extends "admin/settings/settings.html" %}
+{% load i18n %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+
+{% block title %}{{ page_title(title=_('Search Results'), parent=_('Settings')) }}{% endblock %}
+
+{% block action %}
+<h2 class="sidepanel-header">{% trans %}Search Results{% endtrans %}</h2>{% if message %}
+{{ messages_theme.draw_message(message, 'alert-form') }}{% endif %}
+{% for setting in found_settings %}
+<h4>{{ _(setting.name) }} <small>{{ _(setting.group.name) }}</small></h4>
+{%- if setting.description %}<p>{{ _(setting.description) }}</p>{% endif -%}
+<a href="{% url 'admin_settings' group_id=setting.group.id, group_slug=setting.group.key %}#id_{{ setting.pk }}">{% trans %}Go to this setting{% endtrans %}</a>
+<hr>
+{% endfor %}
+{% endblock %}

+ 42 - 0
templates/admin/settings/settings.html

@@ -0,0 +1,42 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+{% from "admin/macros.html" import page_title %}
+{% import "admin/messages.html" as messages_theme %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{{ page_title(title=_(active_group.name), parent=_('Settings')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Settings{% endtrans %} <small>{% trans %}Change your forum configuration{% endtrans %}</small></h1>
+</div>
+<div class="row">
+  <div class="span3">
+    <ul class="nav nav-pills nav-stacked side-panel">
+      <h4>{% trans %}Search Settings{% endtrans %}</h4>
+      <form action="{% url 'admin_settings_search' %}" class="form-inline" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        {{ form_theme.input_text(search_form.fields.search_text, width=3, attrs={'placeholder': _('Search Settings...')}, horizontal=true) }}
+        <button type="submit" class="btn btn-primary"><i class="icon-search icon-white"></i></button>
+      </form>
+      <h4>{% trans%}Settings Groups{% endtrans %}</h4>{% for group in groups %}
+      <li{% if group.is_active(active_group) %} class="active"{% endif %}><a href="{% url 'admin_settings' group_id=group.id, group_slug=group.key %}"{% if not group.is_active(active_group) and group.description %} class="tooltip-right" title="{{ _(group.description) }}"{% endif %}>{{ _(group.name) }}</a></li>{% endfor %}
+    </ul>
+  </div>
+  <div class="span9">{% block action %}
+  	<h2 class="sidepanel-header">{{ _(active_group.name) }}</h2>{% if message %}
+  	{{ messages_theme.draw_message(message, 'alert-form') }}
+  	{% endif %}{% if active_group.description %}
+  	<p>{{ _(active_group.description) }}</p>{% endif %}
+    <form class="form-vertical" action="{% url 'admin_settings' group_id=active_group.id, group_slug=active_group.key %}" method="post">
+      <div class="form-container">
+       	{{ form_theme.form_widget(form, width=9) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Change Settings{% endtrans %}</button>
+      </div>
+    </form>
+  {% endblock %}</div>
+</div>
+{% endblock %}

+ 25 - 0
templates/admin/signin.html

@@ -0,0 +1,25 @@
+{% extends "admin/layout_compact.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "_forms.html" as form_theme with context %}
+      
+{% block title %}{% trans %}Sign In{% endtrans %}{% endblock %}
+
+{% block header %}<strong>Misago</strong> {% trans %}Board Administration{% endtrans %}{% endblock %}
+      
+{% block content %}
+          {% if message %}
+          <div class="alert alert-{{ message.type }} block-alert">
+            {% include message.tpl %}
+          </div>
+          {% endif %}
+          <form class="form-vertical" action="{{ admin_index|url() }}" method="post">
+          	<div class="form-container">
+              {{ form_theme.form_widget(form, width=4) }}
+            </div>
+            <div class="form-actions">
+              <button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> {% trans %}Sign In{% endtrans %}</button>
+              <a href="{% url 'index' %}" class="btn pull-right"><i class="icon-home"></i> {% trans %}Return to Forums{% endtrans %}</a>
+            </div>
+          </form>
+{% endblock %}

+ 12 - 0
templates/admin/todo.html

@@ -0,0 +1,12 @@
+{% extends "admin/layout.html" %}
+{% load i18n %}
+{% from "admin/macros.html" import page_title %}
+
+{% block title %}{{ page_title(title=_('Unimplemented Admin Action')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Unimplemented Admin Action{% endtrans %} <small>{% trans %}Placeholder Page{% endtrans %}</small></h1>
+</div>
+<p>{% trans %}This is placeholder page unimplemented admin actions can refer to.{% endtrans %}</p>
+{% endblock %}

+ 15 - 0
templates/admin/users/admin_users/list.html

@@ -0,0 +1,15 @@
+{% extends "admin/admin/list.html" %}
+{% load i18n %}
+{% load l10n %}
+{% load url from future %}
+
+{% block table_head scoped %}
+  <th>&nbsp;</th>
+  {{ super() }}
+{% endblock %}
+{% block table_row scoped %}
+  <td style="width: 1%;"><img src="{{ item.get_avatar('small') }}" class="avatar-small" alt="{% trans %}User Avatar{% endtrans %}"></td>
+  <td colspan="2" class="lead-cell">
+  	<strong>{{ item.username }}</strong> <span class="muted">{{ item.email }}</span>{% if item.activation > 0 %} <span class="label">{% trans %}Inactive{% endtrans %}</span>{% endif %}{% if item.is_admin() %} <span class="label label-info">{% trans %}Admin{% endtrans %}</span>{% endif %}
+  </td>
+{% endblock%}

+ 54 - 0
templates/debug_toolbar/base.html

@@ -0,0 +1,54 @@
+{% load i18n %}
+<style type="text/css">
+@media print { #djDebug {display:none;}}
+{{ css }}
+</style>
+<script type="text/javascript">{{ js }}</script>
+<div id="djDebug" style="display:none;">
+	<div style="display:none;" id="djDebugToolbar">
+		<ul id="djDebugPanelList">
+			{% if panels %}
+			<li><a id="djHideToolBarButton" href="#" title="{% trans "Hide Toolbar" %}">{% trans "Hide" %} &raquo;</a></li>
+			{% else %}
+			<li id="djDebugButton">DEBUG</li>
+			{% endif %}
+			{% for panel in panels %}
+				<li class="djDebugPanelButton">
+					{% if panel.has_content %}
+						<a href="{{ panel.url|default:"#" }}" title="{{ panel.title }}" class="{{ panel.dom_id }}">
+					{% else %}
+					    <div class="contentless">
+					{% endif %}
+					{{ panel.nav_title }}
+					{% with panel.nav_subtitle as subtitle %}
+						{% if subtitle %}<br /><small>{{ subtitle }}</small>{% endif %}
+					{% endwith %}
+					{% if panel.has_content %}
+						</a>
+					{% else %}
+					    </div>
+					{% endif %}
+				</li>
+			{% endfor %}
+		</ul>
+	</div>
+	<div style="display:none;" id="djDebugToolbarHandle">
+		<a title="{% trans "Show Toolbar" %}" id="djShowToolBarButton" href="#">&laquo;</a>
+	</div>
+	{% for panel in panels %}
+		{% if panel.has_content %}
+			<div id="{{ panel.dom_id }}" class="panelContent">
+				<div class="djDebugPanelTitle">
+					<a href="" class="djDebugClose">{% trans "Close" %}</a>
+					<h3>{{ panel.title|safe }}</h3>
+				</div>
+				<div class="djDebugPanelContent">
+				    <div class="scroll">
+				        {{ panel.content|safe }}
+				    </div>
+				</div>
+			</div>
+		{% endif %}
+	{% endfor %}
+	<div id="djDebugWindow" class="panelContent"></div>
+</div>

+ 56 - 0
templates/debug_toolbar/panels/cache.html

@@ -0,0 +1,56 @@
+{% load i18n %}
+<table>
+	<colgroup>
+		<col width="12%"/>
+		<col width="12%"/>
+		<col width="12%"/>
+		<col width="12%"/>
+		<col width="12%"/>
+		<col width="12%"/>
+		<col width="12%"/>
+		<col width="12%"/>
+	</colgroup>
+	<tr>
+		<th>{% trans "Total Calls" %}</th>
+		<td>{{ cache_calls }}</td>
+		<th>{% trans "Total Time" %}</th>
+		<td>{{ cache_time }}ms</td>
+		<th>{% trans "Hits" %}</th>
+		<td>{{ cache.hits }}</td>
+		<th>{% trans "Misses" %}</th>
+		<td>{{ cache.misses }}</td>
+	</tr>
+	<tr>
+		<th>gets</th>
+		<td>{{ cache.gets }}</td>
+		<th>sets</th>
+		<td>{{ cache.sets }}</td>
+		<th>deletes</th>
+		<td>{{ cache.deletes }}</td>
+		<th>get_many</th>
+		<td>{{ cache.get_many }}</td>
+	</tr>
+</table>
+{% if cache.calls %}
+<h3>{% trans "Breakdown" %}</h3>
+<table>
+	<thead>
+		<tr>
+			<th>{% trans "Time" %}&nbsp;(ms)</th>
+			<th>{% trans "Type" %}</th>
+			<th>{% trans "Parameters" %}</th>
+			<th>{% trans "Function" %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for query in cache.calls %}
+			<tr class="{% cycle 'row1' 'row2' %}">
+				<td>{{ query.0|floatformat:"4" }}</td>
+				<td>{{ query.1|escape }}</td>
+				<td>{{ query.2|escape }}</td>
+				<td><acronym title="{{ query.3.0 }}:{{ query.3.1 }}">{{ query.3.2|escape }}</acronym>: {{ query.3.3.0|escape }}</td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>
+{% endif %}

+ 17 - 0
templates/debug_toolbar/panels/headers.html

@@ -0,0 +1,17 @@
+{% load i18n %}
+<table>
+	<thead>
+		<tr>
+			<th>{% trans "Key" %}</th>
+			<th>{% trans "Value" %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for key, value in headers.iteritems %}
+			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+				<td>{{ key|escape }}</td>
+				<td>{{ value|escape }}</td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>

+ 28 - 0
templates/debug_toolbar/panels/logger.html

@@ -0,0 +1,28 @@
+{% load i18n %}
+{% if records %}
+	<table>
+		<thead>
+			<tr>
+				<th>{% trans "Level" %}</th>
+				<th>{% trans "Time" %}</th>
+				<th>{% trans "Channel" %}</th>
+				<th>{% trans "Message" %}</th>
+				<th>{% trans "Location" %}</th>
+			</tr>
+		</thead>
+		<tbody>
+			{% for record in records %}
+				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+					<td>{{ record.level }}</td>
+					<td>{{ record.time|date:"h:i:s m/d/Y" }}</td>
+					<td>{{ record.channel|default:"-" }}</td>
+					<td>{{ record.message }}</td>
+					<td>{{ record.file }}:{{ record.line }}</td>
+				</tr>
+			{% endfor %}
+		</tbody>
+	</table>
+{% else %}
+	<p>{% trans "No messages logged" %}.</p>
+{% endif %}
+

+ 36 - 0
templates/debug_toolbar/panels/profiling.html

@@ -0,0 +1,36 @@
+{% load i18n %}
+
+<table width="100%">
+	<thead>
+		<tr>
+			<th>{% trans "Call" %}</th>
+			<th>{% trans "TotTime" %}</th>
+			<th>{% trans "Per" %}</th>
+			<th>{% trans "CumTime" %}</th>
+			<th>{% trans "Per" %}</th>
+			<th>{% trans "Count" %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for call in func_list %}
+			<!--  style="background:{{ call.background }}" -->
+			<tr id="{{ call.id }}" class="djDebugProfileRow{% for parent_id in call.parent_ids %} djToggleDetails_{{ parent_id }}{% endfor %}" depth="{{ call.depth }}">
+				<td>
+					<div style="padding-left: {{ call.indent }}px;">
+						{% if call.has_subfuncs %}
+							<a class="djProfileToggleDetails djToggleSwitch" data-toggle-id="{{ call.id }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">-</a>
+						{% else %}
+							<span class="djNoToggleSwitch"></span>
+						{% endif %}
+						<span class="stack">{{ call.func_std_string }}</span>
+					</div>
+				</td>
+				<td>{{ call.tottime|floatformat:3 }}</td>
+				<td>{{ call.tottime_per_call|floatformat:3 }}</td>
+				<td>{{ call.cumtime|floatformat:3 }}</td>
+				<td>{{ call.cumtime_per_call|floatformat:3 }}</td>
+				<td>{{ call.count }}</td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>

+ 123 - 0
templates/debug_toolbar/panels/request_vars.html

@@ -0,0 +1,123 @@
+{% load i18n %}
+
+<h4>{% trans 'View information' %}</h4>
+<table>
+	<thead>
+		<tr>
+			<th>{% trans 'View Function' %}</th>
+			<th>{% trans 'args' %}</th>
+			<th>{% trans 'kwargs' %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		<tr>
+			<td>{{ view_func }}</td>
+			<td>{{ view_args|default:"None" }}</td>
+			<td>
+			{% if view_kwargs.items %}
+				{% for k, v in view_kwargs.items %}
+					{{ k }}={{ v }}{% if not forloop.last %}, {% endif %}
+				{% endfor %}
+			{% else %}
+				None
+			{% endif %}
+			</td>
+		</tr>
+	</tbody>
+</table>
+
+<h4>{% trans 'COOKIES Variables' %}</h4>
+{% if cookies %}
+	<table>
+		<colgroup>
+			<col style="width:20%"/>
+			<col/>
+		</colgroup>
+		<thead>
+			<tr>
+				<th>{% trans "Variable" %}</th>
+				<th>{% trans "Value" %}</th>
+			</tr>
+		</thead>
+		<tbody>
+			{% for key, value in cookies %}
+				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+					<td>{{ key|escape }}</td>
+					<td>{{ value|escape }}</td>
+				</tr>
+			{% endfor %}
+		</tbody>
+	</table>
+{% else %}
+	<p>{% trans "No COOKIE data" %}</p>
+{% endif %}
+
+<h4>{% trans 'SESSION Variables' %}</h4>
+{% if session %}
+	<table>
+		<colgroup>
+			<col style="width:20%"/>
+			<col/>
+		</colgroup>
+		<thead>
+			<tr>
+				<th>{% trans "Variable" %}</th>
+				<th>{% trans "Value" %}</th>
+			</tr>
+		</thead>
+		<tbody>
+			{% for key, value in session %}
+				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+					<td>{{ key|escape }}</td>
+					<td>{{ value|escape }}</td>
+				</tr>
+			{% endfor %}
+		</tbody>
+	</table>
+{% else %}
+	<p>{% trans "No SESSION data" %}</p>
+{% endif %}
+
+<h4>{% trans 'GET Variables' %}</h4>
+{% if get %}
+	<table>
+		<thead>
+			<tr>
+				<th>{% trans "Variable" %}</th>
+				<th>{% trans "Value" %}</th>
+			</tr>
+		</thead>
+		<tbody>
+			{% for key, value in get %}
+				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+					<td>{{ key|escape }}</td>
+					<td>{{ value|join:", "|escape }}</td>
+				</tr>
+			{% endfor %}
+		</tbody>
+	</table>
+{% else %}
+	<p>{% trans "No GET data" %}</p>
+{% endif %}
+
+<h4>{% trans 'POST Variables' %}</h4>
+{% if post %}
+	<table>
+		<thead>
+			<tr>
+				<th>{% trans "Variable" %}</th>
+				<th>{% trans "Value" %}</th>
+			</tr>
+		</thead>
+		<tbody>
+			{% for key, value in post %}
+				<tr class="{% cycle 'row1' 'row2' %}">
+					<td>{{ key|escape }}</td>
+					<td>{{ value|join:", "|escape }}</td>
+				</tr>
+			{% endfor %}
+		</tbody>
+	</table>
+{% else %}
+	<p>{% trans "No POST data" %}</p>
+{% endif %}

+ 17 - 0
templates/debug_toolbar/panels/settings_vars.html

@@ -0,0 +1,17 @@
+{% load i18n %}
+<table>
+	<thead>
+		<tr>
+			<th>{% trans "Setting" %}</th>
+			<th>{% trans "Value" %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for var in settings.items|dictsort:"0" %}
+			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+				<td>{{ var.0 }}</td>
+				<td><code>{{ var.1|pprint }}</code></td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>

+ 19 - 0
templates/debug_toolbar/panels/signals.html

@@ -0,0 +1,19 @@
+{% load i18n %}
+<table>
+	<thead>
+		<tr>
+			<th>{% trans "Signal" %}</th>
+			<th>{% trans 'Providing Args' %}</th>
+			<th>{% trans 'Receivers' %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for name, signal, receivers in signals %}
+			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+				<td>{{ name|escape }}</td>
+				<td>{{ signal.providing_args|join:", " }}</td>
+				<td>{{ receivers|join:", " }}</td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>

+ 87 - 0
templates/debug_toolbar/panels/sql.html

@@ -0,0 +1,87 @@
+{% load i18n %}
+<div class="clearfix">
+	<ul class="stats">
+		{% for alias, info in databases %}
+			<li>
+				<strong class="label"><span style="background-color: rgb({{ info.rgb_color|join:", " }})" class="color">&nbsp;</span> {{ alias }}</strong>
+				<span class="info">{{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %})</span>
+			</li>
+		{% endfor %}
+	</ul>
+</div>
+
+{% if queries %}
+	<table>
+		<thead>
+			<tr>
+				<th class="color">&nbsp;</th>
+				<th class="query" colspan="2">{% trans 'Query' %}</th>
+				<th class="timeline">{% trans 'Timeline' %}</th>
+				<th class="time">{% trans 'Time (ms)' %}</th>
+				<th class="actions">{% trans "Action" %}</th>
+			</tr>
+		</thead>
+		<tbody>
+			{% for query in queries %}
+				<tr class="djDebugHoverable {% cycle 'djDebugOdd' 'djDebugEven' %}{% if query.is_slow %} djDebugRowWarning{% endif %}{% if query.starts_trans %} djDebugStartTransaction{% endif %}{% if query.ends_trans %} djDebugEndTransaction{% endif %}{% if query.in_trans %} djDebugInTransaction{% endif %}" id="sqlMain_{{ forloop.counter }}">
+					<td class="color"><span style="background-color: rgb({{ query.rgb_color|join:", " }});">&nbsp;</span></td>
+					<td class="toggle">
+						<a class="djToggleSwitch" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">+</a>
+					</td>
+					<td class="query">
+						<div class="djDebugSqlWrap">
+							<div class="djDebugSql">{{ query.sql|safe }}</div>
+						</div>
+					</td>
+					<td class="timeline">
+						<div class="djDebugTimeline"><div class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" style="left:{{ query.start_offset }}%;"><strong style="width:{{ query.width_ratio }}%;">{{ query.width_ratio }}%</strong></div></div>
+					</td>
+					<td class="time">
+						{{ query.duration|floatformat:"2" }}
+					</td>
+					<td class="actions">
+					{% if query.params %}
+						{% if query.is_select %}
+							<a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}">Sel</a>
+							<a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}">Expl</a>
+							{% ifequal query.engine 'mysql' %}
+								<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}">Prof</a>
+							{% endifequal %}
+						{% endif %}
+					{% endif %}
+					</td>
+				</tr>
+				<tr class="djUnselected djDebugHoverable {% cycle 'djDebugOdd' 'djDebugEven' %}{% if query.is_slow %} djDebugRowWarning{% endif %} djToggleDetails_{{ forloop.counter }}" id="sqlDetails_{{ forloop.counter }}">
+					<td colspan="2"></td>
+					<td colspan="4">
+						<div class="djSQLDetailsDiv">
+							<p><strong>Connection:</strong> {{ query.alias }}</p>
+							{% if query.iso_level %}
+								<p><strong>Isolation Level:</strong> {{ query.iso_level }}</p>
+							{% endif %}
+							{% if query.trans_status %}
+								<p><strong>Transaction Status:</strong> {{ query.trans_status }}</p>
+							{% endif %}
+							{% if query.stacktrace %}
+								<pre class="stack">{{ query.stacktrace }}</pre>
+							{% endif %}
+							{% if query.template_info %}
+								<table>
+									{% for line in query.template_info.context %}
+									<tr>
+										<td>{{ line.num }}</td>
+										<td><code style="font-family: monospace;{% if line.highlight %}background-color: lightgrey{% endif %}">{{ line.content }}</code></td>
+									</tr>
+									{% endfor %}
+								</table>
+								<p><strong>{{ query.template_info.name|default:"(unknown)" }}</strong></p>
+							{% endif %}
+						</div>
+					</td>
+				</tr>
+			{% endfor %}
+		</tbody>
+	</table>
+{% else %}
+	<p>No SQL queries were recorded during this request.</p>
+{% endif %}

+ 33 - 0
templates/debug_toolbar/panels/sql_explain.html

@@ -0,0 +1,33 @@
+{% load i18n %}
+<div class="djDebugPanelTitle">
+	<a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a>
+	<h3>{% trans "SQL Explained" %}</h3>
+</div>
+<div class="djDebugPanelContent">
+	<div class="scroll">
+		<dl>
+			<dt>{% trans "Executed SQL" %}</dt>
+			<dd>{{ sql|safe }}</dd>
+			<dt>{% trans "Time" %}</dt>
+			<dd>{{ duration }} ms</dd>
+		</dl>
+		<table class="djSqlExplain">
+			<thead>
+				<tr>
+					{% for h in headers %}
+						<th>{{ h|upper }}</th>
+					{% endfor %}
+				</tr>
+			</thead>
+			<tbody>
+				{% for row in result %}
+					<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+						{% for column in row %}
+							<td>{{ column|escape }}</td>
+						{% endfor %}
+					</tr>
+				{% endfor %}
+			</tbody>
+		</table>
+	</div>
+</div>

+ 40 - 0
templates/debug_toolbar/panels/sql_profile.html

@@ -0,0 +1,40 @@
+{% load i18n %}
+<div class="djDebugPanelTitle">
+	<a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a>
+	<h3>{% trans "SQL Profiled" %}</h3>
+</div>
+<div class="djDebugPanelContent">
+	<div class="scroll">
+		{% if result %}
+			<dl>
+				<dt>{% trans "Executed SQL" %}</dt>
+				<dd>{{ sql|safe }}</dd>
+				<dt>{% trans "Time" %}</dt>
+				<dd>{{ duration }} ms</dd>
+			</dl>
+			<table class="djSqlProfile">
+				<thead>
+					<tr>
+						{% for h in headers %}
+							<th>{{ h|upper }}</th>
+						{% endfor %}
+					</tr>
+				</thead>
+				<tbody>
+					{% for row in result %}
+						<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+							{% for column in row %}
+								<td>{{ column|escape }}</td>
+							{% endfor %}
+						</tr>
+					{% endfor %}
+				</tbody>
+			</table>
+		{% else %}
+			<dl>
+				<dt>{% trans 'Error' %}</dt>
+				<dd>{{ result_error }}</dd>
+			</dl>
+		{% endif %}
+	</div>
+</div>

+ 37 - 0
templates/debug_toolbar/panels/sql_select.html

@@ -0,0 +1,37 @@
+{% load i18n %}
+<div class="djDebugPanelTitle">
+	<a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a>
+	<h3>{% trans "SQL Selected" %}</h3>
+</div>
+<div class="djDebugPanelContent">
+	<div class="scroll">
+		<dl>
+			<dt>{% trans "Executed SQL" %}</dt>
+			<dd>{{ sql|safe }}</dd>
+			<dt>{% trans "Time" %}</dt>
+			<dd>{{ duration }} ms</dd>
+		</dl>
+		{% if result %}
+		<table class="djSqlSelect">
+			<thead>
+				<tr>
+					{% for h in headers %}
+						<th>{{ h|upper }}</th>
+					{% endfor %}
+				</tr>
+			</thead>
+			<tbody>
+				{% for row in result %}
+					<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+						{% for column in row %}
+							<td>{{ column|escape }}</td>
+						{% endfor %}
+					</tr>
+				{% endfor %}
+			</tbody>
+		</table>
+		{% else %}
+			<p>{% trans "Empty set" %}</p>
+		{% endif %}
+	</div>
+</div>

+ 14 - 0
templates/debug_toolbar/panels/template_source.html

@@ -0,0 +1,14 @@
+{% load i18n %}
+<div class="djDebugPanelTitle">
+	<a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a>
+	<h3>{% trans 'Template Source' %}: <code>{{ template_name }}</code></h3>
+</div>
+<div class="djDebugPanelContent">
+	<div class="scroll">
+		{% if not source.pygmentized %}
+			<code>{{ source }}</code>
+		{% else %}
+			{{ source }}
+		{% endif %}
+	</div>
+</div>

+ 44 - 0
templates/debug_toolbar/panels/templates.html

@@ -0,0 +1,44 @@
+{% load i18n %}
+<h4>{% trans 'Template path' %}{{ template_dirs|length|pluralize }}</h4>
+{% if template_dirs %}
+	<ol>
+	{% for template in template_dirs %}
+		<li>{{ template }}</li>
+	{% endfor %}
+	</ol>
+{% else %}
+	<p>None</p>
+{% endif %}
+
+<h4>{% trans "Template" %}{{ templates|length|pluralize }}</h4>
+{% if templates %}
+<dl>
+{% for template in templates %}
+	<dt><strong><a class="remoteCall toggleTemplate" href="/__debug__/template_source/?template={{ template.template.name }}">{{ template.template.name|addslashes }}</a></strong></dt>
+	<dd><samp>{{ template.template.origin_name|addslashes }}</samp></dd>
+	{% if template.context %}
+	<dd>
+		<div class="djTemplateShowContextDiv"><a class="djTemplateShowContext"><span class="toggleArrow">&#x25B6;</span> {% trans 'Toggle Context' %}</a></div>
+		<div class="djTemplateHideContextDiv" style="display:none;"><code>{{ template.context }}</code></div>
+	</dd>
+	{% endif %}
+{% endfor %}
+</dl>
+{% else %}
+	<p>{% trans 'None' %}</p>
+{% endif %}
+
+<h4>{% trans 'Context processor' %}{{ context_processors|length|pluralize }}</h4>
+{% if context_processors %}
+<dl>
+{% for key, value in context_processors.iteritems %}
+	<dt><strong>{{ key|escape }}</strong></dt>
+	<dd>
+		<div class="djTemplateShowContextDiv"><a class="djTemplateShowContext"><span class="toggleArrow">&#x25B6;</span> {% trans 'Toggle Context' %}</a></div>
+		<div class="djTemplateHideContextDiv" style="display:none;"><code>{{ value|escape }}</code></div>
+	</dd>
+{% endfor %}
+</dl>
+{% else %}
+	<p>{% trans 'None' %}</p>
+{% endif %}

+ 21 - 0
templates/debug_toolbar/panels/timer.html

@@ -0,0 +1,21 @@
+{% load i18n %}
+<table>
+	<colgroup>
+		<col style="width:20%"/>
+		<col/>
+	</colgroup>
+	<thead>
+		<tr>
+			<th>{% trans "Resource" %}</th>
+			<th>{% trans "Value" %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for key, value in rows %}
+			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+				<td>{{ key|escape }}</td>
+				<td>{{ value|escape }}</td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>

+ 18 - 0
templates/debug_toolbar/panels/versions.html

@@ -0,0 +1,18 @@
+{% load i18n %}
+
+<table>
+	<thead>
+		<tr>
+			<th>{% trans "Package" %}</th>
+			<th>{% trans "Version" %}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for package, version in versions.iteritems %}
+			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
+				<td>{{ package }}</td>
+				<td>{{ version }}</td>
+			</tr>
+		{% endfor %}
+	</tbody>
+</table>

+ 12 - 0
templates/debug_toolbar/redirect.html

@@ -0,0 +1,12 @@
+{% load i18n %}
+<html>
+<head>
+</head>
+<body>
+<h1>HttpResponseRedirect</h1>
+<p>{% trans 'Location' %}: <a href="{{ redirect_to }}">{{ redirect_to }}</a></p>
+<p class="notice">
+	{% trans "The Django Debug Toolbar has intercepted a redirect to the above URL for debug viewing purposes.  You can click the above link to continue with the redirect as normal.  If you'd like to disable this feature, set the <code>DEBUG_TOOLBAR_CONFIG</code> dictionary's key <code>INTERCEPT_REDIRECTS</code> to <code>False</code>." %}
+</p>
+</body>
+</html>

+ 27 - 0
templates/sora/auth/forgot_password.html

@@ -0,0 +1,27 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% trans %}Request New Password{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span8 offset2">
+    <div class="page-header">
+      <h1>{% trans %}Request New Password{% endtrans %}</h1>
+    </div>
+    {% if message %}<div class="alert alert-form alert-error">
+      {% include message.tpl %}
+    </div>{% endif %}
+    <form action="{% url 'forgot_password' %}" method="post">
+      <div class="form-container">
+        {{ form_theme.form_widget(form, width=8) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Reset Password{% endtrans %}</button>
+      </div>
+    </form>
+  </div>
+</div>
+{% endblock %}

+ 27 - 0
templates/sora/auth/register.html

@@ -0,0 +1,27 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% trans %}Register new account{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span8 offset2">
+    <div class="page-header">
+      <h1>{% trans %}Register new account{% endtrans %}</h1>
+    </div>
+    {% if message %}<div class="alert alert-form alert-error">
+      {% include message.tpl %}
+    </div>{% endif %}
+    <form action="{% url 'register' %}" method="post">
+      <div class="form-container">
+        {{ form_theme.form_widget(form, width=8) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Register account{% endtrans %}</button>
+      </div>
+    </form>
+  </div>
+</div>
+{% endblock %}

+ 27 - 0
templates/sora/auth/resend_activation.html

@@ -0,0 +1,27 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% trans %}Request new Activation E-mail{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span8 offset2">
+    <div class="page-header">
+      <h1>{% trans %}Request new Activation E-mail{% endtrans %}</h1>
+    </div>
+    {% if message %}<div class="alert alert-form alert-error">
+      {% include message.tpl %}
+    </div>{% endif %}
+    <form action="{% url 'send_activation' %}" method="post">
+      <div class="form-container">
+        {{ form_theme.form_widget(form, width=8) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Request new E-mail{% endtrans %}</button>
+      </div>
+    </form>
+  </div>
+</div>
+{% endblock %}

+ 16 - 0
templates/sora/base.html

@@ -0,0 +1,16 @@
+{% from "sora/macros.html" import page_title -%}
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>{% block title %}{{ page_title() }}{% endblock %}</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link href="{{ STATIC_URL }}sora/css/sora.css" rel="stylesheet">{% block stylesheets %}{% endblock %}
+  </head>
+  <body{% block body_class %}{% endblock %}>
+  	{% block body %}{% endblock %}
+  	<script src="{{ STATIC_URL }}sora/js/jquery-1.7.2.min.js"></script>
+  	<script src="{{ STATIC_URL }}sora/js/bootstrap.min.js"></script>
+  	<script src="{{ STATIC_URL }}sora/js/sora.js"></script>{% block javascripts %}{% endblock %}
+  </body>
+</html>

+ 26 - 0
templates/sora/error403.html

@@ -0,0 +1,26 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Permission denied{% endtrans %} | {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span8 offset2 side-bar">
+    <div class="page-header">
+      <h1>{% if title %}{{ title }}{% else %}{% trans %}Permission denied{% endtrans %}{% endif %}</h1>
+    </div>
+{% if message %}{% set special=true %}
+    <div class="lead">
+      {% include message.tpl %}
+    </div>
+{% else %}
+    <p class="lead">{% trans %}You dont have permission to see this page.{% endtrans %}</p>
+{% endif %}
+    <ul class="unstyled">
+      <li><i class="icon-home"></i> <a href="{% url 'index' %}">{% trans %}Return to board index{% endtrans %}</a></li>
+      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
+{% endblock %}

+ 26 - 0
templates/sora/error404.html

@@ -0,0 +1,26 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Page not found{% endtrans %} | {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span8 offset2 side-bar">
+    <div class="page-header">
+      <h1>{% if title %}{{ title }}{% else %}{% trans %}Page not found{% endtrans %}{% endif %}</h1>
+    </div>
+{% if message %}{% set special=true %}
+    <div class="lead">
+      {% include message.tpl %}
+    </div>
+{% else %}
+    <p class="lead">{% trans %}The page you are looking for could not be found.{% endtrans %}</p>
+{% endif %}
+    <ul class="unstyled">
+      <li><i class="icon-home"></i> <a href="{% url 'index' %}">{% trans %}Return to board index{% endtrans %}</a></li>
+      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
+{% endblock %}

+ 17 - 0
templates/sora/index.html

@@ -0,0 +1,17 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% if settings.board_index_title %}{{ settings.board_index_title }}{% else %}{{ settings.board_name }}{% endif %}{% endblock %}
+     
+{% block meta %}{% if settings.board_index_meta %}
+<meta name="description" content="{{ settings.board_index_meta }}">
+{% endif %}{% endblock %}
+      
+{% block content %}
+<div class="page-header">
+  <h1>TODO: Misago Forum Index</h1>
+</div>
+<h2>Work in progress.</h2>
+<p>Check out <a href="http://area51.misago-project.org">project forums</a> if you want to discuss.</p>
+{% endblock %}

+ 37 - 0
templates/sora/layout.html

@@ -0,0 +1,37 @@
+{% extends "sora/base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block body %}
+{% include "sora/userbar.html" with context %}
+<div class="navbar navbar-fixed-top navbar-inverse navbar-header">
+  <div class="navbar-inner">
+    <div class="container">
+      <a class="brand" href="{% url 'index' %}">{% if settings.board_header %}{{ settings.board_header }}{% else %}{{ settings.board_name }}{% endif %}{% if settings.board_header_postscript %} <span>{{ settings.board_header_postscript }}</span>{% endif %}</a>
+      <ul class="nav">
+        <li><a href="{% url 'index' %}" title="{% trans %}Forum Home{% endtrans %}" class="tooltip-bottom"><i class="icon-comment"></i></a></li>{% if not user.crawler %}
+        <li><a href="#" title="{% trans %}Search Community{% endtrans %}" class="tooltip-bottom"><i class="icon-search"></i></a></li>{% endif %}
+        <li><a href="{% url 'users' %}" title="{% trans %}Browse Users{% endtrans %}" class="tooltip-bottom"><i class="icon-user"></i></a></li>
+      </ul>{% if not user.crawler %}
+      <form class="form-inline search-form">
+        <input type="text" class="span3" placeholder="{% trans %}Search community...{% endtrans %}">
+        <button type="submit" class="btn"><i class="icon-search"></i></button>
+      </form>{% endif %}
+    </div>
+  </div>
+</div>
+
+<div class="container">
+  {% if messages %}
+  <div class="alerts-global">{% for message in messages %}
+  	<div class="alert alert-{{ message.type }}">{% include message.tpl %}</div>
+  {% endfor %}</div>{% endif %}
+  
+  {% block content %}
+  {% endblock %}
+  
+  <footer>{% if settings.board_credits %}
+    <p>{{ settings.board_credits|safe }}</p>{% endif %}
+    <p class="software">This community is powered by <a href="http://misago-project.org">Misago forum software</a> by Rafał Pitoń</p>
+  </footer>
+</div>{% endblock %}

+ 5 - 0
templates/sora/macros.html

@@ -0,0 +1,5 @@
+{% load i18n %}
+
+{% macro page_title(title='', parent='', page=0) -%}
+{% if parent %}{{ parent }}: {% endif %}{% if title %}{{ title }}{% if page > 1 %} ({% trans page=page %}{{ page }} page{% endtrans %}){% endif %} | {% endif %}{{ settings.boart_title }}
+{%- endmacro %}

+ 40 - 0
templates/sora/signin.html

@@ -0,0 +1,40 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% trans %}Sign In{% endtrans %} | {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Sign In to Your Account{% endtrans %}</h1>
+</div>
+<div class="row">
+  <div class="span6">
+    <div class="well">
+	  {% if message %}<div class="alert alert-form alert-error">
+	    {% include message.tpl %}
+	  </div>{% endif %}
+	  <form class="form-horizontal" action="{% url 'sign_in' %}" method="post">
+	    <div class="form-container">
+	      {{ form_theme.form_widget(form, horizontal=true, width=6) }}
+	    </div>
+	    <div class="form-actions">
+	      <button type="submit" class="btn btn-primary">{% trans %}Sign In{% endtrans %}</button>
+	    </div>
+	  </form>
+	</div>
+  </div>
+  <div class="span6">  
+    <h3>{% trans %}I don't remember my password{% endtrans %}</h3>
+    <p><i class="icon-refresh"></i> <a href="{% url 'forgot_password' %}"><strong>{% trans %}I want to reset my account's password{% endtrans %}</strong></a></p>
+	  
+    <h3>{% trans %}I haven't received activation e-mail{% endtrans %}</h3>
+    <p><i class="icon-envelope"></i> <a href="{% url 'send_activation' %}"><strong>{% trans %}Resend me e-mail with activation link{% endtrans %}</strong></a></p>
+	  
+    {% if settings.account_activation != 'block' %}
+    <h3>{% trans %}I don't have account yet{% endtrans %}</h3>
+    <p><i class="icon-edit"></i> <a href="{% url 'register' %}"><strong>{% trans %}I want to register new account{% endtrans %}</strong></a></p>{% endif %}
+  </div>
+</div>
+{% endblock %}

+ 24 - 0
templates/sora/userbar.html

@@ -0,0 +1,24 @@
+{% if not user.crawler %}<div class="navbar navbar-fixed-top navbar-userbar">
+  <div class="navbar-inner">
+    <div class="container">
+      <ul class="nav">{% if user.is_authenticated() %}
+        <li class="user-profile"><a href="{% url 'user' user=user.id, username=user.username_slug %}" title="{% trans %}Go to your profile{% endtrans %}" class="tooltip-bottom"><div><img src="{{ user.get_avatar() }}" class="avatar-small" alt="{{ user.username }}" title="{{ user.username }}"> {{ user.username }}</div></a></li>
+        <li><a href="#" title="{% trans %}Go to Moderator Control Panel{% endtrans %}" class="tooltip-bottom"><i class="icon-tasks"></i> {% trans %}Mod CP{% endtrans %}</a></li>
+        <li><a href="{% url 'usercp' %}" title="{% trans %}Edit your profile options{% endtrans %}" class="tooltip-bottom"><i class="icon-cog"></i> {% trans %}Options{% endtrans %}</a></li>
+        <li><form action="{% url 'sign_out' %}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" title="{% trans %}Sign Out and browse as guest{% endtrans %}" class="btn btn-link tooltip-bottom"><i class="icon-off"></i> {% trans %}Sign Out{% endtrans %}</button></form></li>
+        {% else %}
+        <li><a href="{% url 'sign_in' %}" title="{% trans %}Sign In using your account data{% endtrans %}" class="tooltip-bottom"><i class="icon-check"></i> {% trans %}Sign In{% endtrans %}</a></li>{% if settings.account_activation != 'block' %}
+        <li><a href="{% url 'register' %}" title="{% trans %}Register new account{% endtrans %}" class="tooltip-bottom"><i class="icon-edit"></i> {% trans %}Register{% endtrans %}</a></li>{% endif %}
+      {% endif %}</ul>
+      <ul class="nav pull-right">{% if user.is_authenticated() %}
+        <li><a href="#" title="{% trans %}Notifications{% endtrans %}" class="tooltip-bottom"><i class="icon-fire"></i><span class="stat att">13</span></a></li>
+        <li><a href="#" title="{% trans %}Private messages{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="stat">2</span></a></li>
+        <li><a href="#" title="{% trans %}Reported posts{% endtrans %}" class="tooltip-bottom"><i class="icon-bell"></i><span class="stat">5</span></a></li>
+        <li><a href="#" title="{% trans %}People you are following{% endtrans %}" class="tooltip-bottom"><i class="icon-heart"></i></a></li>
+        <li><a href="#" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>{% endif %}
+        <li><a href="#" title="{% trans %}Today Posts{% endtrans %}" class="tooltip-bottom"><i class="icon-star"></i></a></li>
+        <li><a href="#" title="{% trans %}New Posts{% endtrans %}" class="tooltip-bottom"><i class="icon-star-empty "></i></a></li>
+      </ul>
+    </div>
+  </div>
+</div>{% endif %}

+ 12 - 0
templates/sora/users/profile.html

@@ -0,0 +1,12 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% trans username=profile.username %}Member Profile: {{ username }}{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1><img src="{{ profile.get_avatar() }}" class="avatar" alt="{% trans %}Member Avatar{% endtrans %}" title="{% trans %}Member Avatar{% endtrans %}"> {{ profile.username }} <small>{% trans %}Member Profile{% endtrans %}</small></h1>
+</div>
+{% endblock %}

+ 10 - 0
templates/sora/users/usercp/avatar.html

@@ -0,0 +1,10 @@
+{% extends "sora/users/usercp/usercp.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Change your Avatar{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+{{ super() }}
+<h2>{% trans %}Change your Avatar{% endtrans %}</h2>
+{% endblock %}

+ 10 - 0
templates/sora/users/usercp/credentials.html

@@ -0,0 +1,10 @@
+{% extends "sora/users/usercp/usercp.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Change Sign-In Credentials{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+{{ super() }}
+<h2>{% trans %}Change Sign-In Credentials{% endtrans %}</h2>
+{% endblock %}

+ 10 - 0
templates/sora/users/usercp/ignored.html

@@ -0,0 +1,10 @@
+{% extends "sora/users/usercp/usercp.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Manage Ignored Members{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+{{ super() }}
+<h2>{% trans %}Manage Ignored Members{% endtrans %}</h2>
+{% endblock %}

+ 10 - 0
templates/sora/users/usercp/options.html

@@ -0,0 +1,10 @@
+{% extends "sora/users/usercp/usercp.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Change Forum Options{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+{{ super() }}
+<h2>{% trans %}Change Forum Options{% endtrans %}</h2>
+{% endblock %}

+ 10 - 0
templates/sora/users/usercp/signature.html

@@ -0,0 +1,10 @@
+{% extends "sora/users/usercp/usercp.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Change your Signature{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+{{ super() }}
+<h2>{% trans %}Change your Signature{% endtrans %}</h2>
+{% endblock %}

+ 19 - 0
templates/sora/users/usercp/usercp.html

@@ -0,0 +1,19 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Options{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+<div class="page-header header-tabbed">
+  <h1>{% trans %}Your Control Panel{% endtrans %}</h1>
+  <ul class="nav nav-tabs">
+  	<li{% if tab == 'options' %} class="active"{% endif %}><a href="{% url 'usercp' %}"><i class="icon-cog"></i> {% trans %}Forum Options{% endtrans %}</a></li>
+  	<li{% if tab == 'credentials' %} class="active"{% endif %}><a href="{% url 'usercp_credentials' %}"><i class="icon-edit"></i> {% trans %}Email & Password{% endtrans %}</a></li>
+  	<li{% if tab == 'username' %} class="active"{% endif %}><a href="{% url 'usercp_username' %}"><i class="icon-user"></i> {% trans %}Username{% endtrans %}</a></li>
+  	<li{% if tab == 'avatar' %} class="active"{% endif %}><a href="{% url 'usercp_avatar' %}"><i class="icon-picture"></i> {% trans %}Avatar{% endtrans %}</a></li>
+  	<li{% if tab == 'signature' %} class="active"{% endif %}><a href="{% url 'usercp_signature' %}"><i class="icon-align-left"></i> {% trans %}Signature{% endtrans %}</a></li>
+  	<li{% if tab == 'ignored' %} class="active"{% endif %}><a href="{% url 'usercp_ignored' %}"><i class="icon-eye-close"></i> {% trans %}Ignored Members{% endtrans %}</a></li>
+  </ul>
+</div>
+{% endblock %}

+ 10 - 0
templates/sora/users/usercp/username.html

@@ -0,0 +1,10 @@
+{% extends "sora/users/usercp/usercp.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block title %}{% trans %}Change your Username{% endtrans %} | {{ settings.board_name }}{% endblock %}
+
+{% block content %}
+{{ super() }}
+<h2>{% trans %}Change your Username{% endtrans %}</h2>
+{% endblock %}