├── daily_news ├── __init__.py ├── admin.py ├── tests.py ├── views.py └── models.py ├── zhihudailypurify ├── __init__.py ├── __init__.pyc ├── settings.pyc ├── urls.py ├── wsgi.py └── settings.py ├── requirments.txt ├── config.yaml ├── static └── admin │ ├── img │ ├── icon-no.gif │ ├── nav-bg.gif │ ├── icon-yes.gif │ ├── tool-left.gif │ ├── chooser-bg.gif │ ├── default-bg.gif │ ├── icon-unknown.gif │ ├── icon_addlink.gif │ ├── icon_alert.gif │ ├── icon_clock.gif │ ├── icon_error.gif │ ├── icon_success.gif │ ├── tool-right.gif │ ├── tooltag-add.gif │ ├── changelist-bg.gif │ ├── icon_calendar.gif │ ├── icon_searchbox.png │ ├── inline-delete.png │ ├── inline-restore.png │ ├── nav-bg-grabber.gif │ ├── nav-bg-reverse.gif │ ├── selector-icons.gif │ ├── sorting-icons.gif │ ├── tool-left_over.gif │ ├── changelist-bg_rtl.gif │ ├── deleted-overlay.gif │ ├── icon_changelink.gif │ ├── icon_deletelink.gif │ ├── nav-bg-selected.gif │ ├── selector-search.gif │ ├── tool-right_over.gif │ ├── tooltag-add_over.gif │ ├── chooser_stacked-bg.gif │ ├── default-bg-reverse.gif │ ├── gis │ │ ├── move_vertex_off.png │ │ └── move_vertex_on.png │ ├── inline-delete-8bit.png │ ├── inline-restore-8bit.png │ ├── inline-splitter-bg.gif │ ├── tooltag-arrowright.gif │ └── tooltag-arrowright_over.gif │ ├── js │ ├── jquery.init.js │ ├── prepopulate.min.js │ ├── collapse.min.js │ ├── collapse.js │ ├── LICENSE-JQUERY.txt │ ├── prepopulate.js │ ├── compress.py │ ├── inlines.min.js │ ├── timeparse.js │ ├── actions.min.js │ ├── admin │ │ ├── RelatedObjectLookups.js │ │ ├── ordering.js │ │ └── DateTimeShortcuts.js │ ├── SelectBox.js │ ├── actions.js │ ├── urlify.js │ ├── calendar.js │ ├── inlines.js │ ├── getElementsBySelector.js │ ├── core.js │ └── SelectFilter2.js │ └── css │ ├── dashboard.css │ ├── login.css │ ├── ie.css │ ├── rtl.css │ ├── changelists.css │ ├── forms.css │ ├── widgets.css │ └── base.css ├── credential_example.py ├── index.wsgi ├── manage.py ├── templates └── daily_news │ └── index.html ├── README.md └── bundle_local.py /daily_news/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zhihudailypurify/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirments.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.3.2 2 | requests==2.7.0 3 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | name: zhihudailypurify 2 | version: 1 3 | 4 | libraries: 5 | - name: "django" 6 | version: "1.4" -------------------------------------------------------------------------------- /static/admin/img/icon-no.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon-no.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/nav-bg.gif -------------------------------------------------------------------------------- /static/admin/img/icon-yes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon-yes.gif -------------------------------------------------------------------------------- /static/admin/img/tool-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tool-left.gif -------------------------------------------------------------------------------- /zhihudailypurify/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/zhihudailypurify/__init__.pyc -------------------------------------------------------------------------------- /zhihudailypurify/settings.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/zhihudailypurify/settings.pyc -------------------------------------------------------------------------------- /static/admin/img/chooser-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/chooser-bg.gif -------------------------------------------------------------------------------- /static/admin/img/default-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/default-bg.gif -------------------------------------------------------------------------------- /static/admin/img/icon-unknown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon-unknown.gif -------------------------------------------------------------------------------- /static/admin/img/icon_addlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_addlink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_alert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_alert.gif -------------------------------------------------------------------------------- /static/admin/img/icon_clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_clock.gif -------------------------------------------------------------------------------- /static/admin/img/icon_error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_error.gif -------------------------------------------------------------------------------- /static/admin/img/icon_success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_success.gif -------------------------------------------------------------------------------- /static/admin/img/tool-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tool-right.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tooltag-add.gif -------------------------------------------------------------------------------- /static/admin/img/changelist-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/changelist-bg.gif -------------------------------------------------------------------------------- /static/admin/img/icon_calendar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_calendar.gif -------------------------------------------------------------------------------- /static/admin/img/icon_searchbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_searchbox.png -------------------------------------------------------------------------------- /static/admin/img/inline-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/inline-delete.png -------------------------------------------------------------------------------- /static/admin/img/inline-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/inline-restore.png -------------------------------------------------------------------------------- /static/admin/img/nav-bg-grabber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/nav-bg-grabber.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/nav-bg-reverse.gif -------------------------------------------------------------------------------- /static/admin/img/selector-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/selector-icons.gif -------------------------------------------------------------------------------- /static/admin/img/sorting-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/sorting-icons.gif -------------------------------------------------------------------------------- /static/admin/img/tool-left_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tool-left_over.gif -------------------------------------------------------------------------------- /static/admin/img/changelist-bg_rtl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/changelist-bg_rtl.gif -------------------------------------------------------------------------------- /static/admin/img/deleted-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/deleted-overlay.gif -------------------------------------------------------------------------------- /static/admin/img/icon_changelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_changelink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_deletelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/icon_deletelink.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-selected.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/nav-bg-selected.gif -------------------------------------------------------------------------------- /static/admin/img/selector-search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/selector-search.gif -------------------------------------------------------------------------------- /static/admin/img/tool-right_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tool-right_over.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-add_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tooltag-add_over.gif -------------------------------------------------------------------------------- /static/admin/img/chooser_stacked-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/chooser_stacked-bg.gif -------------------------------------------------------------------------------- /static/admin/img/default-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/default-bg-reverse.gif -------------------------------------------------------------------------------- /static/admin/img/gis/move_vertex_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/gis/move_vertex_off.png -------------------------------------------------------------------------------- /static/admin/img/gis/move_vertex_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/gis/move_vertex_on.png -------------------------------------------------------------------------------- /static/admin/img/inline-delete-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/inline-delete-8bit.png -------------------------------------------------------------------------------- /static/admin/img/inline-restore-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/inline-restore-8bit.png -------------------------------------------------------------------------------- /static/admin/img/inline-splitter-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/inline-splitter-bg.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-arrowright.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tooltag-arrowright.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-arrowright_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/heorkucaiyaoname/master/static/admin/img/tooltag-arrowright_over.gif -------------------------------------------------------------------------------- /daily_news/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib import admin 4 | from daily_news.models import DailyNews 5 | 6 | # Register your models here. 7 | admin.site.register(DailyNews) -------------------------------------------------------------------------------- /credential_example.py: -------------------------------------------------------------------------------- 1 | MYSQL_DB = 'zhihudailypurify' 2 | MYSQL_USER = 'YOUR_USER_NAME_HERE' 3 | MYSQL_PASS = 'YOUR_USER_PASS_HERE' 4 | MYSQL_HOST_M = 'YOUR_HOST_M_HERE' 5 | MYSQL_HOST_S = 'YOUR_HOST_S_HERE' 6 | MYSQL_PORT = 'YOUR_MYSQL_PORT_HERE' 7 | -------------------------------------------------------------------------------- /index.wsgi: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import os 6 | 7 | app_root = os.path.dirname(__file__) 8 | sys.path.insert(0, os.path.join(app_root, 'virtualenv.bundle')) 9 | 10 | import sae 11 | from zhihudailypurify import wsgi 12 | 13 | application = sae.create_wsgi_app(wsgi.application) 14 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/env python 3 | import os 4 | import sys 5 | 6 | if __name__ == "__main__": 7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zhihudailypurify.settings") 8 | 9 | from django.core.management import execute_from_command_line 10 | 11 | execute_from_command_line(sys.argv) 12 | -------------------------------------------------------------------------------- /static/admin/js/jquery.init.js: -------------------------------------------------------------------------------- 1 | /* Puts the included jQuery into our own namespace using noConflict and passing 2 | * it 'true'. This ensures that the included jQuery doesn't pollute the global 3 | * namespace (i.e. this preserves pre-existing values for both window.$ and 4 | * window.jQuery). 5 | */ 6 | var django = { 7 | "jQuery": jQuery.noConflict(true) 8 | }; 9 | -------------------------------------------------------------------------------- /static/admin/js/prepopulate.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.prepopulate=function(d,e){return this.each(function(){var b=a(this);b.data("_changed",!1);b.change(function(){b.data("_changed",!0)});var c=function(){if(!0!=b.data("_changed")){var c=[];a.each(d,function(b,d){0'+gettext("Show")+")")});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]);return!1},function(){a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", 2 | [a(this).attr("id")]);return!1})})})(django.jQuery); 3 | -------------------------------------------------------------------------------- /templates/daily_news/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Zhihu Daily Purify Web Server 6 | 9 | 10 | 11 |

Zhihu Daily Purify Web Server (Hosted on Sina App Engine)

12 |
13 | Disclaimer: Zhihu is a trademark of Zhihu Inc. The app and web site of Zhihu Daily Purify are not created nor endorsed by Zhihu Inc.
14 | All the infomation and content accessible through Zhihu Daily Purify are subject to Zhihu's copyright and terms of use.
15 | This is a free app and does not charge for anything. All content are available for free from Zhihu. 16 |
17 |

18 | Author: Izzy Leung 19 |

20 | 21 | -------------------------------------------------------------------------------- /zhihudailypurify/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf.urls import patterns, include, url 4 | 5 | # Uncomment the next two lines to enable the admin: 6 | from django.contrib import admin 7 | admin.autodiscover() 8 | 9 | urlpatterns = patterns('', 10 | # Examples: 11 | # url(r'^$', 'zhihudailypurify.views.home', name='home'), 12 | # url(r'^zhihudailypurify/', include('zhihudailypurify.foo.urls')), 13 | 14 | # Uncomment the admin/doc line below to enable admin documentation: 15 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 16 | 17 | # Uncomment the next line to enable the admin: 18 | url(r'^admin/', include(admin.site.urls)), 19 | url(r'^$', 'daily_news.views.index'), 20 | url(r'^raw/(?P\d+)/$', 'daily_news.views.raw', name='date_string'), 21 | url(r'^search/(?P[\w|\W]+)/$', 'daily_news.views.search', name='key_word'), 22 | ) 23 | -------------------------------------------------------------------------------- /static/admin/js/collapse.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(document).ready(function() { 3 | // Add anchor tag for Show/Hide link 4 | $("fieldset.collapse").each(function(i, elem) { 5 | // Don't hide if fields in this fieldset have errors 6 | if ($(elem).find("div.errors").length == 0) { 7 | $(elem).addClass("collapsed").find("h2").first().append(' (' + gettext("Show") + 9 | ')'); 10 | } 11 | }); 12 | // Add toggle to anchor tag 13 | $("fieldset.collapse a.collapse-toggle").toggle( 14 | function() { // Show 15 | $(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]); 16 | return false; 17 | }, 18 | function() { // Hide 19 | $(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]); 20 | return false; 21 | } 22 | ); 23 | }); 24 | })(django.jQuery); 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Zhihu Daily Purify Backend - Django 2 | ================ 3 | The backend of Zhihu Daily Purify deployed on SAE. (No longer working because the IP of SAE is blocked by Zhihu Daily's server) 4 | 5 | #Setup 6 | SAE pre-installed modules are not included in `requirement.txt`. Please install those modules with the corresponding version on SAE by executing the following command: 7 | 8 | ``` 9 | pip install django==1.4.0 MySQL-python==1.2.3 10 | ``` 11 | 12 | Then, install the remaining dependencies by executing 13 | 14 | ``` 15 | pip install -r requirement.txt 16 | ``` 17 | 18 | And create a database called `zhihudailypurify` in MySQL, and run the command 19 | ``` 20 | ./manage.py syncdb 21 | ``` 22 | to initiate the database. 23 | 24 | Don't forget to edit `credential_example.py` to fill in your credentials, and rename the file to `credential.py`. 25 | 26 | Also, provide an unique `SECRET_KEY` in `settings.py` under `grewordlover` folder. 27 | 28 | Finally, launch the server with the command 29 | ``` 30 | ./manage.py runserver 31 | ``` 32 | and everything should be up and running properly. 33 | -------------------------------------------------------------------------------- /static/admin/css/login.css: -------------------------------------------------------------------------------- 1 | /* LOGIN FORM */ 2 | 3 | body.login { 4 | background: #eee; 5 | } 6 | 7 | .login #container { 8 | background: white; 9 | border: 1px solid #ccc; 10 | width: 28em; 11 | min-width: 300px; 12 | margin-left: auto; 13 | margin-right: auto; 14 | margin-top: 100px; 15 | } 16 | 17 | .login #content-main { 18 | width: 100%; 19 | } 20 | 21 | .login form { 22 | margin-top: 1em; 23 | } 24 | 25 | .login .form-row { 26 | padding: 4px 0; 27 | float: left; 28 | width: 100%; 29 | } 30 | 31 | .login .form-row label { 32 | float: left; 33 | width: 9em; 34 | padding-right: 0.5em; 35 | line-height: 2em; 36 | text-align: right; 37 | font-size: 1em; 38 | color: #333; 39 | } 40 | 41 | .login .form-row #id_username, .login .form-row #id_password { 42 | width: 14em; 43 | } 44 | 45 | .login span.help { 46 | font-size: 10px; 47 | display: block; 48 | } 49 | 50 | .login .submit-row { 51 | clear: both; 52 | padding: 1em 0 0 9.4em; 53 | } 54 | 55 | .login .password-reset-link { 56 | text-align: center; 57 | } 58 | -------------------------------------------------------------------------------- /static/admin/js/LICENSE-JQUERY.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 John Resig, http://jquery.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /zhihudailypurify/wsgi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | WSGI config for zhihudailypurify project. 5 | 6 | This module contains the WSGI application used by Django's development server 7 | and any production WSGI deployments. It should expose a module-level variable 8 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 9 | this application via the ``WSGI_APPLICATION`` setting. 10 | 11 | Usually you will have the standard Django WSGI application here, but it also 12 | might make sense to replace the whole Django WSGI application with a custom one 13 | that later delegates to the Django one. For example, you could introduce WSGI 14 | middleware here, or combine a Django application with an application of another 15 | framework. 16 | 17 | """ 18 | import os 19 | 20 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zhihudailypurify.settings") 21 | 22 | # This application object is used by any WSGI server configured to use this 23 | # file. This includes Django's development server, if the WSGI_APPLICATION 24 | # setting points here. 25 | from django.core.wsgi import get_wsgi_application 26 | application = get_wsgi_application() 27 | 28 | # Apply WSGI middleware here. 29 | # from helloworld.wsgi import HelloWorldApplication 30 | # application = HelloWorldApplication(application) 31 | -------------------------------------------------------------------------------- /static/admin/js/prepopulate.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.fn.prepopulate = function(dependencies, maxLength) { 3 | /* 4 | Depends on urlify.js 5 | Populates a selected field with the values of the dependent fields, 6 | URLifies and shortens the string. 7 | dependencies - array of dependent fields id's 8 | maxLength - maximum length of the URLify'd string 9 | */ 10 | return this.each(function() { 11 | var field = $(this); 12 | 13 | field.data('_changed', false); 14 | field.change(function() { 15 | field.data('_changed', true); 16 | }); 17 | 18 | var populate = function () { 19 | // Bail if the fields value has changed 20 | if (field.data('_changed') == true) return; 21 | 22 | var values = []; 23 | $.each(dependencies, function(i, field) { 24 | if ($(field).val().length > 0) { 25 | values.push($(field).val()); 26 | } 27 | }) 28 | field.val(URLify(values.join(' '), maxLength)); 29 | }; 30 | 31 | $(dependencies.join(',')).keyup(populate).change(populate).focus(populate); 32 | }); 33 | }; 34 | })(django.jQuery); 35 | -------------------------------------------------------------------------------- /static/admin/css/ie.css: -------------------------------------------------------------------------------- 1 | /* IE 6 & 7 */ 2 | 3 | /* Proper fixed width for dashboard in IE6 */ 4 | 5 | .dashboard #content { 6 | *width: 768px; 7 | } 8 | 9 | .dashboard #content-main { 10 | *width: 535px; 11 | } 12 | 13 | /* IE 6 ONLY */ 14 | 15 | /* Keep header from flowing off the page */ 16 | 17 | #container { 18 | _position: static; 19 | } 20 | 21 | /* Put the right sidebars back on the page */ 22 | 23 | .colMS #content-related { 24 | _margin-right: 0; 25 | _margin-left: 10px; 26 | _position: static; 27 | } 28 | 29 | /* Put the left sidebars back on the page */ 30 | 31 | .colSM #content-related { 32 | _margin-right: 10px; 33 | _margin-left: -115px; 34 | _position: static; 35 | } 36 | 37 | .form-row { 38 | _height: 1%; 39 | } 40 | 41 | /* Fix right margin for changelist filters in IE6 */ 42 | 43 | #changelist-filter ul { 44 | _margin-right: -10px; 45 | } 46 | 47 | /* IE ignores min-height, but treats height as if it were min-height */ 48 | 49 | .change-list .filtered { 50 | _height: 400px; 51 | } 52 | 53 | /* IE doesn't know alpha transparency in PNGs */ 54 | 55 | .inline-deletelink { 56 | background: transparent url(../img/inline-delete-8bit.png) no-repeat; 57 | } 58 | 59 | /* IE7 doesn't support inline-block */ 60 | .change-list ul.toplinks li { 61 | zoom: 1; 62 | *display: inline; 63 | } -------------------------------------------------------------------------------- /static/admin/js/compress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import optparse 4 | import subprocess 5 | import sys 6 | 7 | here = os.path.dirname(__file__) 8 | 9 | def main(): 10 | usage = "usage: %prog [file1..fileN]" 11 | description = """With no file paths given this script will automatically 12 | compress all jQuery-based files of the admin app. Requires the Google Closure 13 | Compiler library and Java version 6 or later.""" 14 | parser = optparse.OptionParser(usage, description=description) 15 | parser.add_option("-c", dest="compiler", default="~/bin/compiler.jar", 16 | help="path to Closure Compiler jar file") 17 | parser.add_option("-v", "--verbose", 18 | action="store_true", dest="verbose") 19 | parser.add_option("-q", "--quiet", 20 | action="store_false", dest="verbose") 21 | (options, args) = parser.parse_args() 22 | 23 | compiler = os.path.expanduser(options.compiler) 24 | if not os.path.exists(compiler): 25 | sys.exit("Google Closure compiler jar file %s not found. Please use the -c option to specify the path." % compiler) 26 | 27 | if not args: 28 | if options.verbose: 29 | sys.stdout.write("No filenames given; defaulting to admin scripts\n") 30 | args = [os.path.join(here, f) for f in [ 31 | "actions.js", "collapse.js", "inlines.js", "prepopulate.js"]] 32 | 33 | for arg in args: 34 | if not arg.endswith(".js"): 35 | arg = arg + ".js" 36 | to_compress = os.path.expanduser(arg) 37 | if os.path.exists(to_compress): 38 | to_compress_min = "%s.min.js" % "".join(arg.rsplit(".js")) 39 | cmd = "java -jar %s --js %s --js_output_file %s" % (compiler, to_compress, to_compress_min) 40 | if options.verbose: 41 | sys.stdout.write("Running: %s\n" % cmd) 42 | subprocess.call(cmd.split()) 43 | else: 44 | sys.stdout.write("File %s not found. Sure it exists?\n" % to_compress) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /daily_news/views.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from django.shortcuts import render 4 | from django.http import HttpResponse 5 | from django.shortcuts import render_to_response 6 | from daily_news.models import DailyNews 7 | from django.core.exceptions import ObjectDoesNotExist, ValidationError 8 | 9 | import json 10 | from datetime import datetime 11 | 12 | import sys 13 | 14 | reload(sys) 15 | sys.setdefaultencoding('utf-8') 16 | 17 | def index(request): 18 | return render_to_response('daily_news/index.html') 19 | 20 | def raw(request, date_string): 21 | try: 22 | date = datetime.strptime(date_string, "%Y%m%d") 23 | if date > datetime(2013, 5, 19) and date <= datetime(2014, 10, 13): 24 | try: 25 | news = DailyNews.objects.get(date=date_string) 26 | return HttpResponse(news.content) 27 | except ObjectDoesNotExist, e: 28 | news = DailyNews() 29 | news.date = date_string 30 | news.content = news.download_content() 31 | try: 32 | news.clean() 33 | news.save() 34 | return HttpResponse(news.content) 35 | except ValidationError, e: 36 | return HttpResponse("") 37 | elif date > datetime(2014, 10, 13): 38 | return HttpResponse(u"现在这个服务器已经无法获取知乎的消息,请在设置中启用 Heroku 这个加速服务器吧") 39 | else: 40 | return HttpResponse("") 41 | except ValueError, e: 42 | return HttpResponse(e) 43 | 44 | def search(request, key_word): 45 | result_list = [] 46 | for single_news in DailyNews.objects.filter(content__contains=key_word).order_by('-date'): 47 | for news in json.loads(single_news.content): 48 | if key_word in news['dailyTitle']: 49 | found_news = {'date': single_news.date, 'content': news} 50 | result_list.append(found_news) 51 | continue 52 | 53 | if news['isMulti']: 54 | for title in news['questionTitleList']: 55 | if key_word in title: 56 | found_news = {'date': single_news.date, 'content': news} 57 | result_list.append(found_news) 58 | break 59 | else: 60 | if key_word in news['questionTitle']: 61 | found_news = {'date': single_news.date, 'content': news} 62 | result_list.append(found_news) 63 | 64 | return HttpResponse(json.dumps(result_list, ensure_ascii=False).encode('utf-8')) 65 | -------------------------------------------------------------------------------- /static/admin/js/inlines.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.formset=function(c){var a=b.extend({},b.fn.formset.defaults,c),j=function(a,e,d){var i=RegExp("("+e+"-(\\d+|__prefix__))"),e=e+"-"+d;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(i,e));if(a.id)a.id=a.id.replace(i,e);if(a.name)a.name=a.name.replace(i,e)},c=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),g=parseInt(c.val()),f=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),c=""==f.val()||0'+a.addText+""),h=b(this).parent().find("tr:last a")):(b(this).filter(":last").after('"),h=b(this).filter(":last").next().find("a"));h.click(function(){var c=b("#id_"+a.prefix+ 3 | "-TOTAL_FORMS"),e=b("#"+a.prefix+"-empty"),d=e.clone(!0);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+g);d.is("tr")?d.children(":last").append('
'+a.deleteText+"
"):d.is("ul")||d.is("ol")?d.append('
  • '+a.deleteText+"
  • "):d.children(":first").append(''+a.deleteText+ 4 | "");d.find("*").each(function(){j(this,a.prefix,c.val())});d.insertBefore(b(e));b(c).val(parseInt(c.val())+1);g+=1;""!=f.val()&&0>=f.val()-c.val()&&h.parent().hide();d.find("a."+a.deleteCssClass).click(function(){var c=b(this).parents("."+a.formCssClass);c.remove();g-=1;a.removed&&a.removed(c);c=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(c.length);(""==f.val()||0 1: 49 | news['isMulti'] = True 50 | should_append = True 51 | 52 | for node in view_more: 53 | if node.find('a'): 54 | if node.find('a').text == u'查看知乎讨论': 55 | news['questionUrlList'].append(node.find('a')['href']) 56 | else: 57 | should_append = False 58 | else: 59 | continue 60 | 61 | for node in bs.find_all('h2'): 62 | title = node.get_text() if node.get_text() != "" else n['title'] 63 | news['questionTitleList'].append(title) 64 | 65 | if should_append: 66 | news_list.append(news) 67 | elif len(view_more) == 1: 68 | question_element = (bs.find('div', {'class': 'view-more'})).find('a') 69 | if question_element.text == u'查看知乎讨论': 70 | news['questionUrl'] = question_element['href'] 71 | 72 | title = bs.find('h2').get_text() 73 | news['questionTitle'] = title if title != "" else n['title'] 74 | 75 | news_list.append(news) 76 | 77 | return json.dumps(news_list, ensure_ascii=False).encode('utf-8') 78 | 79 | def clean(self): 80 | if not self.date or not self.content: 81 | raise ValidationError("Content cannot be null") 82 | else: 83 | try: 84 | time.strptime(str(self.date), '%Y%m%d') 85 | except ValueError, e: 86 | raise ValidationError("Date illegal") -------------------------------------------------------------------------------- /static/admin/js/admin/RelatedObjectLookups.js: -------------------------------------------------------------------------------- 1 | // Handles related-objects functionality: lookup link for raw_id_fields 2 | // and Add Another links. 3 | 4 | function html_unescape(text) { 5 | // Unescape a string that was escaped using django.utils.html.escape. 6 | text = text.replace(/</g, '<'); 7 | text = text.replace(/>/g, '>'); 8 | text = text.replace(/"/g, '"'); 9 | text = text.replace(/'/g, "'"); 10 | text = text.replace(/&/g, '&'); 11 | return text; 12 | } 13 | 14 | // IE doesn't accept periods or dashes in the window name, but the element IDs 15 | // we use to generate popup window names may contain them, therefore we map them 16 | // to allowed characters in a reversible way so that we can locate the correct 17 | // element when the popup window is dismissed. 18 | function id_to_windowname(text) { 19 | text = text.replace(/\./g, '__dot__'); 20 | text = text.replace(/\-/g, '__dash__'); 21 | return text; 22 | } 23 | 24 | function windowname_to_id(text) { 25 | text = text.replace(/__dot__/g, '.'); 26 | text = text.replace(/__dash__/g, '-'); 27 | return text; 28 | } 29 | 30 | function showRelatedObjectLookupPopup(triggeringLink) { 31 | var name = triggeringLink.id.replace(/^lookup_/, ''); 32 | name = id_to_windowname(name); 33 | var href; 34 | if (triggeringLink.href.search(/\?/) >= 0) { 35 | href = triggeringLink.href + '&pop=1'; 36 | } else { 37 | href = triggeringLink.href + '?pop=1'; 38 | } 39 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 40 | win.focus(); 41 | return false; 42 | } 43 | 44 | function dismissRelatedLookupPopup(win, chosenId) { 45 | var name = windowname_to_id(win.name); 46 | var elem = document.getElementById(name); 47 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 48 | elem.value += ',' + chosenId; 49 | } else { 50 | document.getElementById(name).value = chosenId; 51 | } 52 | win.close(); 53 | } 54 | 55 | function showAddAnotherPopup(triggeringLink) { 56 | var name = triggeringLink.id.replace(/^add_/, ''); 57 | name = id_to_windowname(name); 58 | href = triggeringLink.href 59 | if (href.indexOf('?') == -1) { 60 | href += '?_popup=1'; 61 | } else { 62 | href += '&_popup=1'; 63 | } 64 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 65 | win.focus(); 66 | return false; 67 | } 68 | 69 | function dismissAddAnotherPopup(win, newId, newRepr) { 70 | // newId and newRepr are expected to have previously been escaped by 71 | // django.utils.html.escape. 72 | newId = html_unescape(newId); 73 | newRepr = html_unescape(newRepr); 74 | var name = windowname_to_id(win.name); 75 | var elem = document.getElementById(name); 76 | if (elem) { 77 | var elemName = elem.nodeName.toUpperCase(); 78 | if (elemName == 'SELECT') { 79 | var o = new Option(newRepr, newId); 80 | elem.options[elem.options.length] = o; 81 | o.selected = true; 82 | } else if (elemName == 'INPUT') { 83 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 84 | elem.value += ',' + newId; 85 | } else { 86 | elem.value = newId; 87 | } 88 | } 89 | } else { 90 | var toId = name + "_to"; 91 | elem = document.getElementById(toId); 92 | var o = new Option(newRepr, newId); 93 | SelectBox.add_to_cache(toId, o); 94 | SelectBox.redisplay(toId); 95 | } 96 | win.close(); 97 | } 98 | -------------------------------------------------------------------------------- /static/admin/js/admin/ordering.js: -------------------------------------------------------------------------------- 1 | addEvent(window, 'load', reorder_init); 2 | 3 | var lis; 4 | var top = 0; 5 | var left = 0; 6 | var height = 30; 7 | 8 | function reorder_init() { 9 | lis = document.getElementsBySelector('ul#orderthese li'); 10 | var input = document.getElementsBySelector('input[name=order_]')[0]; 11 | setOrder(input.value.split(',')); 12 | input.disabled = true; 13 | draw(); 14 | // Now initialize the dragging behavior 15 | var limit = (lis.length - 1) * height; 16 | for (var i = 0; i < lis.length; i++) { 17 | var li = lis[i]; 18 | var img = document.getElementById('handle'+li.id); 19 | li.style.zIndex = 1; 20 | Drag.init(img, li, left + 10, left + 10, top + 10, top + 10 + limit); 21 | li.onDragStart = startDrag; 22 | li.onDragEnd = endDrag; 23 | img.style.cursor = 'move'; 24 | } 25 | } 26 | 27 | function submitOrderForm() { 28 | var inputOrder = document.getElementsBySelector('input[name=order_]')[0]; 29 | inputOrder.value = getOrder(); 30 | inputOrder.disabled=false; 31 | } 32 | 33 | function startDrag() { 34 | this.style.zIndex = '10'; 35 | this.className = 'dragging'; 36 | } 37 | 38 | function endDrag(x, y) { 39 | this.style.zIndex = '1'; 40 | this.className = ''; 41 | // Work out how far along it has been dropped, using x co-ordinate 42 | var oldIndex = this.index; 43 | var newIndex = Math.round((y - 10 - top) / height); 44 | // 'Snap' to the correct position 45 | this.style.top = (10 + top + newIndex * height) + 'px'; 46 | this.index = newIndex; 47 | moveItem(oldIndex, newIndex); 48 | } 49 | 50 | function moveItem(oldIndex, newIndex) { 51 | // Swaps two items, adjusts the index and left co-ord for all others 52 | if (oldIndex == newIndex) { 53 | return; // Nothing to swap; 54 | } 55 | var direction, lo, hi; 56 | if (newIndex > oldIndex) { 57 | lo = oldIndex; 58 | hi = newIndex; 59 | direction = -1; 60 | } else { 61 | direction = 1; 62 | hi = oldIndex; 63 | lo = newIndex; 64 | } 65 | var lis2 = new Array(); // We will build the new order in this array 66 | for (var i = 0; i < lis.length; i++) { 67 | if (i < lo || i > hi) { 68 | // Position of items not between the indexes is unaffected 69 | lis2[i] = lis[i]; 70 | continue; 71 | } else if (i == newIndex) { 72 | lis2[i] = lis[oldIndex]; 73 | continue; 74 | } else { 75 | // Item is between the two indexes - move it along 1 76 | lis2[i] = lis[i - direction]; 77 | } 78 | } 79 | // Re-index everything 80 | reIndex(lis2); 81 | lis = lis2; 82 | draw(); 83 | // document.getElementById('hiddenOrder').value = getOrder(); 84 | document.getElementsBySelector('input[name=order_]')[0].value = getOrder(); 85 | } 86 | 87 | function reIndex(lis) { 88 | for (var i = 0; i < lis.length; i++) { 89 | lis[i].index = i; 90 | } 91 | } 92 | 93 | function draw() { 94 | for (var i = 0; i < lis.length; i++) { 95 | var li = lis[i]; 96 | li.index = i; 97 | li.style.position = 'absolute'; 98 | li.style.left = (10 + left) + 'px'; 99 | li.style.top = (10 + top + (i * height)) + 'px'; 100 | } 101 | } 102 | 103 | function getOrder() { 104 | var order = new Array(lis.length); 105 | for (var i = 0; i < lis.length; i++) { 106 | order[i] = lis[i].id.substring(1, 100); 107 | } 108 | return order.join(','); 109 | } 110 | 111 | function setOrder(id_list) { 112 | /* Set the current order to match the lsit of IDs */ 113 | var temp_lis = new Array(); 114 | for (var i = 0; i < id_list.length; i++) { 115 | var id = 'p' + id_list[i]; 116 | temp_lis[temp_lis.length] = document.getElementById(id); 117 | } 118 | reIndex(temp_lis); 119 | lis = temp_lis; 120 | draw(); 121 | } 122 | 123 | function addEvent(elm, evType, fn, useCapture) 124 | // addEvent and removeEvent 125 | // cross-browser event handling for IE5+, NS6 and Mozilla 126 | // By Scott Andrew 127 | { 128 | if (elm.addEventListener){ 129 | elm.addEventListener(evType, fn, useCapture); 130 | return true; 131 | } else if (elm.attachEvent){ 132 | var r = elm.attachEvent("on"+evType, fn); 133 | return r; 134 | } else { 135 | elm['on'+evType] = fn; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /static/admin/js/SelectBox.js: -------------------------------------------------------------------------------- 1 | var SelectBox = { 2 | cache: new Object(), 3 | init: function(id) { 4 | var box = document.getElementById(id); 5 | var node; 6 | SelectBox.cache[id] = new Array(); 7 | var cache = SelectBox.cache[id]; 8 | for (var i = 0; (node = box.options[i]); i++) { 9 | cache.push({value: node.value, text: node.text, displayed: 1}); 10 | } 11 | }, 12 | redisplay: function(id) { 13 | // Repopulate HTML select box from cache 14 | var box = document.getElementById(id); 15 | box.options.length = 0; // clear all options 16 | for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) { 17 | var node = SelectBox.cache[id][i]; 18 | if (node.displayed) { 19 | box.options[box.options.length] = new Option(node.text, node.value, false, false); 20 | } 21 | } 22 | }, 23 | filter: function(id, text) { 24 | // Redisplay the HTML select box, displaying only the choices containing ALL 25 | // the words in text. (It's an AND search.) 26 | var tokens = text.toLowerCase().split(/\s+/); 27 | var node, token; 28 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 29 | node.displayed = 1; 30 | for (var j = 0; (token = tokens[j]); j++) { 31 | if (node.text.toLowerCase().indexOf(token) == -1) { 32 | node.displayed = 0; 33 | } 34 | } 35 | } 36 | SelectBox.redisplay(id); 37 | }, 38 | delete_from_cache: function(id, value) { 39 | var node, delete_index = null; 40 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 41 | if (node.value == value) { 42 | delete_index = i; 43 | break; 44 | } 45 | } 46 | var j = SelectBox.cache[id].length - 1; 47 | for (var i = delete_index; i < j; i++) { 48 | SelectBox.cache[id][i] = SelectBox.cache[id][i+1]; 49 | } 50 | SelectBox.cache[id].length--; 51 | }, 52 | add_to_cache: function(id, option) { 53 | SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); 54 | }, 55 | cache_contains: function(id, value) { 56 | // Check if an item is contained in the cache 57 | var node; 58 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 59 | if (node.value == value) { 60 | return true; 61 | } 62 | } 63 | return false; 64 | }, 65 | move: function(from, to) { 66 | var from_box = document.getElementById(from); 67 | var to_box = document.getElementById(to); 68 | var option; 69 | for (var i = 0; (option = from_box.options[i]); i++) { 70 | if (option.selected && SelectBox.cache_contains(from, option.value)) { 71 | SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 72 | SelectBox.delete_from_cache(from, option.value); 73 | } 74 | } 75 | SelectBox.redisplay(from); 76 | SelectBox.redisplay(to); 77 | }, 78 | move_all: function(from, to) { 79 | var from_box = document.getElementById(from); 80 | var to_box = document.getElementById(to); 81 | var option; 82 | for (var i = 0; (option = from_box.options[i]); i++) { 83 | if (SelectBox.cache_contains(from, option.value)) { 84 | SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 85 | SelectBox.delete_from_cache(from, option.value); 86 | } 87 | } 88 | SelectBox.redisplay(from); 89 | SelectBox.redisplay(to); 90 | }, 91 | sort: function(id) { 92 | SelectBox.cache[id].sort( function(a, b) { 93 | a = a.text.toLowerCase(); 94 | b = b.text.toLowerCase(); 95 | try { 96 | if (a > b) return 1; 97 | if (a < b) return -1; 98 | } 99 | catch (e) { 100 | // silently fail on IE 'unknown' exception 101 | } 102 | return 0; 103 | } ); 104 | }, 105 | select_all: function(id) { 106 | var box = document.getElementById(id); 107 | for (var i = 0; i < box.options.length; i++) { 108 | box.options[i].selected = 'selected'; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /static/admin/css/rtl.css: -------------------------------------------------------------------------------- 1 | body { 2 | direction: rtl; 3 | } 4 | 5 | /* LOGIN */ 6 | 7 | .login .form-row { 8 | float: right; 9 | } 10 | 11 | .login .form-row label { 12 | float: right; 13 | padding-left: 0.5em; 14 | padding-right: 0; 15 | text-align: left; 16 | } 17 | 18 | .login .submit-row { 19 | clear: both; 20 | padding: 1em 9.4em 0 0; 21 | } 22 | 23 | /* GLOBAL */ 24 | 25 | th { 26 | text-align: right; 27 | } 28 | 29 | .module h2, .module caption { 30 | text-align: right; 31 | } 32 | 33 | .addlink, .changelink { 34 | padding-left: 0px; 35 | padding-right: 12px; 36 | background-position: 100% 0.2em; 37 | } 38 | 39 | .deletelink { 40 | padding-left: 0px; 41 | padding-right: 12px; 42 | background-position: 100% 0.25em; 43 | } 44 | 45 | .object-tools { 46 | float: left; 47 | } 48 | 49 | thead th:first-child, 50 | tfoot td:first-child { 51 | border-left: 1px solid #ddd !important; 52 | } 53 | 54 | /* LAYOUT */ 55 | 56 | #user-tools { 57 | right: auto; 58 | left: 0; 59 | text-align: left; 60 | } 61 | 62 | div.breadcrumbs { 63 | text-align: right; 64 | } 65 | 66 | #content-main { 67 | float: right; 68 | } 69 | 70 | #content-related { 71 | float: left; 72 | margin-left: -19em; 73 | margin-right: auto; 74 | } 75 | 76 | .colMS { 77 | margin-left: 20em !important; 78 | margin-right: 10px !important; 79 | } 80 | 81 | /* SORTABLE TABLES */ 82 | 83 | table thead th.sorted .sortoptions { 84 | float: left; 85 | } 86 | 87 | /* dashboard styles */ 88 | 89 | .dashboard .module table td a { 90 | padding-left: .6em; 91 | padding-right: 12px; 92 | } 93 | 94 | /* changelists styles */ 95 | 96 | .change-list .filtered { 97 | background: white url(../img/changelist-bg_rtl.gif) top left repeat-y !important; 98 | } 99 | 100 | .change-list .filtered table { 101 | border-left: 1px solid #ddd; 102 | border-right: 0px none; 103 | } 104 | 105 | #changelist-filter { 106 | right: auto; 107 | left: 0; 108 | border-left: 0px none; 109 | border-right: 1px solid #ddd; 110 | } 111 | 112 | .change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 113 | margin-right: 0px !important; 114 | margin-left: 160px !important; 115 | } 116 | 117 | #changelist-filter li.selected { 118 | border-left: 0px none; 119 | padding-left: 0px; 120 | margin-left: 0; 121 | border-right: 5px solid #ccc; 122 | padding-right: 5px; 123 | margin-right: -10px; 124 | } 125 | 126 | .filtered .actions { 127 | border-left:1px solid #DDDDDD; 128 | margin-left:160px !important; 129 | border-right: 0 none; 130 | margin-right:0 !important; 131 | } 132 | 133 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 134 | border-right: 0; 135 | border-left: 1px solid #ddd; 136 | } 137 | 138 | /* FORMS */ 139 | 140 | .aligned label { 141 | padding: 0 0 3px 1em; 142 | float: right; 143 | } 144 | 145 | .submit-row { 146 | text-align: left 147 | } 148 | 149 | .submit-row p.deletelink-box { 150 | float: right; 151 | } 152 | 153 | .submit-row .deletelink { 154 | background: url(../img/icon_deletelink.gif) 0 50% no-repeat; 155 | padding-right: 14px; 156 | } 157 | 158 | .vDateField, .vTimeField { 159 | margin-left: 2px; 160 | } 161 | 162 | form ul.inline li { 163 | float: right; 164 | padding-right: 0; 165 | padding-left: 7px; 166 | } 167 | 168 | input[type=submit].default, .submit-row input.default { 169 | float: left; 170 | } 171 | 172 | fieldset .field-box { 173 | float: right; 174 | margin-left: 20px; 175 | margin-right: 0; 176 | } 177 | 178 | .errorlist li { 179 | background-position: 100% .3em; 180 | padding: 4px 25px 4px 5px; 181 | } 182 | 183 | .errornote { 184 | background-position: 100% .3em; 185 | padding: 4px 25px 4px 5px; 186 | } 187 | 188 | /* WIDGETS */ 189 | 190 | .calendarnav-previous { 191 | top: 0; 192 | left: auto; 193 | right: 0; 194 | } 195 | 196 | .calendarnav-next { 197 | top: 0; 198 | right: auto; 199 | left: 0; 200 | } 201 | 202 | .calendar caption, .calendarbox h2 { 203 | text-align: center; 204 | } 205 | 206 | .selector { 207 | float: right; 208 | } 209 | 210 | .selector .selector-filter { 211 | text-align: right; 212 | } 213 | 214 | .inline-deletelink { 215 | float: left; 216 | } 217 | 218 | /* MISC */ 219 | 220 | .inline-related h2, .inline-group h2 { 221 | text-align: right 222 | } 223 | 224 | .inline-related h3 span.delete { 225 | padding-right: 20px; 226 | padding-left: inherit; 227 | left: 10px; 228 | right: inherit; 229 | float:left; 230 | } 231 | 232 | .inline-related h3 span.delete label { 233 | margin-left: inherit; 234 | margin-right: 2px; 235 | } 236 | 237 | /* IE7 specific bug fixes */ 238 | 239 | div.colM { 240 | position: relative; 241 | } 242 | 243 | .submit-row input { 244 | float: left; 245 | } -------------------------------------------------------------------------------- /static/admin/js/actions.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.fn.actions = function(opts) { 3 | var options = $.extend({}, $.fn.actions.defaults, opts); 4 | var actionCheckboxes = $(this); 5 | var list_editable_changed = false; 6 | checker = function(checked) { 7 | if (checked) { 8 | showQuestion(); 9 | } else { 10 | reset(); 11 | } 12 | $(actionCheckboxes).attr("checked", checked) 13 | .parent().parent().toggleClass(options.selectedClass, checked); 14 | } 15 | updateCounter = function() { 16 | var sel = $(actionCheckboxes).filter(":checked").length; 17 | $(options.counterContainer).html(interpolate( 18 | ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { 19 | sel: sel, 20 | cnt: _actions_icnt 21 | }, true)); 22 | $(options.allToggle).attr("checked", function() { 23 | if (sel == actionCheckboxes.length) { 24 | value = true; 25 | showQuestion(); 26 | } else { 27 | value = false; 28 | clearAcross(); 29 | } 30 | return value; 31 | }); 32 | } 33 | showQuestion = function() { 34 | $(options.acrossClears).hide(); 35 | $(options.acrossQuestions).show(); 36 | $(options.allContainer).hide(); 37 | } 38 | showClear = function() { 39 | $(options.acrossClears).show(); 40 | $(options.acrossQuestions).hide(); 41 | $(options.actionContainer).toggleClass(options.selectedClass); 42 | $(options.allContainer).show(); 43 | $(options.counterContainer).hide(); 44 | } 45 | reset = function() { 46 | $(options.acrossClears).hide(); 47 | $(options.acrossQuestions).hide(); 48 | $(options.allContainer).hide(); 49 | $(options.counterContainer).show(); 50 | } 51 | clearAcross = function() { 52 | reset(); 53 | $(options.acrossInput).val(0); 54 | $(options.actionContainer).removeClass(options.selectedClass); 55 | } 56 | // Show counter by default 57 | $(options.counterContainer).show(); 58 | // Check state of checkboxes and reinit state if needed 59 | $(this).filter(":checked").each(function(i) { 60 | $(this).parent().parent().toggleClass(options.selectedClass); 61 | updateCounter(); 62 | if ($(options.acrossInput).val() == 1) { 63 | showClear(); 64 | } 65 | }); 66 | $(options.allToggle).show().click(function() { 67 | checker($(this).attr("checked")); 68 | updateCounter(); 69 | }); 70 | $("div.actions span.question a").click(function(event) { 71 | event.preventDefault(); 72 | $(options.acrossInput).val(1); 73 | showClear(); 74 | }); 75 | $("div.actions span.clear a").click(function(event) { 76 | event.preventDefault(); 77 | $(options.allToggle).attr("checked", false); 78 | clearAcross(); 79 | checker(0); 80 | updateCounter(); 81 | }); 82 | lastChecked = null; 83 | $(actionCheckboxes).click(function(event) { 84 | if (!event) { var event = window.event; } 85 | var target = event.target ? event.target : event.srcElement; 86 | if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) { 87 | var inrange = false; 88 | $(lastChecked).attr("checked", target.checked) 89 | .parent().parent().toggleClass(options.selectedClass, target.checked); 90 | $(actionCheckboxes).each(function() { 91 | if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) { 92 | inrange = (inrange) ? false : true; 93 | } 94 | if (inrange) { 95 | $(this).attr("checked", target.checked) 96 | .parent().parent().toggleClass(options.selectedClass, target.checked); 97 | } 98 | }); 99 | } 100 | $(target).parent().parent().toggleClass(options.selectedClass, target.checked); 101 | lastChecked = target; 102 | updateCounter(); 103 | }); 104 | $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() { 105 | list_editable_changed = true; 106 | }); 107 | $('form#changelist-form button[name="index"]').click(function(event) { 108 | if (list_editable_changed) { 109 | return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); 110 | } 111 | }); 112 | $('form#changelist-form input[name="_save"]').click(function(event) { 113 | var action_changed = false; 114 | $('div.actions select option:selected').each(function() { 115 | if ($(this).val()) { 116 | action_changed = true; 117 | } 118 | }); 119 | if (action_changed) { 120 | if (list_editable_changed) { 121 | return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")); 122 | } else { 123 | return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button.")); 124 | } 125 | } 126 | }); 127 | } 128 | /* Setup plugin defaults */ 129 | $.fn.actions.defaults = { 130 | actionContainer: "div.actions", 131 | counterContainer: "span.action-counter", 132 | allContainer: "div.actions span.all", 133 | acrossInput: "div.actions input.select-across", 134 | acrossQuestions: "div.actions span.question", 135 | acrossClears: "div.actions span.clear", 136 | allToggle: "#action-toggle", 137 | selectedClass: "selected" 138 | } 139 | })(django.jQuery); 140 | -------------------------------------------------------------------------------- /static/admin/js/urlify.js: -------------------------------------------------------------------------------- 1 | var LATIN_MAP = { 2 | 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 3 | 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 4 | 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 5 | 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U', 6 | 'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä': 7 | 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 8 | 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 9 | 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u', 10 | 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y' 11 | } 12 | var LATIN_SYMBOLS_MAP = { 13 | '©':'(c)' 14 | } 15 | var GREEK_MAP = { 16 | 'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8', 17 | 'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p', 18 | 'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w', 19 | 'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s', 20 | 'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i', 21 | 'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8', 22 | 'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P', 23 | 'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W', 24 | 'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I', 25 | 'Ϋ':'Y' 26 | } 27 | var TURKISH_MAP = { 28 | 'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U', 29 | 'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G' 30 | } 31 | var RUSSIAN_MAP = { 32 | 'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh', 33 | 'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o', 34 | 'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c', 35 | 'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu', 36 | 'я':'ya', 37 | 'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh', 38 | 'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O', 39 | 'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C', 40 | 'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu', 41 | 'Я':'Ya' 42 | } 43 | var UKRAINIAN_MAP = { 44 | 'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g' 45 | } 46 | var CZECH_MAP = { 47 | 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u', 48 | 'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T', 49 | 'Ů':'U', 'Ž':'Z' 50 | } 51 | 52 | var POLISH_MAP = { 53 | 'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z', 54 | 'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S', 55 | 'Ź':'Z', 'Ż':'Z' 56 | } 57 | 58 | var LATVIAN_MAP = { 59 | 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n', 60 | 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i', 61 | 'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z' 62 | } 63 | 64 | var ALL_DOWNCODE_MAPS=new Array() 65 | ALL_DOWNCODE_MAPS[0]=LATIN_MAP 66 | ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP 67 | ALL_DOWNCODE_MAPS[2]=GREEK_MAP 68 | ALL_DOWNCODE_MAPS[3]=TURKISH_MAP 69 | ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP 70 | ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP 71 | ALL_DOWNCODE_MAPS[6]=CZECH_MAP 72 | ALL_DOWNCODE_MAPS[7]=POLISH_MAP 73 | ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP 74 | 75 | var Downcoder = new Object(); 76 | Downcoder.Initialize = function() 77 | { 78 | if (Downcoder.map) // already made 79 | return ; 80 | Downcoder.map ={} 81 | Downcoder.chars = '' ; 82 | for(var i in ALL_DOWNCODE_MAPS) 83 | { 84 | var lookup = ALL_DOWNCODE_MAPS[i] 85 | for (var c in lookup) 86 | { 87 | Downcoder.map[c] = lookup[c] ; 88 | Downcoder.chars += c ; 89 | } 90 | } 91 | Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g') ; 92 | } 93 | 94 | downcode= function( slug ) 95 | { 96 | Downcoder.Initialize() ; 97 | var downcoded ="" 98 | var pieces = slug.match(Downcoder.regex); 99 | if(pieces) 100 | { 101 | for (var i = 0 ; i < pieces.length ; i++) 102 | { 103 | if (pieces[i].length == 1) 104 | { 105 | var mapped = Downcoder.map[pieces[i]] ; 106 | if (mapped != null) 107 | { 108 | downcoded+=mapped; 109 | continue ; 110 | } 111 | } 112 | downcoded+=pieces[i]; 113 | } 114 | } 115 | else 116 | { 117 | downcoded = slug; 118 | } 119 | return downcoded; 120 | } 121 | 122 | 123 | function URLify(s, num_chars) { 124 | // changes, e.g., "Petty theft" to "petty_theft" 125 | // remove all these words from the string before urlifying 126 | s = downcode(s); 127 | removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from", 128 | "is", "in", "into", "like", "of", "off", "on", "onto", "per", 129 | "since", "than", "the", "this", "that", "to", "up", "via", 130 | "with"]; 131 | r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi'); 132 | s = s.replace(r, ''); 133 | // if downcode doesn't hit, the char will be stripped here 134 | s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars 135 | s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces 136 | s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens 137 | s = s.toLowerCase(); // convert to lowercase 138 | return s.substring(0, num_chars);// trim to first num_chars chars 139 | } 140 | 141 | -------------------------------------------------------------------------------- /static/admin/js/calendar.js: -------------------------------------------------------------------------------- 1 | /* 2 | calendar.js - Calendar functions by Adrian Holovaty 3 | */ 4 | 5 | function removeChildren(a) { // "a" is reference to an object 6 | while (a.hasChildNodes()) a.removeChild(a.lastChild); 7 | } 8 | 9 | // quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]); 10 | function quickElement() { 11 | var obj = document.createElement(arguments[0]); 12 | if (arguments[2] != '' && arguments[2] != null) { 13 | var textNode = document.createTextNode(arguments[2]); 14 | obj.appendChild(textNode); 15 | } 16 | var len = arguments.length; 17 | for (var i = 3; i < len; i += 2) { 18 | obj.setAttribute(arguments[i], arguments[i+1]); 19 | } 20 | arguments[1].appendChild(obj); 21 | return obj; 22 | } 23 | 24 | // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions 25 | var CalendarNamespace = { 26 | monthsOfYear: gettext('January February March April May June July August September October November December').split(' '), 27 | daysOfWeek: gettext('S M T W T F S').split(' '), 28 | firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')), 29 | isLeapYear: function(year) { 30 | return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0)); 31 | }, 32 | getDaysInMonth: function(month,year) { 33 | var days; 34 | if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { 35 | days = 31; 36 | } 37 | else if (month==4 || month==6 || month==9 || month==11) { 38 | days = 30; 39 | } 40 | else if (month==2 && CalendarNamespace.isLeapYear(year)) { 41 | days = 29; 42 | } 43 | else { 44 | days = 28; 45 | } 46 | return days; 47 | }, 48 | draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999 49 | var today = new Date(); 50 | var todayDay = today.getDate(); 51 | var todayMonth = today.getMonth()+1; 52 | var todayYear = today.getFullYear(); 53 | var todayClass = ''; 54 | 55 | month = parseInt(month); 56 | year = parseInt(year); 57 | var calDiv = document.getElementById(div_id); 58 | removeChildren(calDiv); 59 | var calTable = document.createElement('table'); 60 | quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year); 61 | var tableBody = quickElement('tbody', calTable); 62 | 63 | // Draw days-of-week header 64 | var tableRow = quickElement('tr', tableBody); 65 | for (var i = 0; i < 7; i++) { 66 | quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]); 67 | } 68 | 69 | var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay(); 70 | var days = CalendarNamespace.getDaysInMonth(month, year); 71 | 72 | // Draw blanks before first of month 73 | tableRow = quickElement('tr', tableBody); 74 | for (var i = 0; i < startingPos; i++) { 75 | var _cell = quickElement('td', tableRow, ' '); 76 | _cell.style.backgroundColor = '#f3f3f3'; 77 | } 78 | 79 | // Draw days of month 80 | var currentDay = 1; 81 | for (var i = startingPos; currentDay <= days; i++) { 82 | if (i%7 == 0 && currentDay != 1) { 83 | tableRow = quickElement('tr', tableBody); 84 | } 85 | if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) { 86 | todayClass='today'; 87 | } else { 88 | todayClass=''; 89 | } 90 | var cell = quickElement('td', tableRow, '', 'class', todayClass); 91 | 92 | quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));'); 93 | currentDay++; 94 | } 95 | 96 | // Draw blanks after end of month (optional, but makes for valid code) 97 | while (tableRow.childNodes.length < 7) { 98 | var _cell = quickElement('td', tableRow, ' '); 99 | _cell.style.backgroundColor = '#f3f3f3'; 100 | } 101 | 102 | calDiv.appendChild(calTable); 103 | } 104 | } 105 | 106 | // Calendar -- A calendar instance 107 | function Calendar(div_id, callback) { 108 | // div_id (string) is the ID of the element in which the calendar will 109 | // be displayed 110 | // callback (string) is the name of a JavaScript function that will be 111 | // called with the parameters (year, month, day) when a day in the 112 | // calendar is clicked 113 | this.div_id = div_id; 114 | this.callback = callback; 115 | this.today = new Date(); 116 | this.currentMonth = this.today.getMonth() + 1; 117 | this.currentYear = this.today.getFullYear(); 118 | } 119 | Calendar.prototype = { 120 | drawCurrent: function() { 121 | CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback); 122 | }, 123 | drawDate: function(month, year) { 124 | this.currentMonth = month; 125 | this.currentYear = year; 126 | this.drawCurrent(); 127 | }, 128 | drawPreviousMonth: function() { 129 | if (this.currentMonth == 1) { 130 | this.currentMonth = 12; 131 | this.currentYear--; 132 | } 133 | else { 134 | this.currentMonth--; 135 | } 136 | this.drawCurrent(); 137 | }, 138 | drawNextMonth: function() { 139 | if (this.currentMonth == 12) { 140 | this.currentMonth = 1; 141 | this.currentYear++; 142 | } 143 | else { 144 | this.currentMonth++; 145 | } 146 | this.drawCurrent(); 147 | }, 148 | drawPreviousYear: function() { 149 | this.currentYear--; 150 | this.drawCurrent(); 151 | }, 152 | drawNextYear: function() { 153 | this.currentYear++; 154 | this.drawCurrent(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /static/admin/js/inlines.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Django admin inlines 3 | * 4 | * Based on jQuery Formset 1.1 5 | * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com) 6 | * @requires jQuery 1.2.6 or later 7 | * 8 | * Copyright (c) 2009, Stanislaus Madueke 9 | * All rights reserved. 10 | * 11 | * Spiced up with Code from Zain Memon's GSoC project 2009 12 | * and modified for Django by Jannis Leidel 13 | * 14 | * Licensed under the New BSD License 15 | * See: http://www.opensource.org/licenses/bsd-license.php 16 | */ 17 | (function($) { 18 | $.fn.formset = function(opts) { 19 | var options = $.extend({}, $.fn.formset.defaults, opts); 20 | var updateElementIndex = function(el, prefix, ndx) { 21 | var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))"); 22 | var replacement = prefix + "-" + ndx; 23 | if ($(el).attr("for")) { 24 | $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); 25 | } 26 | if (el.id) { 27 | el.id = el.id.replace(id_regex, replacement); 28 | } 29 | if (el.name) { 30 | el.name = el.name.replace(id_regex, replacement); 31 | } 32 | }; 33 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off"); 34 | var nextIndex = parseInt(totalForms.val()); 35 | var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off"); 36 | // only show the add button if we are allowed to add more items, 37 | // note that max_num = None translates to a blank string. 38 | var showAddButton = maxForms.val() == '' || (maxForms.val()-totalForms.val()) > 0; 39 | $(this).each(function(i) { 40 | $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); 41 | }); 42 | if ($(this).length && showAddButton) { 43 | var addButton; 44 | if ($(this).attr("tagName") == "TR") { 45 | // If forms are laid out as table rows, insert the 46 | // "add" button in a new table row: 47 | var numCols = this.eq(0).children().length; 48 | $(this).parent().append('' + options.addText + ""); 49 | addButton = $(this).parent().find("tr:last a"); 50 | } else { 51 | // Otherwise, insert it immediately after the last form: 52 | $(this).filter(":last").after('"); 53 | addButton = $(this).filter(":last").next().find("a"); 54 | } 55 | addButton.click(function() { 56 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); 57 | var template = $("#" + options.prefix + "-empty"); 58 | var row = template.clone(true); 59 | row.removeClass(options.emptyCssClass) 60 | .addClass(options.formCssClass) 61 | .attr("id", options.prefix + "-" + nextIndex); 62 | if (row.is("tr")) { 63 | // If the forms are laid out in table rows, insert 64 | // the remove button into the last table cell: 65 | row.children(":last").append('"); 66 | } else if (row.is("ul") || row.is("ol")) { 67 | // If they're laid out as an ordered/unordered list, 68 | // insert an
  • after the last list item: 69 | row.append('
  • ' + options.deleteText + "
  • "); 70 | } else { 71 | // Otherwise, just insert the remove button as the 72 | // last child element of the form's container: 73 | row.children(":first").append('' + options.deleteText + ""); 74 | } 75 | row.find("*").each(function() { 76 | updateElementIndex(this, options.prefix, totalForms.val()); 77 | }); 78 | // Insert the new form when it has been fully edited 79 | row.insertBefore($(template)); 80 | // Update number of total forms 81 | $(totalForms).val(parseInt(totalForms.val()) + 1); 82 | nextIndex += 1; 83 | // Hide add button in case we've hit the max, except we want to add infinitely 84 | if ((maxForms.val() != '') && (maxForms.val()-totalForms.val()) <= 0) { 85 | addButton.parent().hide(); 86 | } 87 | // The delete button of each row triggers a bunch of other things 88 | row.find("a." + options.deleteCssClass).click(function() { 89 | // Remove the parent form containing this button: 90 | var row = $(this).parents("." + options.formCssClass); 91 | row.remove(); 92 | nextIndex -= 1; 93 | // If a post-delete callback was provided, call it with the deleted form: 94 | if (options.removed) { 95 | options.removed(row); 96 | } 97 | // Update the TOTAL_FORMS form count. 98 | var forms = $("." + options.formCssClass); 99 | $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); 100 | // Show add button again once we drop below max 101 | if ((maxForms.val() == '') || (maxForms.val()-forms.length) > 0) { 102 | addButton.parent().show(); 103 | } 104 | // Also, update names and ids for all remaining form controls 105 | // so they remain in sequence: 106 | for (var i=0, formCount=forms.length; i -1) { 36 | // Token is an ID selector 37 | var bits = token.split('#'); 38 | var tagName = bits[0]; 39 | var id = bits[1]; 40 | var element = document.getElementById(id); 41 | if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) { 42 | // ID not found or tag with that ID not found, return false. 43 | return new Array(); 44 | } 45 | // Set currentContext to contain just this element 46 | currentContext = new Array(element); 47 | continue; // Skip to next token 48 | } 49 | if (token.indexOf('.') > -1) { 50 | // Token contains a class selector 51 | var bits = token.split('.'); 52 | var tagName = bits[0]; 53 | var className = bits[1]; 54 | if (!tagName) { 55 | tagName = '*'; 56 | } 57 | // Get elements matching tag, filter them for class selector 58 | var found = new Array; 59 | var foundCount = 0; 60 | for (var h = 0; h < currentContext.length; h++) { 61 | var elements; 62 | if (tagName == '*') { 63 | elements = getAllChildren(currentContext[h]); 64 | } else { 65 | try { 66 | elements = currentContext[h].getElementsByTagName(tagName); 67 | } 68 | catch(e) { 69 | elements = []; 70 | } 71 | } 72 | for (var j = 0; j < elements.length; j++) { 73 | found[foundCount++] = elements[j]; 74 | } 75 | } 76 | currentContext = new Array; 77 | var currentContextIndex = 0; 78 | for (var k = 0; k < found.length; k++) { 79 | if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) { 80 | currentContext[currentContextIndex++] = found[k]; 81 | } 82 | } 83 | continue; // Skip to next token 84 | } 85 | // Code to deal with attribute selectors 86 | if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { 87 | var tagName = RegExp.$1; 88 | var attrName = RegExp.$2; 89 | var attrOperator = RegExp.$3; 90 | var attrValue = RegExp.$4; 91 | if (!tagName) { 92 | tagName = '*'; 93 | } 94 | // Grab all of the tagName elements within current context 95 | var found = new Array; 96 | var foundCount = 0; 97 | for (var h = 0; h < currentContext.length; h++) { 98 | var elements; 99 | if (tagName == '*') { 100 | elements = getAllChildren(currentContext[h]); 101 | } else { 102 | elements = currentContext[h].getElementsByTagName(tagName); 103 | } 104 | for (var j = 0; j < elements.length; j++) { 105 | found[foundCount++] = elements[j]; 106 | } 107 | } 108 | currentContext = new Array; 109 | var currentContextIndex = 0; 110 | var checkFunction; // This function will be used to filter the elements 111 | switch (attrOperator) { 112 | case '=': // Equality 113 | checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); }; 114 | break; 115 | case '~': // Match one of space seperated words 116 | checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); }; 117 | break; 118 | case '|': // Match start with value followed by optional hyphen 119 | checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); }; 120 | break; 121 | case '^': // Match starts with value 122 | checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); }; 123 | break; 124 | case '$': // Match ends with value - fails with "Warning" in Opera 7 125 | checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); }; 126 | break; 127 | case '*': // Match ends with value 128 | checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); }; 129 | break; 130 | default : 131 | // Just test for existence of attribute 132 | checkFunction = function(e) { return e.getAttribute(attrName); }; 133 | } 134 | currentContext = new Array; 135 | var currentContextIndex = 0; 136 | for (var k = 0; k < found.length; k++) { 137 | if (checkFunction(found[k])) { 138 | currentContext[currentContextIndex++] = found[k]; 139 | } 140 | } 141 | // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue); 142 | continue; // Skip to next token 143 | } 144 | // If we get here, token is JUST an element (not a class or ID selector) 145 | tagName = token; 146 | var found = new Array; 147 | var foundCount = 0; 148 | for (var h = 0; h < currentContext.length; h++) { 149 | var elements = currentContext[h].getElementsByTagName(tagName); 150 | for (var j = 0; j < elements.length; j++) { 151 | found[foundCount++] = elements[j]; 152 | } 153 | } 154 | currentContext = found; 155 | } 156 | return currentContext; 157 | } 158 | 159 | /* That revolting regular expression explained 160 | /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/ 161 | \---/ \---/\-------------/ \-------/ 162 | | | | | 163 | | | | The value 164 | | | ~,|,^,$,* or = 165 | | Attribute 166 | Tag 167 | */ 168 | -------------------------------------------------------------------------------- /static/admin/css/forms.css: -------------------------------------------------------------------------------- 1 | @import url('widgets.css'); 2 | 3 | /* FORM ROWS */ 4 | 5 | .form-row { 6 | overflow: hidden; 7 | padding: 8px 12px; 8 | font-size: 11px; 9 | border-bottom: 1px solid #eee; 10 | } 11 | 12 | .form-row img, .form-row input { 13 | vertical-align: middle; 14 | } 15 | 16 | form .form-row p { 17 | padding-left: 0; 18 | font-size: 11px; 19 | } 20 | 21 | /* FORM LABELS */ 22 | 23 | form h4 { 24 | margin: 0 !important; 25 | padding: 0 !important; 26 | border: none !important; 27 | } 28 | 29 | label { 30 | font-weight: normal !important; 31 | color: #666; 32 | font-size: 12px; 33 | } 34 | 35 | .required label, label.required { 36 | font-weight: bold !important; 37 | color: #333 !important; 38 | } 39 | 40 | /* RADIO BUTTONS */ 41 | 42 | form ul.radiolist li { 43 | list-style-type: none; 44 | } 45 | 46 | form ul.radiolist label { 47 | float: none; 48 | display: inline; 49 | } 50 | 51 | form ul.inline { 52 | margin-left: 0; 53 | padding: 0; 54 | } 55 | 56 | form ul.inline li { 57 | float: left; 58 | padding-right: 7px; 59 | } 60 | 61 | /* ALIGNED FIELDSETS */ 62 | 63 | .aligned label { 64 | display: block; 65 | padding: 3px 10px 0 0; 66 | float: left; 67 | width: 8em; 68 | } 69 | 70 | .aligned ul label { 71 | display: inline; 72 | float: none; 73 | width: auto; 74 | } 75 | 76 | .colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { 77 | width: 350px; 78 | } 79 | 80 | form .aligned p, form .aligned ul { 81 | margin-left: 7em; 82 | padding-left: 30px; 83 | } 84 | 85 | form .aligned table p { 86 | margin-left: 0; 87 | padding-left: 0; 88 | } 89 | 90 | form .aligned p.help { 91 | padding-left: 38px; 92 | } 93 | 94 | .aligned .vCheckboxLabel { 95 | float: none !important; 96 | display: inline; 97 | padding-left: 4px; 98 | } 99 | 100 | .colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField { 101 | width: 610px; 102 | } 103 | 104 | .checkbox-row p.help { 105 | margin-left: 0; 106 | padding-left: 0 !important; 107 | } 108 | 109 | fieldset .field-box { 110 | float: left; 111 | margin-right: 20px; 112 | } 113 | 114 | /* WIDE FIELDSETS */ 115 | 116 | .wide label { 117 | width: 15em !important; 118 | } 119 | 120 | form .wide p { 121 | margin-left: 15em; 122 | } 123 | 124 | form .wide p.help { 125 | padding-left: 38px; 126 | } 127 | 128 | .colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { 129 | width: 450px; 130 | } 131 | 132 | /* COLLAPSED FIELDSETS */ 133 | 134 | fieldset.collapsed * { 135 | display: none; 136 | } 137 | 138 | fieldset.collapsed h2, fieldset.collapsed { 139 | display: block !important; 140 | } 141 | 142 | fieldset.collapsed h2 { 143 | background-image: url(../img/nav-bg.gif); 144 | background-position: bottom left; 145 | color: #999; 146 | } 147 | 148 | fieldset.collapsed .collapse-toggle { 149 | background: transparent; 150 | display: inline !important; 151 | } 152 | 153 | /* MONOSPACE TEXTAREAS */ 154 | 155 | fieldset.monospace textarea { 156 | font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace; 157 | } 158 | 159 | /* SUBMIT ROW */ 160 | 161 | .submit-row { 162 | padding: 5px 7px; 163 | text-align: right; 164 | background: white url(../img/nav-bg.gif) 0 100% repeat-x; 165 | border: 1px solid #ccc; 166 | margin: 5px 0; 167 | overflow: hidden; 168 | } 169 | 170 | body.popup .submit-row { 171 | overflow: auto; 172 | } 173 | 174 | .submit-row input { 175 | margin: 0 0 0 5px; 176 | } 177 | 178 | .submit-row p { 179 | margin: 0.3em; 180 | } 181 | 182 | .submit-row p.deletelink-box { 183 | float: left; 184 | } 185 | 186 | .submit-row .deletelink { 187 | background: url(../img/icon_deletelink.gif) 0 50% no-repeat; 188 | padding-left: 14px; 189 | } 190 | 191 | /* CUSTOM FORM FIELDS */ 192 | 193 | .vSelectMultipleField { 194 | vertical-align: top !important; 195 | } 196 | 197 | .vCheckboxField { 198 | border: none; 199 | } 200 | 201 | .vDateField, .vTimeField { 202 | margin-right: 2px; 203 | } 204 | 205 | .vURLField { 206 | width: 30em; 207 | } 208 | 209 | .vLargeTextField, .vXMLLargeTextField { 210 | width: 48em; 211 | } 212 | 213 | .flatpages-flatpage #id_content { 214 | height: 40.2em; 215 | } 216 | 217 | .module table .vPositiveSmallIntegerField { 218 | width: 2.2em; 219 | } 220 | 221 | .vTextField { 222 | width: 20em; 223 | } 224 | 225 | .vIntegerField { 226 | width: 5em; 227 | } 228 | 229 | .vForeignKeyRawIdAdminField { 230 | width: 5em; 231 | } 232 | 233 | /* INLINES */ 234 | 235 | .inline-group { 236 | padding: 0; 237 | border: 1px solid #ccc; 238 | margin: 10px 0; 239 | } 240 | 241 | .inline-group .aligned label { 242 | width: 8em; 243 | } 244 | 245 | .inline-related { 246 | position: relative; 247 | } 248 | 249 | .inline-related h3 { 250 | margin: 0; 251 | color: #666; 252 | padding: 3px 5px; 253 | font-size: 11px; 254 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 255 | border-bottom: 1px solid #ddd; 256 | } 257 | 258 | .inline-related h3 span.delete { 259 | float: right; 260 | } 261 | 262 | .inline-related h3 span.delete label { 263 | margin-left: 2px; 264 | font-size: 11px; 265 | } 266 | 267 | .inline-related fieldset { 268 | margin: 0; 269 | background: #fff; 270 | border: none; 271 | } 272 | 273 | .inline-related fieldset.module h3 { 274 | margin: 0; 275 | padding: 2px 5px 3px 5px; 276 | font-size: 11px; 277 | text-align: left; 278 | font-weight: bold; 279 | background: #bcd; 280 | color: #fff; 281 | } 282 | 283 | .inline-group .tabular fieldset.module { 284 | border: none; 285 | border-bottom: 1px solid #ddd; 286 | } 287 | 288 | .inline-related.tabular fieldset.module table { 289 | width: 100%; 290 | } 291 | 292 | .last-related fieldset { 293 | border: none; 294 | } 295 | 296 | .inline-group .tabular tr.has_original td { 297 | padding-top: 2em; 298 | } 299 | 300 | .inline-group .tabular tr td.original { 301 | padding: 2px 0 0 0; 302 | width: 0; 303 | _position: relative; 304 | } 305 | 306 | .inline-group .tabular th.original { 307 | width: 0px; 308 | padding: 0; 309 | } 310 | 311 | .inline-group .tabular td.original p { 312 | position: absolute; 313 | left: 0; 314 | height: 1.1em; 315 | padding: 2px 7px; 316 | overflow: hidden; 317 | font-size: 9px; 318 | font-weight: bold; 319 | color: #666; 320 | _width: 700px; 321 | } 322 | 323 | .inline-group ul.tools { 324 | padding: 0; 325 | margin: 0; 326 | list-style: none; 327 | } 328 | 329 | .inline-group ul.tools li { 330 | display: inline; 331 | padding: 0 5px; 332 | } 333 | 334 | .inline-group div.add-row, 335 | .inline-group .tabular tr.add-row td { 336 | color: #666; 337 | padding: 3px 5px; 338 | border-bottom: 1px solid #ddd; 339 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 340 | } 341 | 342 | .inline-group .tabular tr.add-row td { 343 | padding: 4px 5px 3px; 344 | border-bottom: none; 345 | } 346 | 347 | .inline-group ul.tools a.add, 348 | .inline-group div.add-row a, 349 | .inline-group .tabular tr.add-row td a { 350 | background: url(../img/icon_addlink.gif) 0 50% no-repeat; 351 | padding-left: 14px; 352 | font-size: 11px; 353 | outline: 0; /* Remove dotted border around link */ 354 | } 355 | 356 | .empty-form { 357 | display: none; 358 | } 359 | -------------------------------------------------------------------------------- /static/admin/js/core.js: -------------------------------------------------------------------------------- 1 | // Core javascript helper functions 2 | 3 | // basic browser identification & version 4 | var isOpera = (navigator.userAgent.indexOf("Opera")>=0) && parseFloat(navigator.appVersion); 5 | var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]); 6 | 7 | // Cross-browser event handlers. 8 | function addEvent(obj, evType, fn) { 9 | if (obj.addEventListener) { 10 | obj.addEventListener(evType, fn, false); 11 | return true; 12 | } else if (obj.attachEvent) { 13 | var r = obj.attachEvent("on" + evType, fn); 14 | return r; 15 | } else { 16 | return false; 17 | } 18 | } 19 | 20 | function removeEvent(obj, evType, fn) { 21 | if (obj.removeEventListener) { 22 | obj.removeEventListener(evType, fn, false); 23 | return true; 24 | } else if (obj.detachEvent) { 25 | obj.detachEvent("on" + evType, fn); 26 | return true; 27 | } else { 28 | return false; 29 | } 30 | } 31 | 32 | // quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]); 33 | function quickElement() { 34 | var obj = document.createElement(arguments[0]); 35 | if (arguments[2] != '' && arguments[2] != null) { 36 | var textNode = document.createTextNode(arguments[2]); 37 | obj.appendChild(textNode); 38 | } 39 | var len = arguments.length; 40 | for (var i = 3; i < len; i += 2) { 41 | obj.setAttribute(arguments[i], arguments[i+1]); 42 | } 43 | arguments[1].appendChild(obj); 44 | return obj; 45 | } 46 | 47 | // ---------------------------------------------------------------------------- 48 | // Cross-browser xmlhttp object 49 | // from http://jibbering.com/2002/4/httprequest.html 50 | // ---------------------------------------------------------------------------- 51 | var xmlhttp; 52 | /*@cc_on @*/ 53 | /*@if (@_jscript_version >= 5) 54 | try { 55 | xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 56 | } catch (e) { 57 | try { 58 | xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 59 | } catch (E) { 60 | xmlhttp = false; 61 | } 62 | } 63 | @else 64 | xmlhttp = false; 65 | @end @*/ 66 | if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { 67 | xmlhttp = new XMLHttpRequest(); 68 | } 69 | 70 | // ---------------------------------------------------------------------------- 71 | // Find-position functions by PPK 72 | // See http://www.quirksmode.org/js/findpos.html 73 | // ---------------------------------------------------------------------------- 74 | function findPosX(obj) { 75 | var curleft = 0; 76 | if (obj.offsetParent) { 77 | while (obj.offsetParent) { 78 | curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft); 79 | obj = obj.offsetParent; 80 | } 81 | // IE offsetParent does not include the top-level 82 | if (isIE && obj.parentElement){ 83 | curleft += obj.offsetLeft - obj.scrollLeft; 84 | } 85 | } else if (obj.x) { 86 | curleft += obj.x; 87 | } 88 | return curleft; 89 | } 90 | 91 | function findPosY(obj) { 92 | var curtop = 0; 93 | if (obj.offsetParent) { 94 | while (obj.offsetParent) { 95 | curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop); 96 | obj = obj.offsetParent; 97 | } 98 | // IE offsetParent does not include the top-level 99 | if (isIE && obj.parentElement){ 100 | curtop += obj.offsetTop - obj.scrollTop; 101 | } 102 | } else if (obj.y) { 103 | curtop += obj.y; 104 | } 105 | return curtop; 106 | } 107 | 108 | //----------------------------------------------------------------------------- 109 | // Date object extensions 110 | // ---------------------------------------------------------------------------- 111 | 112 | Date.prototype.getTwelveHours = function() { 113 | hours = this.getHours(); 114 | if (hours == 0) { 115 | return 12; 116 | } 117 | else { 118 | return hours <= 12 ? hours : hours-12 119 | } 120 | } 121 | 122 | Date.prototype.getTwoDigitMonth = function() { 123 | return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1); 124 | } 125 | 126 | Date.prototype.getTwoDigitDate = function() { 127 | return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate(); 128 | } 129 | 130 | Date.prototype.getTwoDigitTwelveHour = function() { 131 | return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours(); 132 | } 133 | 134 | Date.prototype.getTwoDigitHour = function() { 135 | return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours(); 136 | } 137 | 138 | Date.prototype.getTwoDigitMinute = function() { 139 | return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); 140 | } 141 | 142 | Date.prototype.getTwoDigitSecond = function() { 143 | return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds(); 144 | } 145 | 146 | Date.prototype.getHourMinute = function() { 147 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute(); 148 | } 149 | 150 | Date.prototype.getHourMinuteSecond = function() { 151 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond(); 152 | } 153 | 154 | Date.prototype.strftime = function(format) { 155 | var fields = { 156 | c: this.toString(), 157 | d: this.getTwoDigitDate(), 158 | H: this.getTwoDigitHour(), 159 | I: this.getTwoDigitTwelveHour(), 160 | m: this.getTwoDigitMonth(), 161 | M: this.getTwoDigitMinute(), 162 | p: (this.getHours() >= 12) ? 'PM' : 'AM', 163 | S: this.getTwoDigitSecond(), 164 | w: '0' + this.getDay(), 165 | x: this.toLocaleDateString(), 166 | X: this.toLocaleTimeString(), 167 | y: ('' + this.getFullYear()).substr(2, 4), 168 | Y: '' + this.getFullYear(), 169 | '%' : '%' 170 | }; 171 | var result = '', i = 0; 172 | while (i < format.length) { 173 | if (format.charAt(i) === '%') { 174 | result = result + fields[format.charAt(i + 1)]; 175 | ++i; 176 | } 177 | else { 178 | result = result + format.charAt(i); 179 | } 180 | ++i; 181 | } 182 | return result; 183 | } 184 | 185 | // ---------------------------------------------------------------------------- 186 | // String object extensions 187 | // ---------------------------------------------------------------------------- 188 | String.prototype.pad_left = function(pad_length, pad_string) { 189 | var new_string = this; 190 | for (var i = 0; new_string.length < pad_length; i++) { 191 | new_string = pad_string + new_string; 192 | } 193 | return new_string; 194 | } 195 | 196 | // ---------------------------------------------------------------------------- 197 | // Get the computed style for and element 198 | // ---------------------------------------------------------------------------- 199 | function getStyle(oElm, strCssRule){ 200 | var strValue = ""; 201 | if(document.defaultView && document.defaultView.getComputedStyle){ 202 | strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); 203 | } 204 | else if(oElm.currentStyle){ 205 | strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ 206 | return p1.toUpperCase(); 207 | }); 208 | strValue = oElm.currentStyle[strCssRule]; 209 | } 210 | return strValue; 211 | } 212 | -------------------------------------------------------------------------------- /static/admin/js/SelectFilter2.js: -------------------------------------------------------------------------------- 1 | /* 2 | SelectFilter2 - Turns a multiple-select box into a filter interface. 3 | 4 | Requires core.js, SelectBox.js and addevent.js. 5 | */ 6 | (function($) { 7 | function findForm(node) { 8 | // returns the node of the form containing the given node 9 | if (node.tagName.toLowerCase() != 'form') { 10 | return findForm(node.parentNode); 11 | } 12 | return node; 13 | } 14 | 15 | window.SelectFilter = { 16 | init: function(field_id, field_name, is_stacked, admin_media_prefix) { 17 | if (field_id.match(/__prefix__/)){ 18 | // Don't intialize on empty forms. 19 | return; 20 | } 21 | var from_box = document.getElementById(field_id); 22 | from_box.id += '_from'; // change its ID 23 | from_box.className = 'filtered'; 24 | 25 | var ps = from_box.parentNode.getElementsByTagName('p'); 26 | for (var i=0; i, because it just gets in the way. 29 | from_box.parentNode.removeChild(ps[i]); 30 | } else if (ps[i].className.indexOf("help") != -1) { 31 | // Move help text up to the top so it isn't below the select 32 | // boxes or wrapped off on the side to the right of the add 33 | // button: 34 | from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild); 35 | } 36 | } 37 | 38 | //
    or
    39 | var selector_div = quickElement('div', from_box.parentNode); 40 | selector_div.className = is_stacked ? 'selector stacked' : 'selector'; 41 | 42 | //
    43 | var selector_available = quickElement('div', selector_div, ''); 44 | selector_available.className = 'selector-available'; 45 | var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name])); 46 | quickElement('img', title_available, '', 'src', admin_media_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of available %s. You may choose some by selecting them in the box below and then clicking the "Choose" arrow between the two boxes.'), [field_name])); 47 | 48 | var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter'); 49 | filter_p.className = 'selector-filter'; 50 | 51 | var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + "_input"); 52 | 53 | var search_selector_img = quickElement('img', search_filter_label, '', 'src', admin_media_prefix + 'img/selector-search.gif', 'class', 'help-tooltip', 'alt', '', 'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])); 54 | 55 | filter_p.appendChild(document.createTextNode(' ')); 56 | 57 | var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter")); 58 | filter_input.id = field_id + '_input'; 59 | 60 | selector_available.appendChild(from_box); 61 | var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + '_from", "' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_all_link'); 62 | choose_all.className = 'selector-chooseall'; 63 | 64 | //
      65 | var selector_chooser = quickElement('ul', selector_div, ''); 66 | selector_chooser.className = 'selector-chooser'; 67 | var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_from","' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_link'); 68 | add_link.className = 'selector-add'; 69 | var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_link'); 70 | remove_link.className = 'selector-remove'; 71 | 72 | //
      73 | var selector_chosen = quickElement('div', selector_div, ''); 74 | selector_chosen.className = 'selector-chosen'; 75 | var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name])); 76 | quickElement('img', title_chosen, '', 'src', admin_media_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of chosen %s. You may remove some by selecting them in the box below and then clicking the "Remove" arrow between the two boxes.'), [field_name])); 77 | 78 | var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name')); 79 | to_box.className = 'filtered'; 80 | var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_all_link'); 81 | clear_all.className = 'selector-clearall'; 82 | 83 | from_box.setAttribute('name', from_box.getAttribute('name') + '_old'); 84 | 85 | // Set up the JavaScript event handlers for the select box filter interface 86 | addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); }); 87 | addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); }); 88 | addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) }); 89 | addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) }); 90 | addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); }); 91 | addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); }); 92 | addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); }); 93 | SelectBox.init(field_id + '_from'); 94 | SelectBox.init(field_id + '_to'); 95 | // Move selected from_box options to to_box 96 | SelectBox.move(field_id + '_from', field_id + '_to'); 97 | 98 | if (!is_stacked) { 99 | // In horizontal mode, give the same height to the two boxes. 100 | var j_from_box = $(from_box); 101 | var j_to_box = $(to_box); 102 | var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); } 103 | if (j_from_box.outerHeight() > 0) { 104 | resize_filters(); // This fieldset is already open. Resize now. 105 | } else { 106 | // This fieldset is probably collapsed. Wait for its 'show' event. 107 | j_to_box.closest('fieldset').one('show.fieldset', resize_filters); 108 | } 109 | } 110 | 111 | // Initial icon refresh 112 | SelectFilter.refresh_icons(field_id); 113 | }, 114 | refresh_icons: function(field_id) { 115 | var from = $('#' + field_id + '_from'); 116 | var to = $('#' + field_id + '_to'); 117 | var is_from_selected = from.find('option:selected').length > 0; 118 | var is_to_selected = to.find('option:selected').length > 0; 119 | // Active if at least one item is selected 120 | $('#' + field_id + '_add_link').toggleClass('active', is_from_selected); 121 | $('#' + field_id + '_remove_link').toggleClass('active', is_to_selected); 122 | // Active if the corresponding box isn't empty 123 | $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0); 124 | $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0); 125 | }, 126 | filter_key_up: function(event, field_id) { 127 | var from = document.getElementById(field_id + '_from'); 128 | // don't submit form if user pressed Enter 129 | if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) { 130 | from.selectedIndex = 0; 131 | SelectBox.move(field_id + '_from', field_id + '_to'); 132 | from.selectedIndex = 0; 133 | return false; 134 | } 135 | var temp = from.selectedIndex; 136 | SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value); 137 | from.selectedIndex = temp; 138 | return true; 139 | }, 140 | filter_key_down: function(event, field_id) { 141 | var from = document.getElementById(field_id + '_from'); 142 | // right arrow -- move across 143 | if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) { 144 | var old_index = from.selectedIndex; 145 | SelectBox.move(field_id + '_from', field_id + '_to'); 146 | from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index; 147 | return false; 148 | } 149 | // down arrow -- wrap around 150 | if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) { 151 | from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1; 152 | } 153 | // up arrow -- wrap around 154 | if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) { 155 | from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1; 156 | } 157 | return true; 158 | } 159 | } 160 | 161 | })(django.jQuery); 162 | -------------------------------------------------------------------------------- /static/admin/css/widgets.css: -------------------------------------------------------------------------------- 1 | /* SELECTOR (FILTER INTERFACE) */ 2 | 3 | .selector { 4 | width: 580px; 5 | float: left; 6 | } 7 | 8 | .selector select { 9 | width: 270px; 10 | height: 17.2em; 11 | } 12 | 13 | .selector-available, .selector-chosen { 14 | float: left; 15 | width: 270px; 16 | text-align: center; 17 | margin-bottom: 5px; 18 | } 19 | 20 | .selector-chosen select { 21 | border-top: none; 22 | } 23 | 24 | .selector-available h2, .selector-chosen h2 { 25 | border: 1px solid #ccc; 26 | } 27 | 28 | .selector .selector-available h2 { 29 | background: white url(../img/nav-bg.gif) bottom left repeat-x; 30 | color: #666; 31 | } 32 | 33 | .selector .selector-filter { 34 | background: white; 35 | border: 1px solid #ccc; 36 | border-width: 0 1px; 37 | padding: 3px; 38 | color: #999; 39 | font-size: 10px; 40 | margin: 0; 41 | text-align: left; 42 | } 43 | 44 | .selector .selector-filter label { 45 | width: 16px; 46 | padding: 2px; 47 | } 48 | 49 | .selector .selector-available input { 50 | width: 230px; 51 | } 52 | 53 | .selector ul.selector-chooser { 54 | float: left; 55 | width: 22px; 56 | height: 50px; 57 | background: url(../img/chooser-bg.gif) top center no-repeat; 58 | margin: 10em 5px 0 5px; 59 | padding: 0; 60 | } 61 | 62 | .selector-chooser li { 63 | margin: 0; 64 | padding: 3px; 65 | list-style-type: none; 66 | } 67 | 68 | .selector select { 69 | margin-bottom: 10px; 70 | margin-top: 0; 71 | } 72 | 73 | .selector-add, .selector-remove { 74 | width: 16px; 75 | height: 16px; 76 | display: block; 77 | text-indent: -3000px; 78 | overflow: hidden; 79 | } 80 | 81 | .selector-add { 82 | background: url(../img/selector-icons.gif) 0 -161px no-repeat; 83 | cursor: default; 84 | margin-bottom: 2px; 85 | } 86 | 87 | .active.selector-add { 88 | background: url(../img/selector-icons.gif) 0 -187px no-repeat; 89 | cursor: pointer; 90 | } 91 | 92 | .selector-remove { 93 | background: url(../img/selector-icons.gif) 0 -109px no-repeat; 94 | cursor: default; 95 | } 96 | 97 | .active.selector-remove { 98 | background: url(../img/selector-icons.gif) 0 -135px no-repeat; 99 | cursor: pointer; 100 | } 101 | 102 | a.selector-chooseall, a.selector-clearall { 103 | display: inline-block; 104 | text-align: left; 105 | margin-left: auto; 106 | margin-right: auto; 107 | font-weight: bold; 108 | color: #666; 109 | } 110 | 111 | a.selector-chooseall { 112 | padding: 3px 18px 3px 0; 113 | } 114 | 115 | a.selector-clearall { 116 | padding: 3px 0 3px 18px; 117 | } 118 | 119 | a.active.selector-chooseall:hover, a.active.selector-clearall:hover { 120 | color: #036; 121 | } 122 | 123 | a.selector-chooseall { 124 | background: url(../img/selector-icons.gif) right -263px no-repeat; 125 | cursor: default; 126 | } 127 | 128 | a.active.selector-chooseall { 129 | background: url(../img/selector-icons.gif) right -289px no-repeat; 130 | cursor: pointer; 131 | } 132 | 133 | a.selector-clearall { 134 | background: url(../img/selector-icons.gif) left -211px no-repeat; 135 | cursor: default; 136 | } 137 | 138 | a.active.selector-clearall { 139 | background: url(../img/selector-icons.gif) left -237px no-repeat; 140 | cursor: pointer; 141 | } 142 | 143 | /* STACKED SELECTORS */ 144 | 145 | .stacked { 146 | float: left; 147 | width: 500px; 148 | } 149 | 150 | .stacked select { 151 | width: 480px; 152 | height: 10.1em; 153 | } 154 | 155 | .stacked .selector-available, .stacked .selector-chosen { 156 | width: 480px; 157 | } 158 | 159 | .stacked .selector-available { 160 | margin-bottom: 0; 161 | } 162 | 163 | .stacked .selector-available input { 164 | width: 442px; 165 | } 166 | 167 | .stacked ul.selector-chooser { 168 | height: 22px; 169 | width: 50px; 170 | margin: 0 0 3px 40%; 171 | background: url(../img/chooser_stacked-bg.gif) top center no-repeat; 172 | } 173 | 174 | .stacked .selector-chooser li { 175 | float: left; 176 | padding: 3px 3px 3px 5px; 177 | } 178 | 179 | .stacked .selector-chooseall, .stacked .selector-clearall { 180 | display: none; 181 | } 182 | 183 | .stacked .selector-add { 184 | background: url(../img/selector-icons.gif) 0 -57px no-repeat; 185 | cursor: default; 186 | } 187 | 188 | .stacked .active.selector-add { 189 | background: url(../img/selector-icons.gif) 0 -83px no-repeat; 190 | cursor: pointer; 191 | } 192 | 193 | .stacked .selector-remove { 194 | background: url(../img/selector-icons.gif) 0 -5px no-repeat; 195 | cursor: default; 196 | } 197 | 198 | .stacked .active.selector-remove { 199 | background: url(../img/selector-icons.gif) 0 -31px no-repeat; 200 | cursor: pointer; 201 | } 202 | 203 | /* DATE AND TIME */ 204 | 205 | p.datetime { 206 | line-height: 20px; 207 | margin: 0; 208 | padding: 0; 209 | color: #666; 210 | font-size: 11px; 211 | font-weight: bold; 212 | } 213 | 214 | .datetime span { 215 | font-size: 11px; 216 | color: #ccc; 217 | font-weight: normal; 218 | white-space: nowrap; 219 | } 220 | 221 | table p.datetime { 222 | font-size: 10px; 223 | margin-left: 0; 224 | padding-left: 0; 225 | } 226 | 227 | /* FILE UPLOADS */ 228 | 229 | p.file-upload { 230 | line-height: 20px; 231 | margin: 0; 232 | padding: 0; 233 | color: #666; 234 | font-size: 11px; 235 | font-weight: bold; 236 | } 237 | 238 | .file-upload a { 239 | font-weight: normal; 240 | } 241 | 242 | .file-upload .deletelink { 243 | margin-left: 5px; 244 | } 245 | 246 | span.clearable-file-input label { 247 | color: #333; 248 | font-size: 11px; 249 | display: inline; 250 | float: none; 251 | } 252 | 253 | /* CALENDARS & CLOCKS */ 254 | 255 | .calendarbox, .clockbox { 256 | margin: 5px auto; 257 | font-size: 11px; 258 | width: 16em; 259 | text-align: center; 260 | background: white; 261 | position: relative; 262 | } 263 | 264 | .clockbox { 265 | width: auto; 266 | } 267 | 268 | .calendar { 269 | margin: 0; 270 | padding: 0; 271 | } 272 | 273 | .calendar table { 274 | margin: 0; 275 | padding: 0; 276 | border-collapse: collapse; 277 | background: white; 278 | width: 100%; 279 | } 280 | 281 | .calendar caption, .calendarbox h2 { 282 | margin: 0; 283 | font-size: 11px; 284 | text-align: center; 285 | border-top: none; 286 | } 287 | 288 | .calendar th { 289 | font-size: 10px; 290 | color: #666; 291 | padding: 2px 3px; 292 | text-align: center; 293 | background: #e1e1e1 url(../img/nav-bg.gif) 0 50% repeat-x; 294 | border-bottom: 1px solid #ddd; 295 | } 296 | 297 | .calendar td { 298 | font-size: 11px; 299 | text-align: center; 300 | padding: 0; 301 | border-top: 1px solid #eee; 302 | border-bottom: none; 303 | } 304 | 305 | .calendar td.selected a { 306 | background: #C9DBED; 307 | } 308 | 309 | .calendar td.nonday { 310 | background: #efefef; 311 | } 312 | 313 | .calendar td.today a { 314 | background: #ffc; 315 | } 316 | 317 | .calendar td a, .timelist a { 318 | display: block; 319 | font-weight: bold; 320 | padding: 4px; 321 | text-decoration: none; 322 | color: #444; 323 | } 324 | 325 | .calendar td a:hover, .timelist a:hover { 326 | background: #5b80b2; 327 | color: white; 328 | } 329 | 330 | .calendar td a:active, .timelist a:active { 331 | background: #036; 332 | color: white; 333 | } 334 | 335 | .calendarnav { 336 | font-size: 10px; 337 | text-align: center; 338 | color: #ccc; 339 | margin: 0; 340 | padding: 1px 3px; 341 | } 342 | 343 | .calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover { 344 | color: #999; 345 | } 346 | 347 | .calendar-shortcuts { 348 | background: white; 349 | font-size: 10px; 350 | line-height: 11px; 351 | border-top: 1px solid #eee; 352 | padding: 3px 0 4px; 353 | color: #ccc; 354 | } 355 | 356 | .calendarbox .calendarnav-previous, .calendarbox .calendarnav-next { 357 | display: block; 358 | position: absolute; 359 | font-weight: bold; 360 | font-size: 12px; 361 | background: #C9DBED url(../img/default-bg.gif) bottom left repeat-x; 362 | padding: 1px 4px 2px 4px; 363 | color: white; 364 | } 365 | 366 | .calendarnav-previous:hover, .calendarnav-next:hover { 367 | background: #036; 368 | } 369 | 370 | .calendarnav-previous { 371 | top: 0; 372 | left: 0; 373 | } 374 | 375 | .calendarnav-next { 376 | top: 0; 377 | right: 0; 378 | } 379 | 380 | .calendar-cancel { 381 | margin: 0 !important; 382 | padding: 0 !important; 383 | font-size: 10px; 384 | background: #e1e1e1 url(../img/nav-bg.gif) 0 50% repeat-x; 385 | border-top: 1px solid #ddd; 386 | } 387 | 388 | .calendar-cancel:hover { 389 | background: #e1e1e1 url(../img/nav-bg-reverse.gif) 0 50% repeat-x; 390 | } 391 | 392 | .calendar-cancel a { 393 | color: black; 394 | display: block; 395 | } 396 | 397 | ul.timelist, .timelist li { 398 | list-style-type: none; 399 | margin: 0; 400 | padding: 0; 401 | } 402 | 403 | .timelist a { 404 | padding: 2px; 405 | } 406 | 407 | /* INLINE ORDERER */ 408 | 409 | ul.orderer { 410 | position: relative; 411 | padding: 0 !important; 412 | margin: 0 !important; 413 | list-style-type: none; 414 | } 415 | 416 | ul.orderer li { 417 | list-style-type: none; 418 | display: block; 419 | padding: 0; 420 | margin: 0; 421 | border: 1px solid #bbb; 422 | border-width: 0 1px 1px 0; 423 | white-space: nowrap; 424 | overflow: hidden; 425 | background: #e2e2e2 url(../img/nav-bg-grabber.gif) repeat-y; 426 | } 427 | 428 | ul.orderer li:hover { 429 | cursor: move; 430 | background-color: #ddd; 431 | } 432 | 433 | ul.orderer li a.selector { 434 | margin-left: 12px; 435 | overflow: hidden; 436 | width: 83%; 437 | font-size: 10px !important; 438 | padding: 0.6em 0; 439 | } 440 | 441 | ul.orderer li a:link, ul.orderer li a:visited { 442 | color: #333; 443 | } 444 | 445 | ul.orderer li .inline-deletelink { 446 | position: absolute; 447 | right: 4px; 448 | margin-top: 0.6em; 449 | } 450 | 451 | ul.orderer li.selected { 452 | background-color: #f8f8f8; 453 | border-right-color: #f8f8f8; 454 | } 455 | 456 | ul.orderer li.deleted { 457 | background: #bbb url(../img/deleted-overlay.gif); 458 | } 459 | 460 | ul.orderer li.deleted a:link, ul.orderer li.deleted a:visited { 461 | color: #888; 462 | } 463 | 464 | ul.orderer li.deleted .inline-deletelink { 465 | background-image: url(../img/inline-restore.png); 466 | } 467 | 468 | ul.orderer li.deleted:hover, ul.orderer li.deleted a.selector:hover { 469 | cursor: default; 470 | } 471 | 472 | /* EDIT INLINE */ 473 | 474 | .inline-deletelink { 475 | float: right; 476 | text-indent: -9999px; 477 | background: transparent url(../img/inline-delete.png) no-repeat; 478 | width: 15px; 479 | height: 15px; 480 | border: 0px none; 481 | outline: 0; /* Remove dotted border around link */ 482 | } 483 | 484 | .inline-deletelink:hover { 485 | background-position: -15px 0; 486 | cursor: pointer; 487 | } 488 | 489 | .editinline button.addlink { 490 | border: 0px none; 491 | color: #5b80b2; 492 | font-size: 100%; 493 | cursor: pointer; 494 | } 495 | 496 | .editinline button.addlink:hover { 497 | color: #036; 498 | cursor: pointer; 499 | } 500 | 501 | .editinline table .help { 502 | text-align: right; 503 | float: right; 504 | padding-left: 2em; 505 | } 506 | 507 | .editinline tfoot .addlink { 508 | white-space: nowrap; 509 | } 510 | 511 | .editinline table thead th:last-child { 512 | border-left: none; 513 | } 514 | 515 | .editinline tr.deleted { 516 | background: #ddd url(../img/deleted-overlay.gif); 517 | } 518 | 519 | .editinline tr.deleted .inline-deletelink { 520 | background-image: url(../img/inline-restore.png); 521 | } 522 | 523 | .editinline tr.deleted td:hover { 524 | cursor: default; 525 | } 526 | 527 | .editinline tr.deleted td:first-child { 528 | background-image: none !important; 529 | } 530 | 531 | /* EDIT INLINE - STACKED */ 532 | 533 | .editinline-stacked { 534 | min-width: 758px; 535 | } 536 | 537 | .editinline-stacked .inline-object { 538 | margin-left: 210px; 539 | background: white; 540 | } 541 | 542 | .editinline-stacked .inline-source { 543 | float: left; 544 | width: 200px; 545 | background: #f8f8f8; 546 | } 547 | 548 | .editinline-stacked .inline-splitter { 549 | float: left; 550 | width: 9px; 551 | background: #f8f8f8 url(../img/inline-splitter-bg.gif) 50% 50% no-repeat; 552 | border-right: 1px solid #ccc; 553 | } 554 | 555 | .editinline-stacked .controls { 556 | clear: both; 557 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 558 | padding: 3px 4px; 559 | font-size: 11px; 560 | border-top: 1px solid #ddd; 561 | } 562 | 563 | -------------------------------------------------------------------------------- /static/admin/js/admin/DateTimeShortcuts.js: -------------------------------------------------------------------------------- 1 | // Inserts shortcut buttons after all of the following: 2 | // 3 | // 4 | 5 | var DateTimeShortcuts = { 6 | calendars: [], 7 | calendarInputs: [], 8 | clockInputs: [], 9 | calendarDivName1: 'calendarbox', // name of calendar
      that gets toggled 10 | calendarDivName2: 'calendarin', // name of
      that contains calendar 11 | calendarLinkName: 'calendarlink',// name of the link that is used to toggle 12 | clockDivName: 'clockbox', // name of clock
      that gets toggled 13 | clockLinkName: 'clocklink', // name of the link that is used to toggle 14 | shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts 15 | admin_media_prefix: '', 16 | init: function() { 17 | // Get admin_media_prefix by grabbing it off the window object. It's 18 | // set in the admin/base.html template, so if it's not there, someone's 19 | // overridden the template. In that case, we'll set a clearly-invalid 20 | // value in the hopes that someone will examine HTTP requests and see it. 21 | if (window.__admin_media_prefix__ != undefined) { 22 | DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__; 23 | } else { 24 | DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/'; 25 | } 26 | 27 | var inputs = document.getElementsByTagName('input'); 28 | for (i=0; i 63 | //

      Choose a time

      64 | // 70 | //

      Cancel

      71 | //
      72 | 73 | var clock_box = document.createElement('div'); 74 | clock_box.style.display = 'none'; 75 | clock_box.style.position = 'absolute'; 76 | clock_box.className = 'clockbox module'; 77 | clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num); 78 | document.body.appendChild(clock_box); 79 | addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation); 80 | 81 | quickElement('h2', clock_box, gettext('Choose a time')); 82 | var time_list = quickElement('ul', clock_box, ''); 83 | time_list.className = 'timelist'; 84 | var time_format = get_format('TIME_INPUT_FORMATS')[0]; 85 | quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));"); 86 | quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));"); 87 | quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));"); 88 | quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));"); 89 | 90 | var cancel_p = quickElement('p', clock_box, ''); 91 | cancel_p.className = 'calendar-cancel'; 92 | quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');'); 93 | django.jQuery(document).bind('keyup', function(event) { 94 | if (event.which == 27) { 95 | // ESC key closes popup 96 | DateTimeShortcuts.dismissClock(num); 97 | event.preventDefault(); 98 | } 99 | }); 100 | }, 101 | openClock: function(num) { 102 | var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num) 103 | var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num) 104 | 105 | // Recalculate the clockbox position 106 | // is it left-to-right or right-to-left layout ? 107 | if (getStyle(document.body,'direction')!='rtl') { 108 | clock_box.style.left = findPosX(clock_link) + 17 + 'px'; 109 | } 110 | else { 111 | // since style's width is in em, it'd be tough to calculate 112 | // px value of it. let's use an estimated px for now 113 | // TODO: IE returns wrong value for findPosX when in rtl mode 114 | // (it returns as it was left aligned), needs to be fixed. 115 | clock_box.style.left = findPosX(clock_link) - 110 + 'px'; 116 | } 117 | clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px'; 118 | 119 | // Show the clock box 120 | clock_box.style.display = 'block'; 121 | addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; }); 122 | }, 123 | dismissClock: function(num) { 124 | document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none'; 125 | window.document.onclick = null; 126 | }, 127 | handleClockQuicklink: function(num, val) { 128 | DateTimeShortcuts.clockInputs[num].value = val; 129 | DateTimeShortcuts.clockInputs[num].focus(); 130 | DateTimeShortcuts.dismissClock(num); 131 | }, 132 | // Add calendar widget to a given field. 133 | addCalendar: function(inp) { 134 | var num = DateTimeShortcuts.calendars.length; 135 | 136 | DateTimeShortcuts.calendarInputs[num] = inp; 137 | 138 | // Shortcut links (calendar icon and "Today" link) 139 | var shortcuts_span = document.createElement('span'); 140 | shortcuts_span.className = DateTimeShortcuts.shortCutsClass; 141 | inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling); 142 | var today_link = document.createElement('a'); 143 | today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);'); 144 | today_link.appendChild(document.createTextNode(gettext('Today'))); 145 | var cal_link = document.createElement('a'); 146 | cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');'); 147 | cal_link.id = DateTimeShortcuts.calendarLinkName + num; 148 | quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/icon_calendar.gif', 'alt', gettext('Calendar')); 149 | shortcuts_span.appendChild(document.createTextNode('\240')); 150 | shortcuts_span.appendChild(today_link); 151 | shortcuts_span.appendChild(document.createTextNode('\240|\240')); 152 | shortcuts_span.appendChild(cal_link); 153 | 154 | // Create calendarbox div. 155 | // 156 | // Markup looks like: 157 | // 158 | //
      159 | //

      160 | // 161 | // February 2003 162 | //

      163 | //
      164 | // 165 | //
      166 | //
      167 | // Yesterday | Today | Tomorrow 168 | //
      169 | //

      Cancel

      170 | //
      171 | var cal_box = document.createElement('div'); 172 | cal_box.style.display = 'none'; 173 | cal_box.style.position = 'absolute'; 174 | cal_box.className = 'calendarbox module'; 175 | cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num); 176 | document.body.appendChild(cal_box); 177 | addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation); 178 | 179 | // next-prev links 180 | var cal_nav = quickElement('div', cal_box, ''); 181 | var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');'); 182 | cal_nav_prev.className = 'calendarnav-previous'; 183 | var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');'); 184 | cal_nav_next.className = 'calendarnav-next'; 185 | 186 | // main box 187 | var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num); 188 | cal_main.className = 'calendar'; 189 | DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num)); 190 | DateTimeShortcuts.calendars[num].drawCurrent(); 191 | 192 | // calendar shortcuts 193 | var shortcuts = quickElement('div', cal_box, ''); 194 | shortcuts.className = 'calendar-shortcuts'; 195 | quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);'); 196 | shortcuts.appendChild(document.createTextNode('\240|\240')); 197 | quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);'); 198 | shortcuts.appendChild(document.createTextNode('\240|\240')); 199 | quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);'); 200 | 201 | // cancel bar 202 | var cancel_p = quickElement('p', cal_box, ''); 203 | cancel_p.className = 'calendar-cancel'; 204 | quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');'); 205 | django.jQuery(document).bind('keyup', function(event) { 206 | if (event.which == 27) { 207 | // ESC key closes popup 208 | DateTimeShortcuts.dismissCalendar(num); 209 | event.preventDefault(); 210 | } 211 | }); 212 | }, 213 | openCalendar: function(num) { 214 | var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num) 215 | var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num) 216 | var inp = DateTimeShortcuts.calendarInputs[num]; 217 | 218 | // Determine if the current value in the input has a valid date. 219 | // If so, draw the calendar with that date's year and month. 220 | if (inp.value) { 221 | var date_parts = inp.value.split('-'); 222 | var year = date_parts[0]; 223 | var month = parseFloat(date_parts[1]); 224 | if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) { 225 | DateTimeShortcuts.calendars[num].drawDate(month, year); 226 | } 227 | } 228 | 229 | // Recalculate the clockbox position 230 | // is it left-to-right or right-to-left layout ? 231 | if (getStyle(document.body,'direction')!='rtl') { 232 | cal_box.style.left = findPosX(cal_link) + 17 + 'px'; 233 | } 234 | else { 235 | // since style's width is in em, it'd be tough to calculate 236 | // px value of it. let's use an estimated px for now 237 | // TODO: IE returns wrong value for findPosX when in rtl mode 238 | // (it returns as it was left aligned), needs to be fixed. 239 | cal_box.style.left = findPosX(cal_link) - 180 + 'px'; 240 | } 241 | cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px'; 242 | 243 | cal_box.style.display = 'block'; 244 | addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; }); 245 | }, 246 | dismissCalendar: function(num) { 247 | document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none'; 248 | window.document.onclick = null; 249 | }, 250 | drawPrev: function(num) { 251 | DateTimeShortcuts.calendars[num].drawPreviousMonth(); 252 | }, 253 | drawNext: function(num) { 254 | DateTimeShortcuts.calendars[num].drawNextMonth(); 255 | }, 256 | handleCalendarCallback: function(num) { 257 | format = get_format('DATE_INPUT_FORMATS')[0]; 258 | // the format needs to be escaped a little 259 | format = format.replace('\\', '\\\\'); 260 | format = format.replace('\r', '\\r'); 261 | format = format.replace('\n', '\\n'); 262 | format = format.replace('\t', '\\t'); 263 | format = format.replace("'", "\\'"); 264 | return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[", 265 | num, 266 | "].value = new Date(y, m-1, d).strftime('", 267 | format, 268 | "');DateTimeShortcuts.calendarInputs[", 269 | num, 270 | "].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+", 271 | num, 272 | ").style.display='none';}"].join(''); 273 | }, 274 | handleCalendarQuickLink: function(num, offset) { 275 | var d = new Date(); 276 | d.setDate(d.getDate() + offset) 277 | DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]); 278 | DateTimeShortcuts.calendarInputs[num].focus(); 279 | DateTimeShortcuts.dismissCalendar(num); 280 | }, 281 | cancelEventPropagation: function(e) { 282 | if (!e) e = window.event; 283 | e.cancelBubble = true; 284 | if (e.stopPropagation) e.stopPropagation(); 285 | } 286 | } 287 | 288 | addEvent(window, 'load', DateTimeShortcuts.init); 289 | -------------------------------------------------------------------------------- /static/admin/css/base.css: -------------------------------------------------------------------------------- 1 | /* 2 | DJANGO Admin styles 3 | */ 4 | 5 | body { 6 | margin: 0; 7 | padding: 0; 8 | font-size: 12px; 9 | font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; 10 | color: #333; 11 | background: #fff; 12 | } 13 | 14 | /* LINKS */ 15 | 16 | a:link, a:visited { 17 | color: #5b80b2; 18 | text-decoration: none; 19 | } 20 | 21 | a:hover { 22 | color: #036; 23 | } 24 | 25 | a img { 26 | border: none; 27 | } 28 | 29 | a.section:link, a.section:visited { 30 | color: white; 31 | text-decoration: none; 32 | } 33 | 34 | /* GLOBAL DEFAULTS */ 35 | 36 | p, ol, ul, dl { 37 | margin: .2em 0 .8em 0; 38 | } 39 | 40 | p { 41 | padding: 0; 42 | line-height: 140%; 43 | } 44 | 45 | h1,h2,h3,h4,h5 { 46 | font-weight: bold; 47 | } 48 | 49 | h1 { 50 | font-size: 18px; 51 | color: #666; 52 | padding: 0 6px 0 0; 53 | margin: 0 0 .2em 0; 54 | } 55 | 56 | h2 { 57 | font-size: 16px; 58 | margin: 1em 0 .5em 0; 59 | } 60 | 61 | h2.subhead { 62 | font-weight: normal; 63 | margin-top: 0; 64 | } 65 | 66 | h3 { 67 | font-size: 14px; 68 | margin: .8em 0 .3em 0; 69 | color: #666; 70 | font-weight: bold; 71 | } 72 | 73 | h4 { 74 | font-size: 12px; 75 | margin: 1em 0 .8em 0; 76 | padding-bottom: 3px; 77 | } 78 | 79 | h5 { 80 | font-size: 10px; 81 | margin: 1.5em 0 .5em 0; 82 | color: #666; 83 | text-transform: uppercase; 84 | letter-spacing: 1px; 85 | } 86 | 87 | ul li { 88 | list-style-type: square; 89 | padding: 1px 0; 90 | } 91 | 92 | ul.plainlist { 93 | margin-left: 0 !important; 94 | } 95 | 96 | ul.plainlist li { 97 | list-style-type: none; 98 | } 99 | 100 | li ul { 101 | margin-bottom: 0; 102 | } 103 | 104 | li, dt, dd { 105 | font-size: 11px; 106 | line-height: 14px; 107 | } 108 | 109 | dt { 110 | font-weight: bold; 111 | margin-top: 4px; 112 | } 113 | 114 | dd { 115 | margin-left: 0; 116 | } 117 | 118 | form { 119 | margin: 0; 120 | padding: 0; 121 | } 122 | 123 | fieldset { 124 | margin: 0; 125 | padding: 0; 126 | } 127 | 128 | blockquote { 129 | font-size: 11px; 130 | color: #777; 131 | margin-left: 2px; 132 | padding-left: 10px; 133 | border-left: 5px solid #ddd; 134 | } 135 | 136 | code, pre { 137 | font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; 138 | background: inherit; 139 | color: #666; 140 | font-size: 11px; 141 | } 142 | 143 | pre.literal-block { 144 | margin: 10px; 145 | background: #eee; 146 | padding: 6px 8px; 147 | } 148 | 149 | code strong { 150 | color: #930; 151 | } 152 | 153 | hr { 154 | clear: both; 155 | color: #eee; 156 | background-color: #eee; 157 | height: 1px; 158 | border: none; 159 | margin: 0; 160 | padding: 0; 161 | font-size: 1px; 162 | line-height: 1px; 163 | } 164 | 165 | /* TEXT STYLES & MODIFIERS */ 166 | 167 | .small { 168 | font-size: 11px; 169 | } 170 | 171 | .tiny { 172 | font-size: 10px; 173 | } 174 | 175 | p.tiny { 176 | margin-top: -2px; 177 | } 178 | 179 | .mini { 180 | font-size: 9px; 181 | } 182 | 183 | p.mini { 184 | margin-top: -3px; 185 | } 186 | 187 | .help, p.help { 188 | font-size: 10px !important; 189 | color: #999; 190 | } 191 | 192 | img.help-tooltip { 193 | cursor: help; 194 | } 195 | 196 | p img, h1 img, h2 img, h3 img, h4 img, td img { 197 | vertical-align: middle; 198 | } 199 | 200 | .quiet, a.quiet:link, a.quiet:visited { 201 | color: #999 !important; 202 | font-weight: normal !important; 203 | } 204 | 205 | .quiet strong { 206 | font-weight: bold !important; 207 | } 208 | 209 | .float-right { 210 | float: right; 211 | } 212 | 213 | .float-left { 214 | float: left; 215 | } 216 | 217 | .clear { 218 | clear: both; 219 | } 220 | 221 | .align-left { 222 | text-align: left; 223 | } 224 | 225 | .align-right { 226 | text-align: right; 227 | } 228 | 229 | .example { 230 | margin: 10px 0; 231 | padding: 5px 10px; 232 | background: #efefef; 233 | } 234 | 235 | .nowrap { 236 | white-space: nowrap; 237 | } 238 | 239 | /* TABLES */ 240 | 241 | table { 242 | border-collapse: collapse; 243 | border-color: #ccc; 244 | } 245 | 246 | td, th { 247 | font-size: 11px; 248 | line-height: 13px; 249 | border-bottom: 1px solid #eee; 250 | vertical-align: top; 251 | padding: 5px; 252 | font-family: "Lucida Grande", Verdana, Arial, sans-serif; 253 | } 254 | 255 | th { 256 | text-align: left; 257 | font-size: 12px; 258 | font-weight: bold; 259 | } 260 | 261 | thead th, 262 | tfoot td { 263 | color: #666; 264 | padding: 2px 5px; 265 | font-size: 11px; 266 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 267 | border-left: 1px solid #ddd; 268 | border-bottom: 1px solid #ddd; 269 | } 270 | 271 | tfoot td { 272 | border-bottom: none; 273 | border-top: 1px solid #ddd; 274 | } 275 | 276 | thead th:first-child, 277 | tfoot td:first-child { 278 | border-left: none !important; 279 | } 280 | 281 | thead th.optional { 282 | font-weight: normal !important; 283 | } 284 | 285 | fieldset table { 286 | border-right: 1px solid #eee; 287 | } 288 | 289 | tr.row-label td { 290 | font-size: 9px; 291 | padding-top: 2px; 292 | padding-bottom: 0; 293 | border-bottom: none; 294 | color: #666; 295 | margin-top: -1px; 296 | } 297 | 298 | tr.alt { 299 | background: #f6f6f6; 300 | } 301 | 302 | .row1 { 303 | background: #EDF3FE; 304 | } 305 | 306 | .row2 { 307 | background: white; 308 | } 309 | 310 | /* SORTABLE TABLES */ 311 | 312 | thead th { 313 | padding: 2px 5px; 314 | line-height: normal; 315 | } 316 | 317 | thead th a:link, thead th a:visited { 318 | color: #666; 319 | } 320 | 321 | thead th.sorted { 322 | background: #c5c5c5 url(../img/nav-bg-selected.gif) top left repeat-x; 323 | } 324 | 325 | table thead th .text span { 326 | padding: 2px 5px; 327 | display:block; 328 | } 329 | 330 | table thead th .text a { 331 | display: block; 332 | cursor: pointer; 333 | padding: 2px 5px; 334 | } 335 | 336 | table thead th.sortable:hover { 337 | background: white url(../img/nav-bg-reverse.gif) 0 -5px repeat-x; 338 | } 339 | 340 | thead th.sorted a.sortremove { 341 | visibility: hidden; 342 | } 343 | 344 | table thead th.sorted:hover a.sortremove { 345 | visibility: visible; 346 | } 347 | 348 | table thead th.sorted .sortoptions { 349 | display: block; 350 | padding: 4px 5px 0 5px; 351 | float: right; 352 | text-align: right; 353 | } 354 | 355 | table thead th.sorted .sortpriority { 356 | font-size: .8em; 357 | min-width: 12px; 358 | text-align: center; 359 | vertical-align: top; 360 | } 361 | 362 | table thead th.sorted .sortoptions a { 363 | width: 14px; 364 | height: 12px; 365 | display: inline-block; 366 | } 367 | 368 | table thead th.sorted .sortoptions a.sortremove { 369 | background: url(../img/sorting-icons.gif) -4px -5px no-repeat; 370 | } 371 | 372 | table thead th.sorted .sortoptions a.sortremove:hover { 373 | background: url(../img/sorting-icons.gif) -4px -27px no-repeat; 374 | } 375 | 376 | table thead th.sorted .sortoptions a.ascending { 377 | background: url(../img/sorting-icons.gif) -5px -50px no-repeat; 378 | } 379 | 380 | table thead th.sorted .sortoptions a.ascending:hover { 381 | background: url(../img/sorting-icons.gif) -5px -72px no-repeat; 382 | } 383 | 384 | table thead th.sorted .sortoptions a.descending { 385 | background: url(../img/sorting-icons.gif) -5px -94px no-repeat; 386 | } 387 | 388 | table thead th.sorted .sortoptions a.descending:hover { 389 | background: url(../img/sorting-icons.gif) -5px -115px no-repeat; 390 | } 391 | 392 | /* ORDERABLE TABLES */ 393 | 394 | table.orderable tbody tr td:hover { 395 | cursor: move; 396 | } 397 | 398 | table.orderable tbody tr td:first-child { 399 | padding-left: 14px; 400 | background-image: url(../img/nav-bg-grabber.gif); 401 | background-repeat: repeat-y; 402 | } 403 | 404 | table.orderable-initalized .order-cell, body>tr>td.order-cell { 405 | display: none; 406 | } 407 | 408 | /* FORM DEFAULTS */ 409 | 410 | input, textarea, select, .form-row p { 411 | margin: 2px 0; 412 | padding: 2px 3px; 413 | vertical-align: middle; 414 | font-family: "Lucida Grande", Verdana, Arial, sans-serif; 415 | font-weight: normal; 416 | font-size: 11px; 417 | } 418 | 419 | textarea { 420 | vertical-align: top !important; 421 | } 422 | 423 | input[type=text], input[type=password], textarea, select, .vTextField { 424 | border: 1px solid #ccc; 425 | } 426 | 427 | /* FORM BUTTONS */ 428 | 429 | .button, input[type=submit], input[type=button], .submit-row input { 430 | background: white url(../img/nav-bg.gif) bottom repeat-x; 431 | padding: 3px 5px; 432 | color: black; 433 | border: 1px solid #bbb; 434 | border-color: #ddd #aaa #aaa #ddd; 435 | } 436 | 437 | .button:active, input[type=submit]:active, input[type=button]:active { 438 | background-image: url(../img/nav-bg-reverse.gif); 439 | background-position: top; 440 | } 441 | 442 | .button[disabled], input[type=submit][disabled], input[type=button][disabled] { 443 | background-image: url(../img/nav-bg.gif); 444 | background-position: bottom; 445 | opacity: 0.4; 446 | } 447 | 448 | .button.default, input[type=submit].default, .submit-row input.default { 449 | border: 2px solid #5b80b2; 450 | background: #7CA0C7 url(../img/default-bg.gif) bottom repeat-x; 451 | font-weight: bold; 452 | color: white; 453 | float: right; 454 | } 455 | 456 | .button.default:active, input[type=submit].default:active { 457 | background-image: url(../img/default-bg-reverse.gif); 458 | background-position: top; 459 | } 460 | 461 | .button[disabled].default, input[type=submit][disabled].default, input[type=button][disabled].default { 462 | background-image: url(../img/default-bg.gif); 463 | background-position: bottom; 464 | opacity: 0.4; 465 | } 466 | 467 | 468 | /* MODULES */ 469 | 470 | .module { 471 | border: 1px solid #ccc; 472 | margin-bottom: 5px; 473 | background: white; 474 | } 475 | 476 | .module p, .module ul, .module h3, .module h4, .module dl, .module pre { 477 | padding-left: 10px; 478 | padding-right: 10px; 479 | } 480 | 481 | .module blockquote { 482 | margin-left: 12px; 483 | } 484 | 485 | .module ul, .module ol { 486 | margin-left: 1.5em; 487 | } 488 | 489 | .module h3 { 490 | margin-top: .6em; 491 | } 492 | 493 | .module h2, .module caption, .inline-group h2 { 494 | margin: 0; 495 | padding: 2px 5px 3px 5px; 496 | font-size: 11px; 497 | text-align: left; 498 | font-weight: bold; 499 | background: #7CA0C7 url(../img/default-bg.gif) top left repeat-x; 500 | color: white; 501 | } 502 | 503 | .module table { 504 | border-collapse: collapse; 505 | } 506 | 507 | /* MESSAGES & ERRORS */ 508 | 509 | ul.messagelist { 510 | padding: 0 0 5px 0; 511 | margin: 0; 512 | } 513 | 514 | ul.messagelist li { 515 | font-size: 12px; 516 | display: block; 517 | padding: 4px 5px 4px 25px; 518 | margin: 0 0 3px 0; 519 | border-bottom: 1px solid #ddd; 520 | color: #666; 521 | background: #ffc url(../img/icon_success.gif) 5px .3em no-repeat; 522 | } 523 | 524 | ul.messagelist li.warning{ 525 | background-image: url(../img/icon_alert.gif); 526 | } 527 | 528 | ul.messagelist li.error{ 529 | background-image: url(../img/icon_error.gif); 530 | } 531 | 532 | .errornote { 533 | font-size: 12px !important; 534 | display: block; 535 | padding: 4px 5px 4px 25px; 536 | margin: 0 0 3px 0; 537 | border: 1px solid red; 538 | color: red; 539 | background: #ffc url(../img/icon_error.gif) 5px .3em no-repeat; 540 | } 541 | 542 | ul.errorlist { 543 | margin: 0 !important; 544 | padding: 0 !important; 545 | } 546 | 547 | .errorlist li { 548 | font-size: 12px !important; 549 | display: block; 550 | padding: 4px 5px 4px 25px; 551 | margin: 0 0 3px 0; 552 | border: 1px solid red; 553 | color: white; 554 | background: red url(../img/icon_alert.gif) 5px .3em no-repeat; 555 | } 556 | 557 | .errorlist li a { 558 | color: white; 559 | text-decoration: underline; 560 | } 561 | 562 | td ul.errorlist { 563 | margin: 0 !important; 564 | padding: 0 !important; 565 | } 566 | 567 | td ul.errorlist li { 568 | margin: 0 !important; 569 | } 570 | 571 | .errors { 572 | background: #ffc; 573 | } 574 | 575 | .errors input, .errors select, .errors textarea { 576 | border: 1px solid red; 577 | } 578 | 579 | div.system-message { 580 | background: #ffc; 581 | margin: 10px; 582 | padding: 6px 8px; 583 | font-size: .8em; 584 | } 585 | 586 | div.system-message p.system-message-title { 587 | padding: 4px 5px 4px 25px; 588 | margin: 0; 589 | color: red; 590 | background: #ffc url(../img/icon_error.gif) 5px .3em no-repeat; 591 | } 592 | 593 | .description { 594 | font-size: 12px; 595 | padding: 5px 0 0 12px; 596 | } 597 | 598 | /* BREADCRUMBS */ 599 | 600 | div.breadcrumbs { 601 | background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x; 602 | padding: 2px 8px 3px 8px; 603 | font-size: 11px; 604 | color: #999; 605 | border-top: 1px solid white; 606 | border-bottom: 1px solid #ccc; 607 | text-align: left; 608 | } 609 | 610 | /* ACTION ICONS */ 611 | 612 | .addlink { 613 | padding-left: 12px; 614 | background: url(../img/icon_addlink.gif) 0 .2em no-repeat; 615 | } 616 | 617 | .changelink { 618 | padding-left: 12px; 619 | background: url(../img/icon_changelink.gif) 0 .2em no-repeat; 620 | } 621 | 622 | .deletelink { 623 | padding-left: 12px; 624 | background: url(../img/icon_deletelink.gif) 0 .25em no-repeat; 625 | } 626 | 627 | a.deletelink:link, a.deletelink:visited { 628 | color: #CC3434; 629 | } 630 | 631 | a.deletelink:hover { 632 | color: #993333; 633 | } 634 | 635 | /* OBJECT TOOLS */ 636 | 637 | .object-tools { 638 | font-size: 10px; 639 | font-weight: bold; 640 | font-family: Arial,Helvetica,sans-serif; 641 | padding-left: 0; 642 | float: right; 643 | position: relative; 644 | margin-top: -2.4em; 645 | margin-bottom: -2em; 646 | } 647 | 648 | .form-row .object-tools { 649 | margin-top: 5px; 650 | margin-bottom: 5px; 651 | float: none; 652 | height: 2em; 653 | padding-left: 3.5em; 654 | } 655 | 656 | .object-tools li { 657 | display: block; 658 | float: left; 659 | background: url(../img/tool-left.gif) 0 0 no-repeat; 660 | padding: 0 0 0 8px; 661 | margin-left: 2px; 662 | height: 16px; 663 | } 664 | 665 | .object-tools li:hover { 666 | background: url(../img/tool-left_over.gif) 0 0 no-repeat; 667 | } 668 | 669 | .object-tools a:link, .object-tools a:visited { 670 | display: block; 671 | float: left; 672 | color: white; 673 | padding: .1em 14px .1em 8px; 674 | height: 14px; 675 | background: #999 url(../img/tool-right.gif) 100% 0 no-repeat; 676 | } 677 | 678 | .object-tools a:hover, .object-tools li:hover a { 679 | background: #5b80b2 url(../img/tool-right_over.gif) 100% 0 no-repeat; 680 | } 681 | 682 | .object-tools a.viewsitelink, .object-tools a.golink { 683 | background: #999 url(../img/tooltag-arrowright.gif) top right no-repeat; 684 | padding-right: 28px; 685 | } 686 | 687 | .object-tools a.viewsitelink:hover, .object-tools a.golink:hover { 688 | background: #5b80b2 url(../img/tooltag-arrowright_over.gif) top right no-repeat; 689 | } 690 | 691 | .object-tools a.addlink { 692 | background: #999 url(../img/tooltag-add.gif) top right no-repeat; 693 | padding-right: 28px; 694 | } 695 | 696 | .object-tools a.addlink:hover { 697 | background: #5b80b2 url(../img/tooltag-add_over.gif) top right no-repeat; 698 | } 699 | 700 | /* OBJECT HISTORY */ 701 | 702 | table#change-history { 703 | width: 100%; 704 | } 705 | 706 | table#change-history tbody th { 707 | width: 16em; 708 | } 709 | 710 | /* PAGE STRUCTURE */ 711 | 712 | #container { 713 | position: relative; 714 | width: 100%; 715 | min-width: 760px; 716 | padding: 0; 717 | } 718 | 719 | #content { 720 | margin: 10px 15px; 721 | } 722 | 723 | #header { 724 | width: 100%; 725 | } 726 | 727 | #content-main { 728 | float: left; 729 | width: 100%; 730 | } 731 | 732 | #content-related { 733 | float: right; 734 | width: 18em; 735 | position: relative; 736 | margin-right: -19em; 737 | } 738 | 739 | #footer { 740 | clear: both; 741 | padding: 10px; 742 | } 743 | 744 | /* COLUMN TYPES */ 745 | 746 | .colMS { 747 | margin-right: 20em !important; 748 | } 749 | 750 | .colSM { 751 | margin-left: 20em !important; 752 | } 753 | 754 | .colSM #content-related { 755 | float: left; 756 | margin-right: 0; 757 | margin-left: -19em; 758 | } 759 | 760 | .colSM #content-main { 761 | float: right; 762 | } 763 | 764 | .popup .colM { 765 | width: 95%; 766 | } 767 | 768 | .subcol { 769 | float: left; 770 | width: 46%; 771 | margin-right: 15px; 772 | } 773 | 774 | .dashboard #content { 775 | width: 500px; 776 | } 777 | 778 | /* HEADER */ 779 | 780 | #header { 781 | background: #417690; 782 | color: #ffc; 783 | overflow: hidden; 784 | } 785 | 786 | #header a:link, #header a:visited { 787 | color: white; 788 | } 789 | 790 | #header a:hover { 791 | text-decoration: underline; 792 | } 793 | 794 | #branding h1 { 795 | padding: 0 10px; 796 | font-size: 18px; 797 | margin: 8px 0; 798 | font-weight: normal; 799 | color: #f4f379; 800 | } 801 | 802 | #branding h2 { 803 | padding: 0 10px; 804 | font-size: 14px; 805 | margin: -8px 0 8px 0; 806 | font-weight: normal; 807 | color: #ffc; 808 | } 809 | 810 | #user-tools { 811 | position: absolute; 812 | top: 0; 813 | right: 0; 814 | padding: 1.2em 10px; 815 | font-size: 11px; 816 | text-align: right; 817 | } 818 | 819 | /* SIDEBAR */ 820 | 821 | #content-related h3 { 822 | font-size: 12px; 823 | color: #666; 824 | margin-bottom: 3px; 825 | } 826 | 827 | #content-related h4 { 828 | font-size: 11px; 829 | } 830 | 831 | #content-related .module h2 { 832 | background: #eee url(../img/nav-bg.gif) bottom left repeat-x; 833 | color: #666; 834 | } 835 | 836 | --------------------------------------------------------------------------------