Merged with Santiago upstream.

This commit is contained in:
Nick Daly 2012-03-12 23:02:05 -05:00
commit 3e1340b8a8
79 changed files with 8923 additions and 649 deletions

143
COPYING
View File

@ -20,10 +20,10 @@ In default form, Plinth incorporates FileDict, a Python module
released under a "MIT/BSD/Python" license, as per [its blog
page](https://erezsh.wordpress.com/2009/05/31/filedict-bug-fixes-and-updates/).
## GNU General Public License, Version 3
## GNU Affero General Public License, Version 3
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
@ -31,17 +31,15 @@ page](https://erezsh.wordpress.com/2009/05/31/filedict-bug-fixes-and-updates/).
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@ -50,44 +48,34 @@ them 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 prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. 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.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
@ -96,7 +84,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@ -573,35 +561,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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
the GNU Affero 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 that a certain numbered version of the GNU General
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@ -659,44 +657,33 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
## Creative Commons Attribution-ShareAlike 3.0 Unported
License

24
NOTES Normal file
View File

@ -0,0 +1,24 @@
% PLINTH
%
% February 2012
# Edits by seandiggity
## 2012-02-27 new theme based upon bootstrap
Added Bootstrap code from Twitter's Bootstrap http://twitter.github.com/bootstrap
Additional images, css changes
Consolidated Bootstrap, Boilerplate, HTML5-Reset code
Edited text and forms for some modules (as a CMS, Plinth's fetching of text and forms from static files needs rethinking)
Changed menu javascript to work with Bootstrap styles
## 2012-02-24 template and theme changes
Added HTML5-Reset code http://html5reset.org
Removed robots.txt (we have "noindex,nofollow" in the template meta tags, do we need this?)
Added meta noindex,nofollow,noarchive tags for specific robots (googlebot etc.)
Removed extraneous meta tags useful only for indexing
Removed HTML5-Reset "_" directory. Not sure why it's useful to add an ambiguous folder to the directory tree.
Made sure no Google-y code wasn't included (analytics, remote copy of JQuery, etc.)
Fixed symbolic link docs/style.css
New favicon and iOS "web clip" button

View File

@ -12,7 +12,7 @@ santiago = os.path.join(data_dir, "santiago.sqlite3")
product_name = "Plinth"
box_name = "Freedom Plug"
box_name = "FreedomBox"
port = 8000

226
fabfile.py vendored Normal file
View File

@ -0,0 +1,226 @@
#!/usr/bin/env python
# This is the fabric file I've been using to deploy things on my box
# and my freedombox.
#
# fab install should take you from base freedom-maker install to
# plinth box
import os,sys, subprocess
import simplejson as json
import fabric.api
from fabric.api import local, env, cd, put, get, task
import cfg
fb_ip = "10.5.53.155"
BINDIR = "/usr/local/bin"
# defaults
env.user = 'root'
@task
def fb():
"Use this to set host to our freedombox (e.g.: fab fb deploy)"
env.hosts = [fb_ip]
@task
def all_hosts():
"Use this to set host to both localhost and freedombox"
env.hosts = ["localhost", "192.168.2.115"]
def remote_dir():
if env.host == fb_ip:
return "/usr/local/share/plinth"
else:
return "/home/james/src/plinth"
def run(*args, **kwargs):
if env.host == "localhost" or env.host=="127.0.0.1":
return local(*args, **kwargs)
else:
return fabric.api.run(*args, **kwargs)
def sudo(*args, **kwargs):
if env.host == "localhost" or env.host=="127.0.0.1":
return run("sudo %s" % args[0], *args[1:], **kwargs)
elif env.user == "root":
return run(*args, **kwargs)
else:
return fabric.api.sudo(*args, **kwargs)
@task
def get_remote_data_dir():
with cd(remote_dir()):
data_dir = run('python -c "import cfg; print cfg.data_dir"')
env.remote_data_dir = os.path.join(remote_dir(), data_dir)
sudo('mkdir -p %s' % env.remote_data_dir)
return env.remote_data_dir
@task
def move_data():
"Move install's data dir to where cfg specifies it should be"
get_remote_data_dir()
with cd(remote_dir()):
sudo('mv data %s' % os.path.split(env.remote_data_dir)[0])
@task
def make():
"Run the makefile, which generates docs and templates"
with cd(remote_dir()):
sudo('make')
def make_link_unless_exists(src, dest):
sudo('test -f %s || ln -s %s %s' % (dest, src, dest))
def link(src, dest):
sudo('ln -fs %s %s' % (src, dest))
@task
def santiago():
"Setup the Santiago port"
santiago_port = 52854
sudo('ifconfig lo up') # or else tor start fails
sudo('apt-get install -y --no-install-recommends tor curl ntp')
# tor needs accurate clock
sudo('date -s "%s"' % subprocess.check_output("date").rstrip())
# create tor hidden service dir
santiago_dir = os.path.join(get_remote_data_dir(), "santiago", "tor")
tor_dir = os.path.join(santiago_dir, "general")
sudo("mkdir -p " + tor_dir)
sudo("chown debian-tor:debian-tor " + tor_dir)
# ensure hidden service config is in torrc
local("rm -rf __fab__torrc")
get("/etc/tor/torrc", "__fab__torrc")
with open ("__fab__torrc", 'r') as INF:
rc = INF.read()
local("rm -rf __fab__torrc")
hidden_service_config = "HiddenServiceDir %s\nHiddenServicePort 80 127.0.0.1:%d" % (tor_dir, santiago_port)
if not hidden_service_config in rc:
sudo("echo '%s' >> /etc/tor/torrc" % hidden_service_config)
sudo('service tor restart')
def backslash_path(f):
if not f.startswith('/'):
f = os.path.abs(f)
if f == '/':
return ''
path, ret = os.path.split(f)
return backslash_path(path) + '\/' + ret
@task
def apache():
"configure apache to find reverse proxy for plinth"
sudo('apt-get install --no-install-recommends -y apache2 libapache2-mod-proxy-html apache2-utils openssl ssl-cert')
sudo('a2enmod proxy_http rewrite ssl')
sudo('touch /var/log/apache2/rewrite.log')
## ssl key and cert
ssl_target = "/etc/apache2/ssl/apache.pem"
sudo('mkdir -p %s' % os.path.split(ssl_target)[0])
sudo('test -f %s || echo "US\nNY\nNYC\nFBox\n\n\n" | openssl req -new -x509 -days 999 -nodes -out %s -keyout %s' % (ssl_target, ssl_target, ssl_target))
conf_path = os.path.join(remote_dir(), "share/apache2/plinth.conf")
sudo("mkdir -p " + os.path.split(conf_path)[0])
sudo("touch "+ conf_path)
sudo(r"sed -i 's/\(\s*\)DocumentRoot.*/\1DocumentRoot %s/g' %s" % (
backslash_path(os.path.join(remote_dir(), "static")),
conf_path))
link(conf_path, "/etc/apache2/sites-enabled/plinth.conf")
sudo('rm -f /etc/apache2/sites-enabled/000-default')
sudo('service apache2 restart')
@task
def deps():
"Basic plinth dependencies"
sudo('apt-get install --no-install-recommends -y python make python-cheetah pandoc python-simplejson python-pyme')
@task
def update():
"Copy modified git-tracked files from this branch to remote"
with cd(remote_dir()):
## Get .fab contents
sudo("touch .fab")
fab = run("cat .fab")
if not fab:
fab = {}
## Make list of files to put
try:
fab = json.loads(fab)
except:
fab={}
branch = [a[2:] for a in local("git branch", capture=True).split("\n") if a.startswith('*')][0]
files = local("git ls-tree -r --name-only %s" % branch, capture=True).split("\n")
else:
files = local("git diff --stat " + fab['last_update_from_commit'], capture=True).split("\n")[:-1]
files = [f.lstrip().split("|")[0].rstrip() for f in files]
## Put the files, one by one, respecting directories
dirs = {}
for pathspec in files:
d,fname = os.path.split(pathspec)
if not d in dirs.keys():
dirs[d]=[]
dirs[d].append(pathspec)
if dirs:
sudo('mkdir -p %s' % ' '.join([os.path.join(remote_dir(), d) for d in dirs.keys()]))
for d in dirs:
for f in dirs[d]:
if os.path.islink(f):
linked = local("ls -l %s" % f, capture=True).split("-> ")[1]
#link(os.path.join(remote_dir(), linked), os.path.join(remote_dir(), d, os.path.basename(f)))
put(f, os.path.join(remote_dir(), d),mirror_local_mode=True)
if f.endswith(".py"):
run("rm -f " + os.path.join(remote_dir(), d, os.path.basename)+"c")
## restart
make()
sudo('/etc/init.d/plinth restart')
## Record activity so we only put changed files next time
commit = local("git log -n 1", capture=True).split("\n")[0].split(" ")[1]
fab['last_update_from_commit'] = commit
with open(".fab", 'w') as OUTF:
OUTF.write(json.dumps(fab))
put(".fab", os.path.join(remote_dir(),".fab"))
local("rm -f .fab")
@task
def link_bin():
"Link executable and init.d script"
# todo: set daemon to point to currect binary
sudo('rm -rf ' + os.path.join(BINDIR, 'plinth.py'))
link(os.path.join(remote_dir(), "plinth.py"), os.path.join(BINDIR, 'plinth.py'))
sudo('rm -rf /etc/init.d/plinth')
link(os.path.join(remote_dir(), "share/init.d/plinth"), "/etc/init.d/plinth")
@task
def restart():
"Run plinth"
run('/etc/init.d/plinth restart')
@task
def stop():
"Stop plinth"
run('/etc/init.d/plinth stop')
@task
def proxy():
put("proxy_up.py", remote_dir())
@task
def deploy():
"Deploy plinth"
deps()
link_bin()
santiago()
update()
apache()

19
menu.py
View File

@ -5,11 +5,15 @@ import cfg
class Menu():
"""One menu item."""
def __init__(self, label="", url="#", order=50):
def __init__(self, label="", icon="", url="#", order=50):
"""label is the text that is displayed on the menu.
icon is the icon to be displayed next to the label.
Choose from the Glyphicon set:
http://twitter.github.com/bootstrap/base-css.html#icons
url is the url location that will be activated when the menu
item is selected
item is selected.
order is the numerical rank of this item within the menu.
Lower order items appear closest to the top/left of the menu.
@ -20,8 +24,9 @@ class Menu():
"""
self.label = label
self.order = order
self.icon = icon
self.url = url
self.order = order
self.items = []
def sort_items(self):
@ -36,7 +41,7 @@ class Menu():
if basehref and url.startswith("/"):
url = cfg.base_href + url
item = Menu(label=label, url=url, order=order)
item = Menu(label=label, icon=icon, url=url, order=order)
self.items.append(item)
self.sort_items()
return item
@ -61,7 +66,7 @@ class Menu():
so = []
for item in self.items:
i = { 'label':item.label, 'url':item.url}
i = { 'label':item.label, 'icon':item.icon, 'url':item.url}
if item.active_p():
i['active']=True
if item.items and render_subs:
@ -74,7 +79,7 @@ class Menu():
if render_subs is True, we render submenus too"""
return ('<SCRIPT LANGUAGE="JavaScript">\n <!--\n var %s_items=' % name
return ('<script type="text/javascript">\n <!--\n var %s_items=' % name
#+ json.dumps(self.serializable(render_subs=render_subs), separators=(',',':')) # compact
+ "\n"+ json.dumps(self.serializable(render_subs=render_subs), sort_keys=True, indent=4) # pretty print
+ ';\n // -->\n </SCRIPT>')
+ ';\n // -->\n </script>')

View File

@ -7,8 +7,8 @@ class Apps(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("apps")
self.menu = cfg.main_menu.add_item("User Apps", "/apps", 80)
self.menu.add_item("Photo Gallery", "/apps/photos", 35)
self.menu = cfg.main_menu.add_item("Apps", "icon-download-alt", "/apps", 80)
self.menu.add_item("Photo Gallery", "icon-picture", "/apps/photos", 35)
@cherrypy.expose
def index(self):
@ -27,7 +27,7 @@ class Apps(PagePlugin):
@require()
def photos(self):
return self.fill_template(title="Open ID", main='', sidebar_right="""
<h2>Photo Gallery</h2><p>Your photos might well be the most valuable
<strong>Photo Gallery</strong><p>Your photos might well be the most valuable
digital property you have, so why trust it to companies that have no
investment in the sentimental value of your family snaps? Keep those
photos local, backed up, easily accessed and free from the whims of

View File

@ -8,12 +8,12 @@ class Help(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("help")
self.menu = cfg.main_menu.add_item(_("Documentation and FAQ"), "/help", 101)
self.menu.add_item(_("Where to Get Help"), "/help/index", 5)
self.menu.add_item(_("Developer's Manual"), "/help/view/plinth", 10)
self.menu.add_item(_("FAQ"), "/help/view/faq", 20)
self.menu.add_item(_("%s Wiki" % cfg.box_name), "http://wiki.debian.org/FreedomBox", 30)
self.menu.add_item(_("About"), "/help/about", 100)
self.menu = cfg.main_menu.add_item(_("Documentation"), "icon-book", "/help", 101)
self.menu.add_item(_("Where to Get Help"), "icon-search", "/help/index", 5)
self.menu.add_item(_("Developer's Manual"), "icon-info-sign", "/help/view/plinth", 10)
self.menu.add_item(_("FAQ"), "icon-question-sign", "/help/view/faq", 20)
self.menu.add_item(_("%s Wiki" % cfg.box_name), "icon-pencil", "http://wiki.debian.org/FreedomBox", 30)
self.menu.add_item(_("About"), "icon-star", "/help/about", 100)
@cherrypy.expose
def index(self):
@ -27,7 +27,7 @@ class Help(PagePlugin):
offer suggestions, edits, and screenshots for completing
it!</p>
<p><a href="http://wiki.debian.org/FreedomBox">A section of
<p><a href="http://wiki.debian.org/FreedomBox" target="_blank">A section of
the Debian wiki</a> is devoted to the %(box)s. At some
point the documentation in the wiki and the documentation in
the manual should dovetail.</p>
@ -48,8 +48,9 @@ class Help(PagePlugin):
@cherrypy.expose
def about(self):
return self.fill_template(title=_("About the %s" % cfg.box_name), main="""
<p> We live in a world where our use of the network is
mediated by organizations that often do not have our best
<img src="/static/theme/img/freedombox-logo-250px.png" class="main-graphic" />
<p>We live in a world where our use of the network is
mediated by those who often do not have our best
interests at heart. By building software that does not rely on
a central service, we can regain control and privacy. By
keeping our data in our homes, we gain useful legal
@ -62,14 +63,15 @@ class Help(PagePlugin):
runs on must be cheap. The software it runs on must be easy to
install and administrate by anybody. It must be easy to
transition from existing services.</p>
<p>There are a number of projects working to realize a future
<p><a class="btn btn-primary btn-large" href="http://wiki.debian.org/FreedomBox" target="_blank">Learn more &raquo;</a></p>""",
sidebar_right=_("""<strong>Our Goal</strong><p>There are a number of projects working to realize a future
of distributed services; we aim to bring them all together in
a convenient package.</p>
<p>For more information about the Freedom Box project, see the
<p>For more information about the FreedomBox project, see the
<a href="http://wiki.debian.org/FreedomBox">Debian
Wiki</a>.</p>""")
Wiki</a>.</p>
"""))
class View(PagePlugin):
def __init__(self, *args, **kwargs):

View File

@ -50,9 +50,9 @@ class Form():
quad = [0,0,0,0]
self.text += """ <label>
<span>%(label)s</span>
<input type="text" class="inputtextnowidth" name="%(name)s0" id="%(id)s0" value="%(q0)s" maxlength="3" size="1"/><b>.</b>
<input type="text" class="inputtextnowidth" name="%(name)s1" id="%(id)s1" value="%(q1)s" maxlength="3" size="1"/><b>.</b>
<input type="text" class="inputtextnowidth" name="%(name)s2" id="%(id)s2" value="%(q2)s" maxlength="3" size="1"/><b>.</b>
<input type="text" class="inputtextnowidth" name="%(name)s0" id="%(id)s0" value="%(q0)s" maxlength="3" size="1"/><strong>.</strong>
<input type="text" class="inputtextnowidth" name="%(name)s1" id="%(id)s1" value="%(q1)s" maxlength="3" size="1"/><strong>.</strong>
<input type="text" class="inputtextnowidth" name="%(name)s2" id="%(id)s2" value="%(q2)s" maxlength="3" size="1"/><strong>.</strong>
<input type="text" class="inputtextnowidth" name="%(name)s3" id="%(id)s3" value="%(q3)s" maxlength="3" size="1"/>
</label>""" % {'label':label, 'name':name, 'id':id, 'q0':quad[0], 'q1':quad[1], 'q2':quad[2], 'q3':quad[3]}
@ -77,7 +77,7 @@ class Form():
self.text += """
<div class="submit">
<label><span></span>
<input type="submit" class="button" value="%s" name="%s" id="%s" />
<input type="submit" class="btn-primary" value="%s" name="%s" id="%s" />
</label></div>\n""" % (label, name, id)
def submit_row(self, buttons):
"""buttons is a list of tuples, each containing label, name, id. Name and id are optional."""
@ -99,7 +99,7 @@ class Form():
if button_text != '':
button_text += "&nbsp;"
button_text += '<input type="submit" class="button" value="%s" name="%s" id="%s" />\n' % (label, name, id)
button_text += '<input type="submit" class="btn-primary" value="%s" name="%s" id="%s" />\n' % (label, name, id)
self.text += '%s</div></label>' % button_text
def name_or_id(self, name, id):
if not name: name = id

View File

@ -9,11 +9,11 @@ class Privacy(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("privacy")
self.menu = cfg.main_menu.add_item("Privacy Controls", "/privacy", 12)
self.menu.add_item("General Config", "/privacy/config", 10)
self.menu.add_item("Ad Blocking", "/privacy/adblock", 20)
self.menu.add_item("TOR", "/privacy/TOR", 30)
self.menu.add_item("HTTPS Everywhere", "/privacy/https_everywhere", 30)
self.menu = cfg.main_menu.add_item("Privacy", "icon-eye-open", "/privacy", 12)
self.menu.add_item("General Config", "icon-asterisk", "/privacy/config", 10)
self.menu.add_item("Ad Blocking", "icon-ban-circle", "/privacy/adblock", 20)
self.menu.add_item("TOR", "icon-eye-close", "/privacy/TOR", 30)
self.menu.add_item("HTTPS Everywhere", "icon-lock", "/privacy/https_everywhere", 30)
@cherrypy.expose
def index(self):
@ -29,7 +29,7 @@ class Privacy(PagePlugin):
is a founding consideration, not an afterthought.</p>
"""
return self.fill_template(title=_("Privacy Control Panel"), main=main,
sidebar_right=_("""<h2>Statement of Principles</h2><p>When we say your
sidebar_right=_("""<strong>Statement of Principles</strong><p>When we say your
privacy is important, it's not just an empty pleasantry. We really
mean it. Your privacy control panel should give you fine-grained
control over exactly who can access your %s and the
@ -37,6 +37,6 @@ information on it.</p>
<p>Your personal information should not leave this box without your
knowledge and direction. And if companies or government wants this
information, they have to ask <b>you</b> for it. This gives you a
information, they have to ask <strong>you</strong> for it. This gives you a
change to refuse and also tells you who wants your data.</p>
""") % cfg.product_name)

View File

@ -14,5 +14,5 @@ class Info(PagePlugin):
@require()
def index(self):
return self.fill_template(title="Router Information", main="""
<p> Eventually we will display a bunch of info, graphs and logs about the routing functions here.</p>
<p>Eventually we will display a bunch of info, graphs and logs about the routing functions here.</p>
""")

View File

@ -11,11 +11,11 @@ class router(PagePlugin):
order = 9 # order of running init in PagePlugins
def __init__(self, *args, **kwargs):
self.register_page("router")
self.menu = cfg.main_menu.add_item("Router Admin", "/router", 10)
self.menu.add_item("Wireless", "/router/wireless", 12)
self.menu.add_item("Firewall", "/router/firewall", 18)
self.menu.add_item("Hotspot and Mesh", "/router/hotspot")
self.menu.add_item("Info", "/router/info", 100)
self.menu = cfg.main_menu.add_item("Router", "icon-retweet", "/router", 10)
self.menu.add_item("Wireless", "icon-signal", "/router/wireless", 12)
self.menu.add_item("Firewall", "icon-fire", "/router/firewall", 18)
self.menu.add_item("Hotspot and Mesh", "icon-map-marker", "/router/hotspot")
self.menu.add_item("Info", "icon-list-alt", "/router/info", 100)
@cherrypy.expose
def index(self):
@ -45,16 +45,16 @@ class router(PagePlugin):
class setup(PagePlugin):
def __init__(self, *args, **kwargs):
self.register_page("router.setup")
self.menu = cfg.html_root.router.menu.add_item("General Setup", "/router/setup", 10)
self.menu.add_item("Dynamic DNS", "/router/setup/ddns", 20)
self.menu.add_item("MAC Address Clone", "/router/setup/mac_address", 30)
self.menu = cfg.html_root.router.menu.add_item("General Setup", "icon-cog", "/router/setup", 10)
self.menu.add_item("Dynamic DNS", "icon-flag", "/router/setup/ddns", 20)
self.menu.add_item("MAC Address Clone", "icon-barcode", "/router/setup/mac_address", 30)
@cherrypy.expose
@require()
def index(self):
parts = self.forms('/router/setup')
parts['title'] = "General Router Setup"
parts['sidebar_right']="""<h2>Introduction</h2><p>Your %s is a replacement for your
parts['sidebar_right']="""<strong>Introduction</strong><p>Your %s is a replacement for your
wireless router. By default, it should do everything your usual
router does. With the addition of some extra modules, its abilities
can rival those of high-end routers costing hundreds of dollars.</p>
@ -88,7 +88,7 @@ class wan(FormPlugin, PagePlugin):
url = ["/router/setup"]
order = 10
js = """<script LANGUAGE="JavaScript">
js = """<script type="text/javascript">
<!--
function hideshow_static() {
var d = document.getElementById('connect_type');
@ -105,7 +105,7 @@ class wan(FormPlugin, PagePlugin):
def sidebar_right(self, *args, **kwargs):
side=''
if cfg.users.expert():
side += """<h2>WAN Connection Type</h2>
side += """<strong>WAN Connection Type</strong>
<h3>DHCP</h3><p>DHCP allows your router to automatically
connect with the upstream network. If you are unsure what
option to choose, stick with DHCP. It is usually
@ -150,7 +150,7 @@ class wan(FormPlugin, PagePlugin):
form.dotted_quad("Static DNS 2", name="dns2", quad=[dns20, dns21, dns22, dns23])
form.dotted_quad("Static DNS 3", name="dns3", quad=[dns30, dns31, dns32, dns33])
form.html('</div>')
form.html(""" <script LANGUAGE="JavaScript">
form.html(""" <script type="text/javascript">
<!--
hideshow_static();
// -->

View File

@ -114,7 +114,7 @@ class Santiago(PagePlugin):
class santiago(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.menu = cfg.html_root.privacy.menu.add_item("Santiago", "/privacy/santiago", 10)
self.menu = cfg.html_root.privacy.menu.add_item("Santiago", "icon-leaf", "/privacy/santiago", 10)
self.register_page("privacy.santiago")
@cherrypy.expose

View File

@ -8,8 +8,8 @@ class Services(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("services")
self.menu = cfg.main_menu.add_item("Other Services", "/services", 90)
self.menu.add_item("Open ID", "/services/openid", 35)
self.menu = cfg.main_menu.add_item("Services", "icon-list", "/services", 90)
self.menu.add_item("Open ID", "icon-user", "/services/openid", 35)
@cherrypy.expose
def index(self):
@ -19,7 +19,7 @@ class Services(PagePlugin):
@require()
def openid(self):
return self.fill_template(title="Open ID", main='', sidebar_right="""
<h2>One Login for Every Site</h2><p>Your %s is also an OpenID
<strong>One Login for Every Site</strong><p>Your %s is also an OpenID
machine. It can generate credentials that allow you to log in to many
websites without the need to remember or enter a separate username and
password at each one.</p>

View File

@ -7,7 +7,7 @@ class FileExplorer(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("sharing.explorer")
cfg.html_root.sharing.menu.add_item("File Explorer", "/sharing/explorer", 30)
cfg.html_root.sharing.menu.add_item("File Explorer", "icon-folder-open", "/sharing/explorer", 30)
@cherrypy.expose
@require()

View File

@ -10,8 +10,8 @@ class Sharing(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("sharing")
self.menu = cfg.main_menu.add_item("Resource Sharing", "/sharing", 35)
self.menu.add_item("File Server", "/sharing/files", 10)
self.menu = cfg.main_menu.add_item("Sharing", "icon-share-alt", "/sharing", 35)
self.menu.add_item("File Server", "icon-inbox", "/sharing/files", 10)
@cherrypy.expose
def index(self):
@ -25,7 +25,7 @@ class Sharing(PagePlugin):
@require()
def files(self):
return self.fill_template(title="File Server", main='', sidebar_right=_("""
<h2>Freedom NAS</h2><p> The %s can make your spare hard drives accessible to your
<strong>Freedom NAS</strong><p> The %s can make your spare hard drives accessible to your
local network, thus acting as a NAS server. We currently support
sharing files via NFS and SMB.
@ -37,7 +37,7 @@ class PrinterSharing(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("sharing.printer")
cfg.html_root.sharing.menu.add_item("Printer Sharing", "/sharing/printer", 35)
cfg.html_root.sharing.menu.add_item("Printer Sharing", "icon-print", "/sharing/printer", 35)
@cherrypy.expose
@require()
@ -47,7 +47,7 @@ class PrinterSharing(PagePlugin):
<p>TODO: Setup and install CUPS</p>
"""
return self.fill_template(title="Printer Sharing", main=main, sidebar_right="""
<h2>Share Your Printer</h2><p> The %s can share your printer via Samba and CUPS.</p>
<strong>Share Your Printer</strong><p> The %s can share your printer via Samba and CUPS.</p>
""" % cfg.box_name)

View File

@ -64,7 +64,7 @@ class general(FormPlugin, PagePlugin):
## only expert users are going to get deep enough to see any timestamps
if not cfg.users.expert():
return ''
return _(#"""<h2>Time Zone</h2>
return _(#"""<strong>Time Zone</strong>
"""<p>Set your timezone to get accurate
timestamps. %(product)s will use this information to set your
%(box)s's systemwide timezone.</p>

View File

@ -15,7 +15,7 @@ class experts(FormPlugin, PagePlugin):
order = 10
def help(self, *args, **kwargs):
side = _(#"""<h2>Expert Mode</h2>
side = _(#"""<strong>Expert Mode</strong>
"""
<p>The %(box)s can be administered in two modes, 'basic'
and 'expert'. Basic mode hides a lot of features and

View File

@ -18,10 +18,10 @@ class Sys(PagePlugin):
def __init__(self, *args, **kwargs):
PagePlugin.__init__(self, *args, **kwargs)
self.register_page("sys")
self.menu = cfg.main_menu.add_item(_("%s System" % cfg.product_name), "/sys", 100)
self.menu.add_item(_("Configure"), "/sys/config", 10)
self.menu.add_item(_("Package Manager"), "/sys/packages", 20)
self.menu.add_item(_("Users and Groups"), "/sys/users", 15)
self.menu = cfg.main_menu.add_item(_("System"), "icon-cog", "/sys", 100)
self.menu.add_item(_("Configure"), "icon-cog", "/sys/config", 10)
self.menu.add_item(_("Package Manager"), "icon-gift", "/sys/packages", 20)
self.menu.add_item(_("Users and Groups"), "icon-user", "/sys/users", 15)
@cherrypy.expose
def index(self):
@ -37,7 +37,7 @@ class Sys(PagePlugin):
@require()
def packages(self):
side=_("""
<h2>Help</h2>
<strong>Help</strong>
<p>On this page, you can add or remove %s plugins to your %s.</p>
<p>Plugins are just Debian packages, so Debian's usual package management features should make this job fairly easy.</p>
""" % (cfg.product_name, cfg.box_name))

View File

@ -24,8 +24,8 @@ class add(FormPlugin, PagePlugin):
order = 30
sidebar_left = ''
sidebar_right = _("""<h2>Add User</h2><p>Adding a user via this
administrative interface <b>might</b> create a system user.
sidebar_right = _("""<strong>Add User</strong><p>Adding a user via this
administrative interface <strong>might</strong> create a system user.
For example, if you provide a user with ssh access, she will
need a system account. If you don't know what that means,
don't worry about it.</p>""")
@ -70,16 +70,16 @@ class edit(FormPlugin, PagePlugin):
order = 35
sidebar_left = ''
sidebar_right = _("""<h2>Edit Users</h2><p>Click on a user's name to
go to a screen for editing that user's account.</p><h2>Delete
Users</h2><p>Check the box next to a users' names and then click
sidebar_right = _("""<strong>Edit Users</strong><p>Click on a user's name to
go to a screen for editing that user's account.</p><strong>Delete
Users</strong><p>Check the box next to a users' names and then click
"Delete User" to remove users from %s and the %s
system.</p><p>Deleting users is permanent!</p>""" % (cfg.product_name, cfg.box_name))
def main(self, msg=''):
users = cfg.users.keys()
add_form = Form(title=_("Edit or Delete User"), action="/sys/users/edit", message=msg)
add_form.html('<span class="indent"><b>Delete</b><br /></span>')
add_form.html('<span class="indent"><strong>Delete</strong><br /></span>')
for uname in sorted(users.keys()):
add_form.html('<span class="indent">&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' %
add_form.get_checkbox(name=uname) +
@ -123,6 +123,6 @@ class edit(FormPlugin, PagePlugin):
return self.fill_template(template="err", title=_("Unnown User"), main=main,
sidebar_left=self.sidebar_left, sidebar_right=sidebar_right)
main = _("""<h2>Edit User '%s'</h2>""" % u['username'])
main = _("""<strong>Edit User '%s'</strong>""" % u['username'])
sidebar_right = ''
return self.fill_template(title="", main=main, sidebar_left=self.sidebar_left, sidebar_right=sidebar_right)

View File

@ -22,13 +22,13 @@ class wan(FormPlugin, PagePlugin):
end will be reachable from the WAN. If your %(box)s
connects you to the internet, that means you'll be able to log
in to the front end from the internet. This might be
convenient, but it is also <b>dangerous</b>, since it can
convenient, but it is also <strong>dangerous</strong>, since it can
enable attackers to gain access to your %(box)s from the
outside world. All they'll need is your username and
passphrase, which they might guess or they might simply try
every posible combination of letters and numbers until they
get in. If you enable the WAN administration option, you
<b>must</b> use long and complex passphrases.</p>
<strong>must</strong> use long and complex passphrases.</p>
<p>For security reasons, neither WAN Administration nor WAN
SSH is available to the `admin` user account.</p>

View File

@ -40,7 +40,7 @@ def error_page_500(status, message, traceback, version):
cfg.log.error("500 Internal Server Error. Trackback is above.")
more="""<p>This is an internal error and not something you caused
or can fix. Please report the error on the <a
href="https://github.com/jvasile/Plinth/issues">bug tracker</a> so
href="https://github.com/seandiggity/Plinth/issues">bug tracker</a> so
we can fix it.</p>"""
return error_page(status, message, "<p>%s</p><pre>%s</pre>" % (more, "\n".join(traceback.split("\n"))))
@ -68,7 +68,7 @@ def load_modules():
cfg.log("skipping %s" % name)
def parse_arguments():
parser = argparse.ArgumentParser(description='Plinht web interface for the FreedomBox.')
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
parser.add_argument('--pidfile', default="",
help='specify a file in which the server may write its pid')
args=parser.parse_args()

View File

@ -5,27 +5,105 @@
$default
#end if
#end def
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB">
<!doctype html>
<!--[if lt IE 7 ]> <html class="ie ie6 no-js" lang="en"> <![endif]-->
<!--[if IE 7 ]> <html class="ie ie7 no-js" lang="en"> <![endif]-->
<!--[if IE 8 ]> <html class="ie ie8 no-js" lang="en"> <![endif]-->
<!--[if IE 9 ]> <html class="ie ie9 no-js" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<!-- the "no-js" class is for Modernizr -->
<head>
<title>$default($title, "Plinth Front End to the Freedom Box")
</title>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
<meta name="description" content="Plint admin frontend for Freedom Box" />
<meta name="robots" content="noindex, nofollow" />
<link rel="shortcut icon" href="$basehref/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="$basehref/static/theme/style.tiny.css" media="screen" />
$css
<script type="text/javascript" src="$basehref/static/theme/menu.js"></script>
<script type="text/javascript" src="$basehref/static/theme/plinth.js"></script>
$js
$main_menu_js
$sub_menu_js
<script LANGUAGE="JavaScript">
<!--
$onload
// -->
</script>
<meta charset="utf-8" />
<!-- Always force latest IE rendering engine and Chrome Frame -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<!-- Mobile Viewport Fix http://j.mp/mobileviewport & http://davidbcalhoun.com/2010/viewport-metatag
device-width : Occupy full width of the screen in its current orientation
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- R2D2, you know better than to trust a strange computer! -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<meta name="googlebot" content="noindex, nofollow, noarchive, nosnippet, noodp, noimageindex, notranslate" />
<meta name="msnbot" content="noindex, nofollow, noarchive, noodp" />
<meta name="slurp" content="noindex, nofollow, noarchive, noodp, noydir" />
<meta name="title" content="$default($title, 'FreedomBox Dashboard')" />
<meta name="description" content="Plinth administrative interface for the FreedomBox" />
<title>$default($title, "FreedomBox Dashboard")</title>
<!-- This is the traditional favicon. Size: 16x16 or 32x32, transparency is OK -->
<link rel="shortcut icon" href="$basehref/static/theme/img/favicon.ico" />
<!-- The is the icon for iOS's Web Clip. Size: 57x57 for older iPhones, 72x72 for iPads, 114x114 for iPhone4
- To prevent iOS from applying its styles to the icon name it thusly: apple-touch-icon-precomposed.png
- Transparency is not recommended (iOS will put a black BG behind the icon) -->
<link rel="apple-touch-icon" sizes="57x57" href="$basehref/static/theme/img/apple-touch-icon-57px-precomposed.png" />
<link rel="apple-touch-icon" sizes="72x72" href="$basehref/static/theme/img/apple-touch-icon-72px-precomposed.png" />
<link rel="apple-touch-icon" sizes="114x114" href="$basehref/static/theme/img/apple-touch-icon-114px-precomposed.png" />
<!-- Bootstrap base CSS -->
<link rel="stylesheet" href="$basehref/static/theme/css/bootstrap.css" />
<style type="text/css">
/* custom styles, load before bootstrap responsive styles */
body {
padding-top:60px;
padding-bottom:40px;
}
.sidebar-nav {
padding: 9px 0;
}
</style>
<!-- Bootstrap responsive CSS -->
<link rel="stylesheet" href="$basehref/static/theme/css/bootstrap-responsive.css" />
<style type="text/css">
/* custom styles, load after all bootstrap styles */
.super-hero {
margin-top:25px;
}
.logo-top {
float:left;
padding-right:5px;
}
.brand {
padding-top:8px;
}
.white {
color:#fff;
}
.error-large {
font-size:1.2em;
padding:10px;
}
.main-graphic {
float:right;
padding:25px;
}
.nav-icon {
margin-right:8px;
}
.sidenav-icon {
margin-right:8px;
padding-right:5px;
}
</style>
<!-- CSS from previous Plinth template, not sure what to keep yet -->
$css
<!-- End Plinth CSS -->
<!-- JS from previous Plinth template, not sure what to keep yet -->
<script type="text/javascript" src="$basehref/static/theme/js/menu.js"></script>
<script type="text/javascript" src="$basehref/static/theme/js/plinth.js"></script>
$js
$main_menu_js
$sub_menu_js
<script type="text/javascript">
<!--
$onload
// -->
</script>
</head>
<body onLoad="javascript:onload_handler();">
<div id="header">
@ -47,66 +125,110 @@
#else
<p id="layoutdims">Not logged in. <a href="$basehref/auth/login" title="Log in">Log in.</a></p>
#end if
</div>
<div class="colmask threecol">
<div class="colmid">
<div class="colleft">
<div class="col1">
<!-- Column 1 start -->
<h2>
#block title_block
$title
#end block title_block
</h2>
#block main_block
$main
#end block main_block
<!-- Column 1 end -->
</div>
<div class="col2">
<!-- Column 2 start -->
#block nav_block
$nav
#end block nav_block
#block sidebar_left_block
$sidebar_left
#end block sidebar_left_block
<!-- Column 2 end -->
</div>
<div class="col3">
<!-- Column 3 start -->
<div id="ads">
<!--<a href="http://matthewjamestaylor.com">
<img src="mjt-125x125.gif" width="125" border="0" height="125" alt="Art and Design by Matthew James Taylor" />
</a>
-->
</div>
#block sidebar_right_block
$sidebar_right
#end block sidebar_right_block
<!-- Column 3 end -->
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a href="$basehref/" class="logo-top"><img src="$basehref/static/theme/img/freedombox-logo-32px.png" alt="FreedomBox" /></a><a class="brand" href="$basehref/">FreedomBox Dashboard</a>
<div class="nav-collapse">
<script type="text/javascript">
<!--
main_menu(main_menu_items);
// -->
</script>
#if $username
<p class="navbar-text pull-right"><i class="icon-user icon-white nav-icon"></i>Logged in as <a href="$username">$username</a>. <a href="$basehref/auth/logout" title="Log out">Log out</a>.</p>
#else
<p class="navbar-text pull-right">Not logged in. <i class="icon-user icon-white nav-icon"></i><a href="$basehref/auth/login" title="Log in">Log in</a>.</p>
#end if
</div><!--/.nav-collapse -->
</div>
</div>
</div>
</div>
</div>
<div id="footer">
#block footer_block
<p>
Plinth is copyright 2011 <a href="http://hackervisions.org">James Vasile</a>. It is
free software offered to you under the terms of
the <a href="http://www.gnu.org/licenses/agpl.html">GNU Affero General Public
License</a>, Version 3 or later.
</p>
<!--<p>Current page: $current_url</p>-->
<p>
This page uses
the <a href="http://matthewjamestaylor.com/blog/perfect-3-column.htm">Perfect
'Holy Grail' 3 Column Liquid Layout</a>
by <a href="http://matthewjamestaylor.com">Matthew James Taylor</a>.
</p>
#end block footer_block
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span3">
<div class="well sidebar-nav">
#block nav_block
$nav
#end block nav_block
#block sidebar_left_block
$sidebar_left
#end block sidebar_left_block
</div><!--/.well -->
</div><!--/span-->
<div class="span9">
<div class="hero-unit">
<h2>
#block title_block
$title
#end block title_block
</h2>
#block main_block
$main
#end block main_block
</div>
<div class="row-fluid">
<div class="span8 alert alert-info">
#block sidebar_right_block
$sidebar_right
#end block sidebar_right_block
</div><!--/span-->
</div><!--/row-->
</div><!--/span-->
</div><!--/row-->
<hr>
<footer>
<p>#block footer_block
<p>
Plinth is &copy; Copyright 2012 <a href="http://hackervisions.org" target="_blank">James Vasile</a>. It is
free software offered to you under the terms of
the <a href="http://www.gnu.org/licenses/agpl.html" target="_blank">GNU Affero General Public
License</a>, Version 3 or later. This Plinth theme was built by <a href="http://seandiggity.com" target="_blank">Sean &quot;Diggity&quot; O&apos;Brien</a>.
</p>
<p>Current page: $current_url</p>
<p>
</p>
#end block footer_block</p>
</footer>
</div><!--/.fluid-container-->
<!-- JavaScript <script> tags are placed at the end of the document to speed up initial page loads-->
<!-- Local copy of HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script type="text/javascript" src="$basehref/static/theme/js/libs/html5.js"></script>
<![endif]-->
<!-- Local copy of Modernizr -->
<script type="text/javascript" src="$basehref/static/theme/js/libs/modernizr-2.5.3-respond-1.1.0.min.js"></script>
<!-- Local copy of jQuery -->
<script type="text/javascript" src="$basehref/static/theme/js/libs/jquery-1.7.1.min.js"></script>
<!-- Bootstrap JS, most files commented out until we know what we need -->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/bootstrap.js"></script>-->
<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/bootstrap-min.js"></script>
<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/button.js"></script>
<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/dropdown.js"></script>
<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/collapse.js"></script>
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/transition.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/alert.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/modal.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/scrollspy.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/tab.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/tooltip.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/popover.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/carousel.js"></script>-->
<!--<script type="text/javascript" src="$basehref/static/theme/js/libs/bootstrap/typeahead.js"></script>-->
<!-- JS plugins -->
<script type="text/javascript" src="$basehref/static/theme/js/plugins.js"></script>
</body>
</html>

View File

@ -1,7 +1,7 @@
#extends templates.two_col
#def title_block
<span class="err">Error: $title</span>
<span class="label label-important error-large">Error: $title</span>
<br />
<br />
#end def

View File

@ -1,5 +1,6 @@
#extends templates.base
#def css
<link rel="stylesheet" type="text/css" href="$basehref/static/theme/2col.tiny.css" media="screen" />
<!-- CSS from previous template, not sure what to keep yet -->
<!--<link rel="stylesheet" href="$basehref/static/theme/style-plinth-2col.css" />-->
#end def

View File

@ -1,30 +0,0 @@
/* 2 Column settings */
.colright {
float:left;
width:0%; /* width of page */
position:relative;
}
.threecol .colmid {
right:5%; /* width of the right column */
}
.threecol .colleft {
right:70%; /* width of the middle column */
}
.threecol .col1 {
width:66%; /* width of center column content (column width minus padding on either side) */
left:102%; /* 100% plus left padding of center column */
}
.threecol .col2 {
width:21%; /* Width of left column content (column width minus padding on either side) */
left:11%; /* width of (right column) plus (center column left and right padding) plus (left column left padding) */
}
.threecol .col3 {
width:21%; /* Width of right column content (column width minus padding on either side) */
/* Note this used to be 85%, but I subtracted 1% for padding to pull stuff closer to the margin */
left:84%; /* Please make note of the brackets here:
(100% - left column width) plus (center column left and right padding) plus (left column left and right padding) plus (right column left padding) */
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,581 @@
/*!
* Bootstrap Responsive v2.0.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.
*/
.clearfix {
*zoom: 1;
}
.clearfix:before, .clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.hidden {
display: none;
visibility: hidden;
}
@media (max-width: 480px) {
.nav-collapse {
-webkit-transform: translate3d(0, 0, 0);
}
.page-header h1 small {
display: block;
line-height: 18px;
}
input[class*="span"],
select[class*="span"],
textarea[class*="span"],
.uneditable-input {
display: block;
width: 100%;
min-height: 28px;
/* Make inputs at least the height of their button counterpart */
/* Makes inputs behave like true block-level elements */
-webkit-box-sizing: border-box;
/* Older Webkit */
-moz-box-sizing: border-box;
/* Older FF */
-ms-box-sizing: border-box;
/* IE8 */
box-sizing: border-box;
/* CSS3 spec*/
}
.input-prepend input[class*="span"], .input-append input[class*="span"] {
width: auto;
}
input[type="checkbox"], input[type="radio"] {
border: 1px solid #ccc;
}
.form-horizontal .control-group > label {
float: none;
width: auto;
padding-top: 0;
text-align: left;
}
.form-horizontal .controls {
margin-left: 0;
}
.form-horizontal .control-list {
padding-top: 0;
}
.form-horizontal .form-actions {
padding-left: 10px;
padding-right: 10px;
}
.modal {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
width: auto;
margin: 0;
}
.modal.fade.in {
top: auto;
}
.modal-header .close {
padding: 10px;
margin: -10px;
}
.carousel-caption {
position: static;
}
}
@media (max-width: 767px) {
.container {
width: auto;
padding: 0 20px;
}
.row-fluid {
width: 100%;
}
.row {
margin-left: 0;
}
.row > [class*="span"], .row-fluid > [class*="span"] {
float: none;
display: block;
width: auto;
margin: 0;
}
}
@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;
}
.span1 {
width: 42px;
}
.span2 {
width: 104px;
}
.span3 {
width: 166px;
}
.span4 {
width: 228px;
}
.span5 {
width: 290px;
}
.span6 {
width: 352px;
}
.span7 {
width: 414px;
}
.span8 {
width: 476px;
}
.span9 {
width: 538px;
}
.span10 {
width: 600px;
}
.span11 {
width: 662px;
}
.span12, .container {
width: 724px;
}
.offset1 {
margin-left: 82px;
}
.offset2 {
margin-left: 144px;
}
.offset3 {
margin-left: 206px;
}
.offset4 {
margin-left: 268px;
}
.offset5 {
margin-left: 330px;
}
.offset6 {
margin-left: 392px;
}
.offset7 {
margin-left: 454px;
}
.offset8 {
margin-left: 516px;
}
.offset9 {
margin-left: 578px;
}
.offset10 {
margin-left: 640px;
}
.offset11 {
margin-left: 702px;
}
.row-fluid {
width: 100%;
*zoom: 1;
}
.row-fluid:before, .row-fluid:after {
display: table;
content: "";
}
.row-fluid:after {
clear: both;
}
.row-fluid > [class*="span"] {
float: left;
margin-left: 2.762430939%;
}
.row-fluid > [class*="span"]:first-child {
margin-left: 0;
}
.row-fluid > .span1 {
width: 5.801104972%;
}
.row-fluid > .span2 {
width: 14.364640883%;
}
.row-fluid > .span3 {
width: 22.928176794%;
}
.row-fluid > .span4 {
width: 31.491712705%;
}
.row-fluid > .span5 {
width: 40.055248616%;
}
.row-fluid > .span6 {
width: 48.618784527%;
}
.row-fluid > .span7 {
width: 57.182320438000005%;
}
.row-fluid > .span8 {
width: 65.74585634900001%;
}
.row-fluid > .span9 {
width: 74.30939226%;
}
.row-fluid > .span10 {
width: 82.87292817100001%;
}
.row-fluid > .span11 {
width: 91.436464082%;
}
.row-fluid > .span12 {
width: 99.999999993%;
}
input.span1, textarea.span1, .uneditable-input.span1 {
width: 32px;
}
input.span2, textarea.span2, .uneditable-input.span2 {
width: 94px;
}
input.span3, textarea.span3, .uneditable-input.span3 {
width: 156px;
}
input.span4, textarea.span4, .uneditable-input.span4 {
width: 218px;
}
input.span5, textarea.span5, .uneditable-input.span5 {
width: 280px;
}
input.span6, textarea.span6, .uneditable-input.span6 {
width: 342px;
}
input.span7, textarea.span7, .uneditable-input.span7 {
width: 404px;
}
input.span8, textarea.span8, .uneditable-input.span8 {
width: 466px;
}
input.span9, textarea.span9, .uneditable-input.span9 {
width: 528px;
}
input.span10, textarea.span10, .uneditable-input.span10 {
width: 590px;
}
input.span11, textarea.span11, .uneditable-input.span11 {
width: 652px;
}
input.span12, textarea.span12, .uneditable-input.span12 {
width: 714px;
}
}
@media (max-width: 979px) {
body {
padding-top: 0;
}
.navbar-fixed-top {
position: static;
margin-bottom: 18px;
}
.navbar-fixed-top .navbar-inner {
padding: 5px;
}
.navbar .container {
width: auto;
padding: 0;
}
.navbar .brand {
padding-left: 10px;
padding-right: 10px;
margin: 0 0 0 -5px;
}
.navbar .nav-collapse {
clear: left;
}
.navbar .nav {
float: none;
margin: 0 0 9px;
}
.navbar .nav > li {
float: none;
}
.navbar .nav > li > a {
margin-bottom: 2px;
}
.navbar .nav > .divider-vertical {
display: none;
}
.navbar .nav .nav-header {
color: #999999;
text-shadow: none;
}
.navbar .nav > li > a, .navbar .dropdown-menu a {
padding: 6px 15px;
font-weight: bold;
color: #999999;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.navbar .dropdown-menu li + li a {
margin-bottom: 2px;
}
.navbar .nav > li > a:hover, .navbar .dropdown-menu a:hover {
background-color: #222222;
}
.navbar .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;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.navbar .dropdown-menu:before, .navbar .dropdown-menu:after {
display: none;
}
.navbar .dropdown-menu .divider {
display: none;
}
.navbar-form, .navbar-search {
float: none;
padding: 9px 15px;
margin: 9px 0;
border-top: 1px solid #222222;
border-bottom: 1px solid #222222;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
}
.navbar .nav.pull-right {
float: none;
margin-left: 0;
}
.navbar-static .navbar-inner {
padding-left: 10px;
padding-right: 10px;
}
.btn-navbar {
display: block;
}
.nav-collapse {
overflow: hidden;
height: 0;
}
}
@media (min-width: 980px) {
.nav-collapse.collapse {
height: auto !important;
}
}
@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;
}
.span1 {
width: 70px;
}
.span2 {
width: 170px;
}
.span3 {
width: 270px;
}
.span4 {
width: 370px;
}
.span5 {
width: 470px;
}
.span6 {
width: 570px;
}
.span7 {
width: 670px;
}
.span8 {
width: 770px;
}
.span9 {
width: 870px;
}
.span10 {
width: 970px;
}
.span11 {
width: 1070px;
}
.span12, .container {
width: 1170px;
}
.offset1 {
margin-left: 130px;
}
.offset2 {
margin-left: 230px;
}
.offset3 {
margin-left: 330px;
}
.offset4 {
margin-left: 430px;
}
.offset5 {
margin-left: 530px;
}
.offset6 {
margin-left: 630px;
}
.offset7 {
margin-left: 730px;
}
.offset8 {
margin-left: 830px;
}
.offset9 {
margin-left: 930px;
}
.offset10 {
margin-left: 1030px;
}
.offset11 {
margin-left: 1130px;
}
.row-fluid {
width: 100%;
*zoom: 1;
}
.row-fluid:before, .row-fluid:after {
display: table;
content: "";
}
.row-fluid:after {
clear: both;
}
.row-fluid > [class*="span"] {
float: left;
margin-left: 2.564102564%;
}
.row-fluid > [class*="span"]:first-child {
margin-left: 0;
}
.row-fluid > .span1 {
width: 5.982905983%;
}
.row-fluid > .span2 {
width: 14.529914530000001%;
}
.row-fluid > .span3 {
width: 23.076923077%;
}
.row-fluid > .span4 {
width: 31.623931624%;
}
.row-fluid > .span5 {
width: 40.170940171000005%;
}
.row-fluid > .span6 {
width: 48.717948718%;
}
.row-fluid > .span7 {
width: 57.264957265%;
}
.row-fluid > .span8 {
width: 65.81196581200001%;
}
.row-fluid > .span9 {
width: 74.358974359%;
}
.row-fluid > .span10 {
width: 82.905982906%;
}
.row-fluid > .span11 {
width: 91.45299145300001%;
}
.row-fluid > .span12 {
width: 100%;
}
input.span1, textarea.span1, .uneditable-input.span1 {
width: 60px;
}
input.span2, textarea.span2, .uneditable-input.span2 {
width: 160px;
}
input.span3, textarea.span3, .uneditable-input.span3 {
width: 260px;
}
input.span4, textarea.span4, .uneditable-input.span4 {
width: 360px;
}
input.span5, textarea.span5, .uneditable-input.span5 {
width: 460px;
}
input.span6, textarea.span6, .uneditable-input.span6 {
width: 560px;
}
input.span7, textarea.span7, .uneditable-input.span7 {
width: 660px;
}
input.span8, textarea.span8, .uneditable-input.span8 {
width: 760px;
}
input.span9, textarea.span9, .uneditable-input.span9 {
width: 860px;
}
input.span10, textarea.span10, .uneditable-input.span10 {
width: 960px;
}
input.span11, textarea.span11, .uneditable-input.span11 {
width: 1060px;
}
input.span12, textarea.span12, .uneditable-input.span12 {
width: 1160px;
}
.thumbnails {
margin-left: -30px;
}
.thumbnails > li {
margin-left: 30px;
}
}

3496
themes/default/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,23 @@
// remap jQuery to $
(function($){})(window.jQuery);
/* trigger when page is ready */
$(document).ready(function (){
// your functions go here
});
/* optional triggers
$(window).load(function() {
});
$(window).resize(function() {
});
*/

View File

@ -0,0 +1,91 @@
/* ==========================================================
* bootstrap-alert.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ){
"use strict"
/* ALERT CLASS DEFINITION
* ====================== */
var dismiss = '[data-dismiss="alert"]'
, Alert = function ( el ) {
$(el).on('click', dismiss, this.close)
}
Alert.prototype = {
constructor: Alert
, close: function ( e ) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.trigger('close')
e && e.preventDefault()
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
$parent.removeClass('in')
function removeElement() {
$parent.remove()
$parent.trigger('closed')
}
$.support.transition && $parent.hasClass('fade') ?
$parent.on($.support.transition.end, removeElement) :
removeElement()
}
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('alert')
if (!data) $this.data('alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.alert.Constructor = Alert
/* ALERT DATA-API
* ============== */
$(function () {
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
})
}( window.jQuery )

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,98 @@
/* ============================================================
* bootstrap-button.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
/* BUTTON PUBLIC CLASS DEFINITION
* ============================== */
var Button = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.button.defaults, options)
}
Button.prototype = {
constructor: Button
, setState: function ( state ) {
var d = 'disabled'
, $el = this.$element
, data = $el.data()
, val = $el.is('input') ? 'val' : 'html'
state = state + 'Text'
data.resetText || $el.data('resetText', $el[val]())
$el[val](data[state] || this.options[state])
// push to event loop to allow forms to submit
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
, toggle: function () {
var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
$parent && $parent
.find('.active')
.removeClass('active')
this.$element.toggleClass('active')
}
}
/* BUTTON PLUGIN DEFINITION
* ======================== */
$.fn.button = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('button')
, options = typeof option == 'object' && option
if (!data) $this.data('button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
$.fn.button.defaults = {
loadingText: 'loading...'
}
$.fn.button.Constructor = Button
/* BUTTON DATA-API
* =============== */
$(function () {
$('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
$(e.target).button('toggle')
})
})
}( window.jQuery )

View File

@ -0,0 +1,154 @@
/* ==========================================================
* bootstrap-carousel.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ){
"use strict"
/* CAROUSEL CLASS DEFINITION
* ========================= */
var Carousel = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.carousel.defaults, options)
this.options.slide && this.slide(this.options.slide)
}
Carousel.prototype = {
cycle: function () {
this.interval = setInterval($.proxy(this.next, this), this.options.interval)
return this
}
, to: function (pos) {
var $active = this.$element.find('.active')
, children = $active.parent().children()
, activePos = children.index($active)
, that = this
if (pos > (children.length - 1) || pos < 0) return
if (this.sliding) {
return this.$element.one('slid', function () {
that.to(pos)
})
}
if (activePos == pos) {
return this.pause().cycle()
}
return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
}
, pause: function () {
clearInterval(this.interval)
return this
}
, next: function () {
if (this.sliding) return
return this.slide('next')
}
, prev: function () {
if (this.sliding) return
return this.slide('prev')
}
, slide: function (type, next) {
var $active = this.$element.find('.active')
, $next = next || $active[type]()
, isCycling = this.interval
, direction = type == 'next' ? 'left' : 'right'
, fallback = type == 'next' ? 'first' : 'last'
, that = this
this.sliding = true
isCycling && this.pause()
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
if (!$.support.transition && this.$element.hasClass('slide')) {
this.$element.trigger('slide')
$active.removeClass('active')
$next.addClass('active')
this.sliding = false
this.$element.trigger('slid')
} else {
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
$next.addClass(direction)
this.$element.trigger('slide')
this.$element.one($.support.transition.end, function () {
$next.removeClass([type, direction].join(' ')).addClass('active')
$active.removeClass(['active', direction].join(' '))
that.sliding = false
setTimeout(function () { that.$element.trigger('slid') }, 0)
})
}
isCycling && this.cycle()
return this
}
}
/* CAROUSEL PLUGIN DEFINITION
* ========================== */
$.fn.carousel = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('carousel')
, options = typeof option == 'object' && option
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (typeof option == 'string' || (option = options.slide)) data[option]()
else data.cycle()
})
}
$.fn.carousel.defaults = {
interval: 5000
}
$.fn.carousel.Constructor = Carousel
/* CAROUSEL DATA-API
* ================= */
$(function () {
$('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
$target.carousel(options)
e.preventDefault()
})
})
}( window.jQuery )

View File

@ -0,0 +1,136 @@
/* =============================================================
* bootstrap-collapse.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
var Collapse = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.collapse.defaults, options)
if (this.options["parent"]) {
this.$parent = $(this.options["parent"])
}
this.options.toggle && this.toggle()
}
Collapse.prototype = {
constructor: Collapse
, dimension: function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
, show: function () {
var dimension = this.dimension()
, scroll = $.camelCase(['scroll', dimension].join('-'))
, actives = this.$parent && this.$parent.find('.in')
, hasData
if (actives && actives.length) {
hasData = actives.data('collapse')
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
this.$element[dimension](0)
this.transition('addClass', 'show', 'shown')
this.$element[dimension](this.$element[0][scroll])
}
, hide: function () {
var dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', 'hide', 'hidden')
this.$element[dimension](0)
}
, reset: function ( size ) {
var dimension = this.dimension()
this.$element
.removeClass('collapse')
[dimension](size || 'auto')
[0].offsetWidth
this.$element.addClass('collapse')
}
, transition: function ( method, startEvent, completeEvent ) {
var that = this
, complete = function () {
if (startEvent == 'show') that.reset()
that.$element.trigger(completeEvent)
}
this.$element
.trigger(startEvent)
[method]('in')
$.support.transition && this.$element.hasClass('collapse') ?
this.$element.one($.support.transition.end, complete) :
complete()
}
, toggle: function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
}
/* COLLAPSIBLE PLUGIN DEFINITION
* ============================== */
$.fn.collapse = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('collapse')
, options = typeof option == 'object' && option
if (!data) $this.data('collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.collapse.defaults = {
toggle: true
}
$.fn.collapse.Constructor = Collapse
/* COLLAPSIBLE DATA-API
* ==================== */
$(function () {
$('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
var $this = $(this), href
, target = $this.attr('data-target')
|| e.preventDefault()
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
, option = $(target).data('collapse') ? 'toggle' : $this.data()
$(target).collapse(option)
})
})
}( window.jQuery )

View File

@ -0,0 +1,92 @@
/* ============================================================
* bootstrap-dropdown.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
, Dropdown = function ( element ) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function ( e ) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
, isActive
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
isActive = $parent.hasClass('open')
clearMenus()
!isActive && $parent.toggleClass('open')
return false
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}( window.jQuery )

View File

@ -0,0 +1,209 @@
/* =========================================================
* bootstrap-modal.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
!function( $ ){
"use strict"
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function ( content, options ) {
this.options = $.extend({}, $.fn.modal.defaults, options)
this.$element = $(content)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
}
Modal.prototype = {
constructor: Modal
, toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
if (this.isShown) return
$('body').addClass('modal-open')
this.isShown = true
this.$element.trigger('show')
escape.call(this)
backdrop.call(this, function () {
var transition = $.support.transition && that.$element.hasClass('fade')
!that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position
that.$element
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
transition ?
that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
that.$element.trigger('shown')
})
}
, hide: function ( e ) {
e && e.preventDefault()
if (!this.isShown) return
var that = this
this.isShown = false
$('body').removeClass('modal-open')
escape.call(this)
this.$element
.trigger('hide')
.removeClass('in')
$.support.transition && this.$element.hasClass('fade') ?
hideWithTransition.call(this) :
hideModal.call(this)
}
}
/* MODAL PRIVATE METHODS
* ===================== */
function hideWithTransition() {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
hideModal.call(that)
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
hideModal.call(that)
})
}
function hideModal( that ) {
this.$element
.hide()
.trigger('hidden')
backdrop.call(this)
}
function backdrop( callback ) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if (this.options.backdrop != 'static') {
this.$backdrop.click($.proxy(this.hide, this))
}
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
removeBackdrop.call(this)
} else if (callback) {
callback()
}
}
function removeBackdrop() {
this.$backdrop.remove()
this.$backdrop = null
}
function escape() {
var that = this
if (this.isShown && this.options.keyboard) {
$(document).on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
$(document).off('keyup.dismiss.modal')
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
$.fn.modal = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('modal')
, options = typeof option == 'object' && option
if (!data) $this.data('modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else data.show()
})
}
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
}
$.fn.modal.Constructor = Modal
/* MODAL DATA-API
* ============== */
$(function () {
$('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
e.preventDefault()
$target.modal(option)
})
})
}( window.jQuery )

View File

@ -0,0 +1,95 @@
/* ===========================================================
* bootstrap-popover.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================================================== */
!function( $ ) {
"use strict"
var Popover = function ( element, options ) {
this.init('popover', element, options)
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
constructor: Popover
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
$tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
$tip.removeClass('fade top bottom left right in')
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
content = content.toString().replace(/(^\s*|\s*$)/, "")
return content
}
, tip: function() {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.popover.Constructor = Popover
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, 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 )

View File

@ -0,0 +1,125 @@
/* =============================================================
* bootstrap-scrollspy.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================== */
!function ( $ ) {
"use strict"
/* SCROLLSPY CLASS DEFINITION
* ========================== */
function ScrollSpy( element, options) {
var process = $.proxy(this.process, this)
, $element = $(element).is('body') ? $(window) : $(element)
, href
this.options = $.extend({}, $.fn.scrollspy.defaults, options)
this.$scrollElement = $element.on('scroll.scroll.data-api', process)
this.selector = (this.options.target
|| ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|| '') + ' .nav li > a'
this.$body = $('body').on('click.scroll.data-api', this.selector, process)
this.refresh()
this.process()
}
ScrollSpy.prototype = {
constructor: ScrollSpy
, refresh: function () {
this.targets = this.$body
.find(this.selector)
.map(function () {
var href = $(this).attr('href')
return /^#\w/.test(href) && $(href).length ? href : null
})
this.offsets = $.map(this.targets, function (id) {
return $(id).position().top
})
}
, process: function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& this.activate( targets[i] )
}
}
, activate: function (target) {
var active
this.activeTarget = target
this.$body
.find(this.selector).parent('.active')
.removeClass('active')
active = this.$body
.find(this.selector + '[href="' + target + '"]')
.parent('li')
.addClass('active')
if ( active.parent('.dropdown-menu') ) {
active.closest('li.dropdown').addClass('active')
}
}
}
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
$.fn.scrollspy = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('scrollspy')
, options = typeof option == 'object' && option
if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.scrollspy.Constructor = ScrollSpy
$.fn.scrollspy.defaults = {
offset: 10
}
/* SCROLLSPY DATA-API
* ================== */
$(function () {
$('[data-spy="scroll"]').each(function () {
var $spy = $(this)
$spy.scrollspy($spy.data())
})
})
}( window.jQuery )

View File

@ -0,0 +1,130 @@
/* ========================================================
* bootstrap-tab.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ======================================================== */
!function( $ ){
"use strict"
/* TAB CLASS DEFINITION
* ==================== */
var Tab = function ( element ) {
this.element = $(element)
}
Tab.prototype = {
constructor: Tab
, show: function () {
var $this = this.element
, $ul = $this.closest('ul:not(.dropdown-menu)')
, selector = $this.attr('data-target')
, previous
, $target
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
if ( $this.parent('li').hasClass('active') ) return
previous = $ul.find('.active a').last()[0]
$this.trigger({
type: 'show'
, relatedTarget: previous
})
$target = $(selector)
this.activate($this.parent('li'), $ul)
this.activate($target, $target.parent(), function () {
$this.trigger({
type: 'shown'
, relatedTarget: previous
})
})
}
, activate: function ( element, container, callback) {
var $active = container.find('> .active')
, transition = callback
&& $.support.transition
&& $active.hasClass('fade')
function next() {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
element.addClass('active')
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in')
} else {
element.removeClass('fade')
}
if ( element.parent('.dropdown-menu') ) {
element.closest('li.dropdown').addClass('active')
}
callback && callback()
}
transition ?
$active.one($.support.transition.end, next) :
next()
$active.removeClass('in')
}
}
/* TAB PLUGIN DEFINITION
* ===================== */
$.fn.tab = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tab')
if (!data) $this.data('tab', (data = new Tab(this)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tab.Constructor = Tab
/* TAB DATA-API
* ============ */
$(function () {
$('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
e.preventDefault()
$(this).tab('show')
})
})
}( window.jQuery )

View File

@ -0,0 +1,270 @@
/* ===========================================================
* bootstrap-tooltip.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ) {
"use strict"
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function ( element, options ) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function ( type, element, options ) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function ( options ) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function ( e ) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) {
self.show()
} else {
self.hoverState = 'in'
setTimeout(function() {
if (self.hoverState == 'in') {
self.show()
}
}, self.options.delay.show)
}
}
, leave: function ( e ) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.hide) {
self.hide()
} else {
self.hoverState = 'out'
setTimeout(function() {
if (self.hoverState == 'out') {
self.hide()
}
}, self.options.delay.hide)
}
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
$tip.find('.tooltip-inner').html(this.getTitle())
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
}
, fixTitle: function () {
var $e = this.$element
if ($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 (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
title = title.toString().replace(/(^\s*|\s*$)/, "")
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, delay: 0
, selector: false
, placement: 'top'
, trigger: 'hover'
, title: ''
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
}
}( window.jQuery )

View File

@ -0,0 +1,51 @@
/* ===================================================
* bootstrap-transition.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ) {
$(function () {
"use strict"
/* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
* ======================================================= */
$.support.transition = (function () {
var thisBody = document.body || document.documentElement
, thisStyle = thisBody.style
, support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
return support && {
end: (function () {
var transitionEnd = "TransitionEnd"
if ( $.browser.webkit ) {
transitionEnd = "webkitTransitionEnd"
} else if ( $.browser.mozilla ) {
transitionEnd = "transitionend"
} else if ( $.browser.opera ) {
transitionEnd = "oTransitionEnd"
}
return transitionEnd
}())
}
})()
})
}( window.jQuery )

View File

@ -0,0 +1,271 @@
/* =============================================================
* bootstrap-typeahead.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
var Typeahead = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.typeahead.defaults, options)
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
this.shown = false
this.listen()
}
Typeahead.prototype = {
constructor: Typeahead
, select: function () {
var val = this.$menu.find('.active').attr('data-value')
this.$element.val(val)
return this.hide()
}
, show: function () {
var pos = $.extend({}, this.$element.offset(), {
height: this.$element[0].offsetHeight
})
this.$menu.css({
top: pos.top + pos.height
, left: pos.left
})
this.$menu.show()
this.shown = true
return this
}
, hide: function () {
this.$menu.hide()
this.shown = false
return this
}
, lookup: function (event) {
var that = this
, items
, q
this.query = this.$element.val()
if (!this.query) {
return this.shown ? this.hide() : this
}
items = $.grep(this.source, function (item) {
if (that.matcher(item)) return item
})
items = this.sorter(items)
if (!items.length) {
return this.shown ? this.hide() : this
}
return this.render(items.slice(0, this.options.items)).show()
}
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase())
}
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
while (item = items.shift()) {
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~item.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
return beginswith.concat(caseSensitive, caseInsensitive)
}
, highlighter: function (item) {
return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
, render: function (items) {
var that = this
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', item)
i.find('a').html(that.highlighter(item))
return i[0]
})
items.first().addClass('active')
this.$menu.html(items)
return this
}
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next()
if (!next.length) {
next = $(this.$menu.find('li')[0])
}
next.addClass('active')
}
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev()
if (!prev.length) {
prev = this.$menu.find('li').last()
}
prev.addClass('active')
}
, listen: function () {
this.$element
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
if ($.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keypress, this))
}
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
, keyup: function (e) {
e.stopPropagation()
e.preventDefault()
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
break
case 9: // tab
case 13: // enter
if (!this.shown) return
this.select()
break
case 27: // escape
this.hide()
break
default:
this.lookup()
}
}
, keypress: function (e) {
e.stopPropagation()
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
e.preventDefault()
this.prev()
break
case 40: // down arrow
e.preventDefault()
this.next()
break
}
}
, blur: function (e) {
var that = this
e.stopPropagation()
e.preventDefault()
setTimeout(function () { that.hide() }, 150)
}
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
}
, mouseenter: function (e) {
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
}
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
$.fn.typeahead = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('typeahead')
, options = typeof option == 'object' && option
if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.typeahead.defaults = {
source: []
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
}
$.fn.typeahead.Constructor = Typeahead
/* TYPEAHEAD DATA-API
* ================== */
$(function () {
$('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
e.preventDefault()
$this.typeahead($this.data())
})
})
}( window.jQuery )

View File

@ -0,0 +1,3 @@
/*! HTML5 Shiv pre3.5 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
Uncompressed source: https://github.com/aFarkas/html5shiv */
(function(a,b){function h(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function i(){var a=l.elements;return typeof a=="string"?a.split(" "):a}function j(a){var b={},c=a.createElement,f=a.createDocumentFragment,g=f();a.createElement=function(a){l.shivMethods||c(a);var f;return b[a]?f=b[a].cloneNode():e.test(a)?f=(b[a]=c(a)).cloneNode():f=c(a),f.canHaveChildren&&!d.test(a)?g.appendChild(f):f},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+i().join().replace(/\w+/g,function(a){return b[a]=c(a),g.createElement(a),'c("'+a+'")'})+");return n}")(l,g)}function k(a){var b;return a.documentShived?a:(l.shivCSS&&!f&&(b=!!h(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),g||(b=!j(a)),b&&(a.documentShived=b),a)}function p(a){var b,c=a.getElementsByTagName("*"),d=c.length,e=RegExp("^(?:"+i().join("|")+")$","i"),f=[];while(d--)b=c[d],e.test(b.nodeName)&&f.push(b.applyElement(q(b)));return f}function q(a){var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(n+":"+a.nodeName);while(d--)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function r(a){var b,c=a.split("{"),d=c.length,e=RegExp("(^|[\\s,>+~])("+i().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),f="$1"+n+"\\:$2";while(d--)b=c[d]=c[d].split("}"),b[b.length-1]=b[b.length-1].replace(e,f),c[d]=b.join("}");return c.join("{")}function s(a){var b=a.length;while(b--)a[b].removeNode()}function t(a){var b,c,d=a.namespaces,e=a.parentWindow;return!o||a.printShived?a:(typeof d[n]=="undefined"&&d.add(n),e.attachEvent("onbeforeprint",function(){var d,e,f,g=a.styleSheets,i=[],j=g.length,k=Array(j);while(j--)k[j]=g[j];while(f=k.pop())if(!f.disabled&&m.test(f.media)){for(d=f.imports,j=0,e=d.length;j<e;j++)k.push(d[j]);try{i.push(f.cssText)}catch(l){}}i=r(i.reverse().join("")),c=p(a),b=h(a,i)}),e.attachEvent("onafterprint",function(){s(c),b.removeNode(!0)}),a.printShived=!0,a)}var c=a.html5||{},d=/^<|^(?:button|form|map|select|textarea|object|iframe)$/i,e=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i,f,g;(function(){var c=b.createElement("a");c.innerHTML="<xyz></xyz>",f="hidden"in c,f&&typeof injectElementWithStyles=="function"&&injectElementWithStyles("#modernizr{}",function(b){b.hidden=!0,f=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle).display=="none"}),g=c.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}()})();var l={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:k};a.html5=l,k(b);var m=/^$|\b(?:all|print)\b/,n="html5shiv",o=!g&&function(){var c=b.documentElement;return typeof b.namespaces!="undefined"&&typeof b.parentWindow!="undefined"&&typeof c.applyElement!="undefined"&&typeof c.removeNode!="undefined"&&typeof a.attachEvent!="undefined"}();l.type+=" print",l.shivPrint=t,t(b)})(this,document)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

67
themes/default/js/menu.js Normal file
View File

@ -0,0 +1,67 @@
function main_menu(items) {
output = '<ul class="nav">'
for (item in items) {
i = items[item];
// Handle active page
if (i["active"]) {
active = ' class="active"';
} else {
active = '';
}
// Line break labels
/*label = i["label"];
if (label.search(" ") != -1) {
label = label.replace(" ", "<br />");
} else {
label = "&nbsp;<br />" + label;
}*/
// Add icon before labels
icon = '<i class="' + i["icon"] + ' icon-white nav-icon"></i>';
label = icon + i["label"];
output = output +'<li' + active + '><a href="' + i["url"] + '"' + active + '>' + label + "</a></li>";
}
output = output + "</ul>";
document.write(output);
}
function render_items(items) {
output = '<ul class="nav nav-list"><li class="nav-header">Menu</li>';
for (item in items) {
i = items[item];
// Handle active page
if (i["active"]) {
active = ' class="active"';
// Add icon before labels
icon = '<i class="' + i["icon"] + ' icon-white sidenav-icon"></i>';
label = icon + i["label"];
} else {
active = '';
// Add icon before labels
icon = '<i class="' + i["icon"] + ' sidenav-icon"></i>';
label = icon + i["label"];
}
output = output +'<li' + active + '><a href="' + i["url"] + '"' + active + '>' + label + "</a></li>";
if (i['subs']) {
output += render_items(i['subs']);
}
}
output = output + "</ul>";
return output
}
function side_menu(items) {
if (items.length == 0) {
return 0;
}
output = render_items(items);
document.write(output);
}

View File

@ -0,0 +1,34 @@
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function f(){ log.history = log.history || []; log.history.push(arguments); if(this.console) { var args = arguments, newarr; args.callee = args.callee.caller; newarr = [].slice.call(args); if (typeof console.log === 'object') log.apply.call(console.log, console, newarr); else console.log.apply(console, newarr);}};
// make it safe to use console.log always
(function(a){function b(){}for(var c="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),d;!!(d=c.pop());){a[d]=a[d]||b;}})
(function(){try{console.log();return window.console;}catch(a){return (window.console={});}}());
// place any jQuery/helper plugins in here, instead of separate, slower script files.
// remap jQuery to $
(function($){})(window.jQuery);
/* trigger when page is ready */
$(document).ready(function (){
// your functions go here
});
/* optional triggers
$(window).load(function() {
});
$(window).resize(function() {
});
*/

View File

@ -1,54 +0,0 @@
function main_menu(items) {
output = "<ul>"
for (item in items) {
i = items[item];
// Handle active page
if (i["active"]) {
active = 'class = "active"';
} else {
active = '';
}
// Line break labels
label = i["label"];
if (label.search(" ") != -1) {
label = label.replace(" ", "<br />");
} else {
label = "&nbsp;<br />" + label;
}
output = output +'<li><a href="' + i["url"] + '" ' + active + '>' + label + "</a></li>";
}
output = output + "</ul>";
document.write(output);
}
function render_items(items) {
output = "<ul>";
for (item in items) {
i = items[item];
// Handle active page
if (i["active"]) {
active = 'class = "active"';
} else {
active = '';
}
output = output +'<li><a href="' + i["url"] + '" ' + active + '>' + i['label'] + "</a></li>";
if (i['subs']) {
output += render_items(i['subs']);
}
}
output = output + "</ul>";
return output
}
function side_menu(items) {
if (items.length == 0) {
return 0;
}
output = "<h2>Menu</h2>" + render_items(items);
document.write(output);
}

12
themes/default/readme.md Normal file
View File

@ -0,0 +1,12 @@
# Plinth HTML5 Bootstrap Theme by Sean "Diggity" O'Brien (https://github.com/seandiggity/Plinth)
## Summary:
This theme is free software offered to you under the terms of the GNU Affero General Public License, Version 3 or later:
http://www.gnu.org/licenses/agpl.html
It is based upon Twitter's Bootstrap (http://twitter.github.com/bootstrap) Bootstrap is licensed under the Apache License v2.0.
Icons from Bootstrap originate from Glyphicons Free, licensed under Creative Commons Attribution 3.0.
Refer to the Plinth documentation for information about editing themes and templates.

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -1,328 +0,0 @@
body {
margin:0;
padding:0;
border:0; /* This removes the border around the viewport in old versions of IE */
width:100%;
background:#EEE;
min-width:600px; /* Minimum width of layout - remove line if not required */
/* The min-width property does not work in old versions of Internet Explorer */
font-size:90%;
font-family:Arial, Helvetica, sans-serif;
}
a {
color:#3D8FE2;
text-decoration:none;
}
a:hover {
color:#fff;
background:#3D8FE2;
text-decoration:none;
}
h1, h2, h3 {
margin:.8em 0 .2em 0;
padding:0;
}
p {
margin:.4em 0 .8em 0;
padding:0;
}
img {
margin:10px 0 5px;
}
#ads img {
display:block;
padding-top:10px;
}
/* Header styles */
#header {
clear:both;
float:left;
width:100%;
border-bottom:1px solid #000;
}
#headerleft {
float: left;
padding-left: 1em;
}
#headerleft a { color:#1D6FE2; text-decoration:none; outline: none;}
#headerright {
float:left;
}
#headerright p,
#headerright h1,
#headerright h2 {
padding:.4em 15px 0 15px;
margin:0;
}
#headerright h1 a,
#headerright h2 a {color:black; text-decoration:none; outline: none;}
#headerright ul {
clear:left;
float:left;
width:100%;
list-style:none;
margin:10px 0 0 0;
padding:0;
}
#headerright ul li {
display:inline;
list-style:none;
margin:0;
padding:0;
}
#headerright ul li a {
display:block;
float:left;
margin:0 0 0 1px;
padding:3px 10px;
text-align:center;
background:#EEE;
color:#000;
text-decoration:none;
position:relative;
left:15px;
line-height:1.3em;
}
#headerright ul li a:hover {
background:lightblue;
color:#fff;
}
#headerright ul li a.active,
#headerright ul li a.active:hover {
color:#fff;
background:#000;
font-weight:bold;
text-decoration:none;
}
#headerright ul li a span {
display:block;
}
/* 'widths' sub menu */
#layoutdims {
clear:both;
background:#eee;
background:lightblue;
border-top:4px solid #000;
margin:0;
padding:6px 15px !important;
text-align:right;
}
/* column container */
.colmask {
position:relative; /* This fixes the IE7 overflow hidden bug */
clear:both;
float:left;
width:100%; /* width of whole page */
overflow:hidden; /* This chops off any overhanging divs */
}
/* common column settings */
.colright,
.colmid,
.colleft {
float:left;
width:100%; /* width of page */
position:relative;
}
.col1,
.col2,
.col3 {
float:left;
position:relative;
padding:0 0em 1em 0; /* no left and right padding on columns, we just make them narrower instead
only padding top and bottom is included here, make it whatever value you need */
overflow:hidden;
}
.col2 h2,
.col3 h2 {color:black;
font-weight:bold;
font-size:100%;
text-align:center;
background:cornflowerblue;
margin-top: 1em;
margin-bottom: 0.4em;}
.col2 h3,
.col3 h3 {color:black;
font-weight:bold;
font-size:100%;
text-align:left;
padding-top: 0em;
margin-top: 0em;
margin-bottom: 0em;}
.col3 p {
margin: 0.4em 0.5em .8em 0.5em;
padding:0;
}
.col2 li {padding-bottom: 0.2em;}
.col2 a {padding-left: 0.2em;}
.col2 a.active,
.col2 a.active:hover {
color:#fff;
background:#000;
font-weight:bold;
text-decoration:none;
padding: 0.2em;
}
/* 3 Column settings */
.threecol {
background:#eee; /* right column background colour */
background:#EEE;
}
.threecol .colmid {
right:25%; /* width of the right column */
background:lightblue;
}
.threecol .colleft {
right:50%; /* width of the middle column */
background:#f4f4f4; /* left column background colour */
background:#EEE;
}
.threecol .col1 {
width:46%; /* width of center column content (column width minus padding on either side) */
left:102%; /* 100% plus left padding of center column */
}
.threecol .col2 {
width:21%; /* Width of left column content (column width minus padding on either side) */
left:31%; /* width of (right column) plus (center column left and right padding) plus (left column left padding) */
}
.threecol .col3 {
width:21%; /* Width of right column content (column width minus padding on either side) */
/* Note this used to be 85%, but I subtracted 1% for padding to pull stuff closer to the margin */
left:84%; /* Please make note of the brackets here:
(100% - left column width) plus (center column left and right padding) plus (left column left and right padding) plus (right column left padding) */
}
/* Footer styles */
#footer {
clear:both;
float:left;
width:100%;
border-top:1px solid #000;
padding: 1em;
font-size:75%;
background:lightblue;
}
#footer p {
margin:0;
}
/* Form Styles */
form.form {
margin:0 auto;
width:100%;
background:#EEE;
position:relative;
margin-top:2em;
}
form.form h2 {
color:#ffffff;
font-size:1.2em;
background:lightblue;
text-transform:uppercase;
padding:0.5em 0em 0.5em 0.5em;
margin-bottom: 1em;
}
form.form h3 {
color:red;
font-weight:bold;
font-size:1em;
padding:0.5em 0 0.5em 0.5em;
text-align:center;
background:#EEE;
}
form.form .line,
form.form p {
width:94%;
display: block;
background:#EEE;
padding:0.5em 0em 1em 1em;
padding-right:0.5em;
margin-left:0.5em;
background:#1D6FE2;
margin-top: 0em;
margin-bottom: 0em;
}
form.form label {
width:100%;
display: block;
background:#EEE;
padding:1em 0 0.5em 0em;
}
form.form .submit {
text-align:left;
}
form.form .indent {
padding: 0px;
padding-left: 0.75em;
}
form.form label span {
display: block;
color:black;
font-size:12px;
float:left;
width:30%;
text-align:right;
padding:0.5em 2em 0 0;
}
form.form .inputtext {
padding:0.2em 0.3em 0.2em 0.3em;
background:#1D6FE2;
border-bottom: 1px double #171717;
border-top: 1px double #171717;
border-left:1px double #333333;
border-right:1px double #333333;
}
form.form .checkbox { text-align: left;}
form.form .inputtextnowidth {
padding:0.5em 0.5em 0em 0em;
background:#1D6FE2;
border-bottom: 1px double #171717;
border-top: 1px double #171717;
border-left:1px double #333333;
border-right:1px double #333333;
}
form.form .textbox{
padding:7px 7px;
width:60%;
background:#1D6FE2;
border-bottom: 1px double #171717;
border-top: 1px double #171717;
border-left:1px double #333333;
border-right:1px double #333333;
overflow:hidden;
height:150px;
}
form.form .button
{
margin:0 0 10px 0;
padding:4px 7px;
background:lightblue;
border:0px;
position: relative;
top:10px;
width:100px;
border-bottom: 1px double lightblue;
border-top: 1px double cornflowerblue;
border-left:1px double lightblue;
border-right:1px double cornflowerblue;
}
.err {color:red;}

1
themes/default/style.css Symbolic link
View File

@ -0,0 +1 @@
css/bootstrap.css

View File

@ -79,11 +79,11 @@ def page_template(template='base', **kwargs):
kwargs['current_url'] = cherrypy.url()
kwargs['username'] = cherrypy.session.get(cfg.session_key)
if not kwargs['nav']: kwargs['nav'] = """ <SCRIPT LANGUAGE="JavaScript">
if not kwargs['nav']: kwargs['nav'] = """ <script type="text/javascript">
<!--
side_menu(sub_menu_items);
// -->
</SCRIPT>"""
</script>"""
return str(template(searchList=[kwargs]))