├── acra ├── __init__.py ├── urls.py ├── decorators.py ├── templates │ ├── timeline.html │ └── dashboard.html ├── admin.py ├── models.py ├── views.py └── static │ └── css │ └── nv.d3.css ├── acra_django_server ├── __init__.py └── urls.py ├── Screenshot.png ├── 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 │ │ ├── timeparse.js │ │ ├── actions.min.js │ │ ├── admin │ │ │ ├── RelatedObjectLookups.js │ │ │ └── DateTimeShortcuts.js │ │ ├── inlines.min.js │ │ ├── SelectBox.js │ │ ├── actions.js │ │ ├── calendar.js │ │ ├── urlify.js │ │ ├── core.js │ │ ├── SelectFilter2.js │ │ └── inlines.js │ └── css │ │ ├── dashboard.css │ │ ├── login.css │ │ ├── ie.css │ │ ├── rtl.css │ │ ├── changelists.css │ │ ├── forms.css │ │ ├── widgets.css │ │ └── base.css ├── css │ └── nv.d3.css └── js │ └── lineWithFocusChart.js ├── .gitignore └── README.md /acra/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /acra_django_server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/Screenshot.png -------------------------------------------------------------------------------- /static/admin/img/icon-no.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon-no.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/nav-bg.gif -------------------------------------------------------------------------------- /static/admin/img/icon-yes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon-yes.gif -------------------------------------------------------------------------------- /static/admin/img/tool-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tool-left.gif -------------------------------------------------------------------------------- /static/admin/img/chooser-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/chooser-bg.gif -------------------------------------------------------------------------------- /static/admin/img/default-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/default-bg.gif -------------------------------------------------------------------------------- /static/admin/img/icon-unknown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon-unknown.gif -------------------------------------------------------------------------------- /static/admin/img/icon_addlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_addlink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_alert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_alert.gif -------------------------------------------------------------------------------- /static/admin/img/icon_clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_clock.gif -------------------------------------------------------------------------------- /static/admin/img/icon_error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_error.gif -------------------------------------------------------------------------------- /static/admin/img/icon_success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_success.gif -------------------------------------------------------------------------------- /static/admin/img/tool-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tool-right.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tooltag-add.gif -------------------------------------------------------------------------------- /static/admin/img/changelist-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/changelist-bg.gif -------------------------------------------------------------------------------- /static/admin/img/icon_calendar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_calendar.gif -------------------------------------------------------------------------------- /static/admin/img/icon_searchbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_searchbox.png -------------------------------------------------------------------------------- /static/admin/img/inline-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/inline-delete.png -------------------------------------------------------------------------------- /static/admin/img/inline-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/inline-restore.png -------------------------------------------------------------------------------- /static/admin/img/nav-bg-grabber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/nav-bg-grabber.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/nav-bg-reverse.gif -------------------------------------------------------------------------------- /static/admin/img/selector-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/selector-icons.gif -------------------------------------------------------------------------------- /static/admin/img/sorting-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/sorting-icons.gif -------------------------------------------------------------------------------- /static/admin/img/tool-left_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tool-left_over.gif -------------------------------------------------------------------------------- /static/admin/img/changelist-bg_rtl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/changelist-bg_rtl.gif -------------------------------------------------------------------------------- /static/admin/img/deleted-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/deleted-overlay.gif -------------------------------------------------------------------------------- /static/admin/img/icon_changelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_changelink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_deletelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/icon_deletelink.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-selected.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/nav-bg-selected.gif -------------------------------------------------------------------------------- /static/admin/img/selector-search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/selector-search.gif -------------------------------------------------------------------------------- /static/admin/img/tool-right_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tool-right_over.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-add_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tooltag-add_over.gif -------------------------------------------------------------------------------- /static/admin/img/chooser_stacked-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/chooser_stacked-bg.gif -------------------------------------------------------------------------------- /static/admin/img/default-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/default-bg-reverse.gif -------------------------------------------------------------------------------- /static/admin/img/gis/move_vertex_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/gis/move_vertex_off.png -------------------------------------------------------------------------------- /static/admin/img/gis/move_vertex_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/gis/move_vertex_on.png -------------------------------------------------------------------------------- /static/admin/img/inline-delete-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/inline-delete-8bit.png -------------------------------------------------------------------------------- /static/admin/img/inline-restore-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/inline-restore-8bit.png -------------------------------------------------------------------------------- /static/admin/img/inline-splitter-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/inline-splitter-bg.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-arrowright.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tooltag-arrowright.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-arrowright_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simpsonpt/django-acra/HEAD/static/admin/img/tooltag-arrowright_over.gif -------------------------------------------------------------------------------- /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 = django || {}; 7 | django.jQuery = jQuery.noConflict(true); 8 | -------------------------------------------------------------------------------- /static/admin/js/prepopulate.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.prepopulate=function(d,g){return this.each(function(){var b=a(this);b.data("_changed",false);b.change(function(){b.data("_changed",true)});var c=function(){if(b.data("_changed")!=true){var e=[];a.each(d,function(h,f){a(f).val().length>0&&e.push(a(f).val())});b.val(URLify(e.join(" "),g))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery); 2 | -------------------------------------------------------------------------------- /acra/urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #encoding: utf-8 3 | from django.conf.urls import patterns, include, url 4 | 5 | urlpatterns = patterns('', 6 | #url(r'^$', 'acra.views.dashboard', name='dashboard'), 7 | url(r'dashboard/', 'acra.views.dashboard', name='dashboard'), 8 | url(r'timeline/', 'acra.views.timeline', name='timeline'), 9 | url(r'_design/acra-storage/_update/report/', 'acra.views.index', name='submit'), 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | settings.py 37 | -------------------------------------------------------------------------------- /static/admin/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* DASHBOARD */ 2 | 3 | .dashboard .module table th { 4 | width: 100%; 5 | } 6 | 7 | .dashboard .module table td { 8 | white-space: nowrap; 9 | } 10 | 11 | .dashboard .module table td a { 12 | display: block; 13 | padding-right: .6em; 14 | } 15 | 16 | /* RECENT ACTIONS MODULE */ 17 | 18 | .module ul.actionlist { 19 | margin-left: 0; 20 | } 21 | 22 | ul.actionlist li { 23 | list-style-type: none; 24 | } 25 | 26 | ul.actionlist li { 27 | overflow: hidden; 28 | text-overflow: ellipsis; 29 | -o-text-overflow: ellipsis; 30 | } 31 | -------------------------------------------------------------------------------- /static/admin/js/collapse.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){0==a(b).find("div.errors").length&&a(b).addClass("collapsed").find("h2").first().append(' ('+gettext("Show")+")")});a("fieldset.collapse a.collapse-toggle").click(function(){a(this).closest("fieldset").hasClass("collapsed")?a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]):a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", 2 | [a(this).attr("id")]);return!1})})})(django.jQuery); 3 | -------------------------------------------------------------------------------- /acra_django_server/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | urlpatterns = patterns('', 7 | # Examples: 8 | # url(r'^$', 'acra_django_server.views.home', name='home'), 9 | # url(r'^blog/', include('blog.urls')), 10 | 11 | url(r'^admin/', include(admin.site.urls)), 12 | url(r'^dashboard/', 'acra.views.dashboard', name='dashboard'), 13 | 14 | url(r'^timeline/', 'acra.views.timeline', name='timeline'), 15 | url(r'^acra/', include("acra.urls")), 16 | url(r'^acra-vivintsky/', include("acra.urls")), 17 | url(r'^accounts/login/$', 'django.contrib.auth.views.login'), 18 | 19 | url(r'^$', 'acra.views.dashboard', name='dashboard'), 20 | 21 | ) 22 | -------------------------------------------------------------------------------- /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").click(function(ev) { 14 | if ($(this).closest("fieldset").hasClass("collapsed")) { 15 | // Show 16 | $(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]); 17 | } else { 18 | // Hide 19 | $(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]); 20 | } 21 | return false; 22 | }); 23 | }); 24 | })(django.jQuery); 25 | -------------------------------------------------------------------------------- /acra/decorators.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from functools import wraps 3 | 4 | def http_basic_auth(func): 5 | @wraps(func) 6 | def _decorator(request, *args, **kwargs): 7 | from django.contrib.auth import authenticate, login 8 | if request.META.has_key('HTTP_AUTHORIZATION'): 9 | authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1) 10 | if authmeth.lower() == 'basic': 11 | auth = auth.strip().decode('base64') 12 | username, password = auth.split(':', 1) 13 | user = authenticate(username=username, password=password) 14 | if user is not None: 15 | if user.has_perm('acra.add_crashreport') and user.has_perm('acra.change_crashreport') and user.has_perm('acra.delete_crashreport'): 16 | login(request, user) 17 | return func(request, *args, **kwargs) 18 | response = HttpResponse() 19 | response.status_code = 401 20 | return response 21 | return _decorator 22 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | padding-right: 0.5em; 33 | line-height: 2em; 34 | font-size: 1em; 35 | clear: both; 36 | color: #333; 37 | } 38 | 39 | .login .form-row #id_username, .login .form-row #id_password { 40 | clear: both; 41 | padding: 6px; 42 | width: 100%; 43 | -webkit-box-sizing: border-box; 44 | -moz-box-sizing: border-box; 45 | box-sizing: border-box; 46 | } 47 | 48 | .login span.help { 49 | font-size: 10px; 50 | display: block; 51 | } 52 | 53 | .login .submit-row { 54 | clear: both; 55 | padding: 1em 0 0 9.4em; 56 | } 57 | 58 | .login .password-reset-link { 59 | text-align: center; 60 | } 61 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /acra/templates/timeline.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 17 | 18 | 19 | 20 | 56 | 57 | 58 | 59 | 60 | 61 | dashboard 62 |
63 | 64 |
65 |

Timeline

66 | 67 |
68 | 69 | 70 | 71 | %s" % (key.lower(),json_data[key]) ) 45 | v = getattr(crashreport,key.lower(),None) 46 | if(v!=None): 47 | setattr(crashreport,key.lower(),json_data[key]) 48 | crashreport.save() 49 | 50 | return HttpResponse(json.dumps({"ok":"true"}), content_type="application/json") 51 | 52 | 53 | @csrf_exempt 54 | @login_required 55 | def dashboard(request): 56 | android_versions = CrashReport.objects.filter().order_by("android_version").values("android_version").distinct(); 57 | version_count = CrashReport.objects.filter().values('android_version').annotate(count=Count('pk')).order_by("android_version") 58 | app_version_count = CrashReport.objects.filter().values('app_version_name').annotate(count=Count('pk')).order_by("app_version_name") 59 | brand_count = CrashReport.objects.filter().values('brand').annotate(count=Count('pk')).order_by("brand") 60 | #log.log(logging.DEBUG, "query: "+str(app_version_count.query)) 61 | 62 | return render(request, 'dashboard.html', {'android_verions': android_versions, 63 | 'versions':version_count, 64 | 'app_version':app_version_count, 65 | 'brand':brand_count}) 66 | 67 | 68 | @csrf_exempt 69 | @login_required 70 | def timeline(request): 71 | 72 | query = """ 73 | select count(id) as count, date_format(created, '%Y-%m-%dT%H:00:00.0000') as datef from acra_crashreport 74 | group by datef 75 | order by datef 76 | """ 77 | cursor = connection.cursor() 78 | cursor.execute(query) 79 | data = cursor.fetchall(); 80 | 81 | 82 | return render(request, 'timeline.html', {"time_data": data}) 83 | -------------------------------------------------------------------------------- /static/admin/js/actions.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.actions=function(n){var b=a.extend({},a.fn.actions.defaults,n),e=a(this),g=false,k=function(c){c?i():j();a(e).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},f=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).prop("checked",function(){if(c==e.length){value=true;i()}else{value=false;l()}return value})},i= 2 | function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},j=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},l=function(){j();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show(); 3 | a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);f();a(b.acrossInput).val()==1&&m()});a(b.allToggle).show().click(function(){k(a(this).prop("checked"));f()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).prop("checked",false);l();k(0);f()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target? 4 | c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey===true){var h=false;a(lastChecked).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))h=h?false:true;h&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;f()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){g= 5 | true});a('form#changelist-form button[name="index"]').click(function(){if(g)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c=true});if(c)return g?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.")): 6 | 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."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery); 7 | -------------------------------------------------------------------------------- /acra/templates/dashboard.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 13 | 14 | 15 | 94 | 95 | 96 | 97 | 98 | 99 | Timeline 100 |
101 | 109 | 110 |
111 |

Android Version

112 | 113 |
114 |
115 |

App Version

116 | 117 |
118 |
119 |

Brand

120 | 121 |
122 | 123 | 124 | '); 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 + '&_popup=1'; 36 | } else { 37 | href = triggeringLink.href + '?_popup=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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Django-ACRA 2 | ####Ready To Go Helper! 3 | 4 | Django Model, View and Admin Panel for ACRA (Application Crash Reports for Android). 5 | 6 | 7 | ##Installation 8 | 9 | Assuming that the project name is **my_android_server** copy the folder **acra** to the project and then edit **settings.py** and **urls.py**. 10 | 11 | 12 | 13 | - In the **settings.py** file just add: 14 | 15 | ``` 16 | INSTALLED_APPS = ( 17 | 'django.contrib.auth', 18 | 'django.contrib.contenttypes', 19 | 'django.contrib.sessions', 20 | '...' 21 | 'my_android_server.acra', 22 | ) 23 | ``` 24 | 25 | - In the **urls.py** make sure to include this: 26 | 27 | ``` 28 | from django.conf.urls import patterns, include, url 29 | from django.contrib import admin 30 | admin.autodiscover() 31 | import settings 32 | 33 | urlpatterns = patterns('', 34 | ... 35 | url(r'^acra/', include("my_android_server.acra.urls")), 36 | url(r'^admin/', include(admin.site.urls)), 37 | ... 38 | ) 39 | ``` 40 | 41 | - Finally **sync** the database 42 | 43 | ``` 44 | $ python manage.py syncdb 45 | ``` 46 | 47 | - Create a Django user in your admin page (to get a `username` and `password`) and set the acra permissions(`'acra.add_crashreport'`, `'acra.change_crashreport'` and `'acra.delete_crashreport'`) to this new user. 48 | 49 | ##Android Snippet 50 | 51 | - Application Class 52 | 53 | ``` 54 | package com.pathonproject.testacra; 55 | 56 | import org.acra.ACRA; 57 | import org.acra.annotation.ReportsCrashes; 58 | 59 | import android.app.Application; 60 | 61 | @ReportsCrashes( 62 | formUri = "http://pathonproject.com/acra/_design/acra-storage/_update/report/"), 63 | reportType = HttpSender.Type.JSON, 64 | formUriBasicAuthLogin = "", 65 | formUriBasicAuthPassword = "" 66 | ) 67 | public class TA extends Application 68 | { 69 | 70 | public void onCreate() 71 | { 72 | super.onCreate(); 73 | ACRA.init(this); 74 | 75 | //ACRA.getErrorReporter().handleException(null); 76 | ACRA.getErrorReporter().checkReportsOnApplicationStart(); 77 | //ACRA.getErrorReporter().handleException(null); 78 | } 79 | } 80 | ``` 81 | 82 | - AndroidManifest.xml 83 | 84 | ``` 85 | 86 | 87 | 88 | 94 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | ``` 105 | 106 | ##Screenshot 107 | 108 | ![Screenshot](https://raw.github.com/Simpsonpt/django-acra/master/Screenshot.png) 109 | 110 | ##Version 111 | 112 | * [Django ACRA v1.0] 113 | 114 | ##Tech 115 | 116 | * [ACRA] - Application Crash Reports for Android 117 | * [License] - Module License 118 | 119 | ##Author 120 | 121 | * [@Simps0n] - Twitter 122 | * [Pathon Project Playground] - Webpage 123 | 124 | [ACRA]: https://github.com/ACRA/acra 125 | [License]: https://github.com/Simpsonpt/django-acra/blob/master/LICENSE 126 | [Django ACRA v1.0]: https://github.com/Simpsonpt/django-acra/releases/tag/django-acra-1.0 127 | [@Simps0n]: https://twitter.com/simps0n 128 | [Pathon Project Playground]: http://www.pathonproject.com/ 129 | -------------------------------------------------------------------------------- /static/admin/js/inlines.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.formset=function(d){var a=b.extend({},b.fn.formset.defaults,d),c=b(this),d=c.parent(),i=function(a,e,g){var d=RegExp("("+e+"-(\\d+|__prefix__))"),e=e+"-"+g;b(a).prop("for")&&b(a).prop("for",b(a).prop("for").replace(d,e));a.id&&(a.id=a.id.replace(d,e));a.name&&(a.name=a.name.replace(d,e))},f=b("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),g=parseInt(f.val(),10),e=b("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),f=""===e.val()||0'+a.addText+""),h=d.find("tr:last a")):(c.filter(":last").after('"),h=c.filter(":last").next().find("a"));h.click(function(d){d.preventDefault();var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),d=b("#"+a.prefix+ 3 | "-empty"),c=d.clone(true);c.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+g);c.is("tr")?c.children(":last").append('
'+a.deleteText+"
"):c.is("ul")||c.is("ol")?c.append('
  • '+a.deleteText+"
  • "):c.children(":first").append(''+a.deleteText+"");c.find("*").each(function(){i(this, 4 | a.prefix,f.val())});c.insertBefore(b(d));b(f).val(parseInt(f.val(),10)+1);g=g+1;e.val()!==""&&e.val()-f.val()<=0&&h.parent().hide();c.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();g=g-1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(e.val()===""||e.val()-d.length>0)&&h.parent().show();for(var c=0,f=d.length;c 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 | thead th.sorted .text { 88 | padding-right: 0; 89 | padding-left: 42px; 90 | } 91 | 92 | /* dashboard styles */ 93 | 94 | .dashboard .module table td a { 95 | padding-left: .6em; 96 | padding-right: 12px; 97 | } 98 | 99 | /* changelists styles */ 100 | 101 | .change-list .filtered { 102 | background: white url(../img/changelist-bg_rtl.gif) top left repeat-y !important; 103 | } 104 | 105 | .change-list .filtered table { 106 | border-left: 1px solid #ddd; 107 | border-right: 0px none; 108 | } 109 | 110 | #changelist-filter { 111 | right: auto; 112 | left: 0; 113 | border-left: 0px none; 114 | border-right: 1px solid #ddd; 115 | } 116 | 117 | .change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 118 | margin-right: 0px !important; 119 | margin-left: 160px !important; 120 | } 121 | 122 | #changelist-filter li.selected { 123 | border-left: 0px none; 124 | padding-left: 0px; 125 | margin-left: 0; 126 | border-right: 5px solid #ccc; 127 | padding-right: 5px; 128 | margin-right: -10px; 129 | } 130 | 131 | .filtered .actions { 132 | border-left:1px solid #DDDDDD; 133 | margin-left:160px !important; 134 | border-right: 0 none; 135 | margin-right:0 !important; 136 | } 137 | 138 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 139 | border-right: 0; 140 | border-left: 1px solid #ddd; 141 | } 142 | 143 | /* FORMS */ 144 | 145 | .aligned label { 146 | padding: 0 0 3px 1em; 147 | float: right; 148 | } 149 | 150 | .submit-row { 151 | text-align: left 152 | } 153 | 154 | .submit-row p.deletelink-box { 155 | float: right; 156 | } 157 | 158 | .submit-row .deletelink { 159 | background: url(../img/icon_deletelink.gif) 0 50% no-repeat; 160 | padding-right: 14px; 161 | } 162 | 163 | .vDateField, .vTimeField { 164 | margin-left: 2px; 165 | } 166 | 167 | form ul.inline li { 168 | float: right; 169 | padding-right: 0; 170 | padding-left: 7px; 171 | } 172 | 173 | input[type=submit].default, .submit-row input.default { 174 | float: left; 175 | } 176 | 177 | fieldset .field-box { 178 | float: right; 179 | margin-left: 20px; 180 | margin-right: 0; 181 | } 182 | 183 | .errorlist li { 184 | background-position: 100% .3em; 185 | padding: 4px 25px 4px 5px; 186 | } 187 | 188 | .errornote { 189 | background-position: 100% .3em; 190 | padding: 4px 25px 4px 5px; 191 | } 192 | 193 | /* WIDGETS */ 194 | 195 | .calendarnav-previous { 196 | top: 0; 197 | left: auto; 198 | right: 0; 199 | } 200 | 201 | .calendarnav-next { 202 | top: 0; 203 | right: auto; 204 | left: 0; 205 | } 206 | 207 | .calendar caption, .calendarbox h2 { 208 | text-align: center; 209 | } 210 | 211 | .selector { 212 | float: right; 213 | } 214 | 215 | .selector .selector-filter { 216 | text-align: right; 217 | } 218 | 219 | .inline-deletelink { 220 | float: left; 221 | } 222 | 223 | /* MISC */ 224 | 225 | .inline-related h2, .inline-group h2 { 226 | text-align: right 227 | } 228 | 229 | .inline-related h3 span.delete { 230 | padding-right: 20px; 231 | padding-left: inherit; 232 | left: 10px; 233 | right: inherit; 234 | float:left; 235 | } 236 | 237 | .inline-related h3 span.delete label { 238 | margin-left: inherit; 239 | margin-right: 2px; 240 | } 241 | 242 | /* IE7 specific bug fixes */ 243 | 244 | div.colM { 245 | position: relative; 246 | } 247 | 248 | .submit-row input { 249 | float: left; 250 | } -------------------------------------------------------------------------------- /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 | var checker = function(checked) { 7 | if (checked) { 8 | showQuestion(); 9 | } else { 10 | reset(); 11 | } 12 | $(actionCheckboxes).prop("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).prop("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).prop("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).prop("checked", false); 78 | clearAcross(); 79 | checker(0); 80 | updateCounter(); 81 | }); 82 | lastChecked = null; 83 | $(actionCheckboxes).click(function(event) { 84 | if (!event) { 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).prop("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).prop("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/calendar.js: -------------------------------------------------------------------------------- 1 | /* 2 | calendar.js - Calendar functions by Adrian Holovaty 3 | depends on core.js for utility functions like removeChildren or quickElement 4 | */ 5 | 6 | // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions 7 | var CalendarNamespace = { 8 | monthsOfYear: gettext('January February March April May June July August September October November December').split(' '), 9 | daysOfWeek: gettext('S M T W T F S').split(' '), 10 | firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')), 11 | isLeapYear: function(year) { 12 | return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0)); 13 | }, 14 | getDaysInMonth: function(month,year) { 15 | var days; 16 | if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { 17 | days = 31; 18 | } 19 | else if (month==4 || month==6 || month==9 || month==11) { 20 | days = 30; 21 | } 22 | else if (month==2 && CalendarNamespace.isLeapYear(year)) { 23 | days = 29; 24 | } 25 | else { 26 | days = 28; 27 | } 28 | return days; 29 | }, 30 | draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999 31 | var today = new Date(); 32 | var todayDay = today.getDate(); 33 | var todayMonth = today.getMonth()+1; 34 | var todayYear = today.getFullYear(); 35 | var todayClass = ''; 36 | 37 | month = parseInt(month); 38 | year = parseInt(year); 39 | var calDiv = document.getElementById(div_id); 40 | removeChildren(calDiv); 41 | var calTable = document.createElement('table'); 42 | quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year); 43 | var tableBody = quickElement('tbody', calTable); 44 | 45 | // Draw days-of-week header 46 | var tableRow = quickElement('tr', tableBody); 47 | for (var i = 0; i < 7; i++) { 48 | quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]); 49 | } 50 | 51 | var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay(); 52 | var days = CalendarNamespace.getDaysInMonth(month, year); 53 | 54 | // Draw blanks before first of month 55 | tableRow = quickElement('tr', tableBody); 56 | for (var i = 0; i < startingPos; i++) { 57 | var _cell = quickElement('td', tableRow, ' '); 58 | _cell.style.backgroundColor = '#f3f3f3'; 59 | } 60 | 61 | // Draw days of month 62 | var currentDay = 1; 63 | for (var i = startingPos; currentDay <= days; i++) { 64 | if (i%7 == 0 && currentDay != 1) { 65 | tableRow = quickElement('tr', tableBody); 66 | } 67 | if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) { 68 | todayClass='today'; 69 | } else { 70 | todayClass=''; 71 | } 72 | var cell = quickElement('td', tableRow, '', 'class', todayClass); 73 | 74 | quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));'); 75 | currentDay++; 76 | } 77 | 78 | // Draw blanks after end of month (optional, but makes for valid code) 79 | while (tableRow.childNodes.length < 7) { 80 | var _cell = quickElement('td', tableRow, ' '); 81 | _cell.style.backgroundColor = '#f3f3f3'; 82 | } 83 | 84 | calDiv.appendChild(calTable); 85 | } 86 | } 87 | 88 | // Calendar -- A calendar instance 89 | function Calendar(div_id, callback) { 90 | // div_id (string) is the ID of the element in which the calendar will 91 | // be displayed 92 | // callback (string) is the name of a JavaScript function that will be 93 | // called with the parameters (year, month, day) when a day in the 94 | // calendar is clicked 95 | this.div_id = div_id; 96 | this.callback = callback; 97 | this.today = new Date(); 98 | this.currentMonth = this.today.getMonth() + 1; 99 | this.currentYear = this.today.getFullYear(); 100 | } 101 | Calendar.prototype = { 102 | drawCurrent: function() { 103 | CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback); 104 | }, 105 | drawDate: function(month, year) { 106 | this.currentMonth = month; 107 | this.currentYear = year; 108 | this.drawCurrent(); 109 | }, 110 | drawPreviousMonth: function() { 111 | if (this.currentMonth == 1) { 112 | this.currentMonth = 12; 113 | this.currentYear--; 114 | } 115 | else { 116 | this.currentMonth--; 117 | } 118 | this.drawCurrent(); 119 | }, 120 | drawNextMonth: function() { 121 | if (this.currentMonth == 12) { 122 | this.currentMonth = 1; 123 | this.currentYear++; 124 | } 125 | else { 126 | this.currentMonth++; 127 | } 128 | this.drawCurrent(); 129 | }, 130 | drawPreviousYear: function() { 131 | this.currentYear--; 132 | this.drawCurrent(); 133 | }, 134 | drawNextYear: function() { 135 | this.currentYear++; 136 | this.drawCurrent(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /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/css/changelists.css: -------------------------------------------------------------------------------- 1 | /* CHANGELISTS */ 2 | 3 | #changelist { 4 | position: relative; 5 | width: 100%; 6 | } 7 | 8 | #changelist table { 9 | width: 100%; 10 | } 11 | 12 | .change-list .hiddenfields { display:none; } 13 | 14 | .change-list .filtered table { 15 | border-right: 1px solid #ddd; 16 | } 17 | 18 | .change-list .filtered { 19 | min-height: 400px; 20 | } 21 | 22 | .change-list .filtered { 23 | background: white url(../img/changelist-bg.gif) top right repeat-y !important; 24 | } 25 | 26 | .change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 27 | margin-right: 160px !important; 28 | width: auto !important; 29 | } 30 | 31 | .change-list .filtered table tbody th { 32 | padding-right: 1em; 33 | } 34 | 35 | #changelist-form .results { 36 | overflow-x: auto; 37 | } 38 | 39 | #changelist .toplinks { 40 | border-bottom: 1px solid #ccc !important; 41 | } 42 | 43 | #changelist .paginator { 44 | color: #666; 45 | border-top: 1px solid #eee; 46 | border-bottom: 1px solid #eee; 47 | background: white url(../img/nav-bg.gif) 0 180% repeat-x; 48 | overflow: hidden; 49 | } 50 | 51 | .change-list .filtered .paginator { 52 | border-right: 1px solid #ddd; 53 | } 54 | 55 | /* CHANGELIST TABLES */ 56 | 57 | #changelist table thead th { 58 | padding: 0; 59 | white-space: nowrap; 60 | vertical-align: middle; 61 | } 62 | 63 | #changelist table thead th.action-checkbox-column { 64 | width: 1.5em; 65 | text-align: center; 66 | } 67 | 68 | #changelist table tbody td, #changelist table tbody th { 69 | border-left: 1px solid #ddd; 70 | } 71 | 72 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 73 | border-left: 0; 74 | border-right: 1px solid #ddd; 75 | } 76 | 77 | #changelist table tbody td.action-checkbox { 78 | text-align:center; 79 | } 80 | 81 | #changelist table tfoot { 82 | color: #666; 83 | } 84 | 85 | /* TOOLBAR */ 86 | 87 | #changelist #toolbar { 88 | padding: 3px; 89 | border-bottom: 1px solid #ddd; 90 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 91 | color: #666; 92 | } 93 | 94 | #changelist #toolbar form input { 95 | font-size: 11px; 96 | padding: 1px 2px; 97 | } 98 | 99 | #changelist #toolbar form #searchbar { 100 | padding: 2px; 101 | } 102 | 103 | #changelist #changelist-search img { 104 | vertical-align: middle; 105 | } 106 | 107 | /* FILTER COLUMN */ 108 | 109 | #changelist-filter { 110 | position: absolute; 111 | top: 0; 112 | right: 0; 113 | z-index: 1000; 114 | width: 160px; 115 | border-left: 1px solid #ddd; 116 | background: #efefef; 117 | margin: 0; 118 | } 119 | 120 | #changelist-filter h2 { 121 | font-size: 11px; 122 | padding: 2px 5px; 123 | border-bottom: 1px solid #ddd; 124 | } 125 | 126 | #changelist-filter h3 { 127 | font-size: 12px; 128 | margin-bottom: 0; 129 | } 130 | 131 | #changelist-filter ul { 132 | padding-left: 0; 133 | margin-left: 10px; 134 | } 135 | 136 | #changelist-filter li { 137 | list-style-type: none; 138 | margin-left: 0; 139 | padding-left: 0; 140 | } 141 | 142 | #changelist-filter a { 143 | color: #999; 144 | } 145 | 146 | #changelist-filter a:hover { 147 | color: #036; 148 | } 149 | 150 | #changelist-filter li.selected { 151 | border-left: 5px solid #ccc; 152 | padding-left: 5px; 153 | margin-left: -10px; 154 | } 155 | 156 | #changelist-filter li.selected a { 157 | color: #5b80b2 !important; 158 | } 159 | 160 | /* DATE DRILLDOWN */ 161 | 162 | .change-list ul.toplinks { 163 | display: block; 164 | background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x; 165 | border-top: 1px solid white; 166 | float: left; 167 | padding: 0 !important; 168 | margin: 0 !important; 169 | width: 100%; 170 | } 171 | 172 | .change-list ul.toplinks li { 173 | padding: 3px 6px; 174 | font-weight: bold; 175 | list-style-type: none; 176 | display: inline-block; 177 | } 178 | 179 | .change-list ul.toplinks .date-back a { 180 | color: #999; 181 | } 182 | 183 | .change-list ul.toplinks .date-back a:hover { 184 | color: #036; 185 | } 186 | 187 | /* PAGINATOR */ 188 | 189 | .paginator { 190 | font-size: 11px; 191 | padding-top: 10px; 192 | padding-bottom: 10px; 193 | line-height: 22px; 194 | margin: 0; 195 | border-top: 1px solid #ddd; 196 | } 197 | 198 | .paginator a:link, .paginator a:visited { 199 | padding: 2px 6px; 200 | border: solid 1px #ccc; 201 | background: white; 202 | text-decoration: none; 203 | } 204 | 205 | .paginator a.showall { 206 | padding: 0 !important; 207 | border: none !important; 208 | } 209 | 210 | .paginator a.showall:hover { 211 | color: #036 !important; 212 | background: transparent !important; 213 | } 214 | 215 | .paginator .end { 216 | border-width: 2px !important; 217 | margin-right: 6px; 218 | } 219 | 220 | .paginator .this-page { 221 | padding: 2px 6px; 222 | font-weight: bold; 223 | font-size: 13px; 224 | vertical-align: top; 225 | } 226 | 227 | .paginator a:hover { 228 | color: white; 229 | background: #5b80b2; 230 | border-color: #036; 231 | } 232 | 233 | /* ACTIONS */ 234 | 235 | .filtered .actions { 236 | margin-right: 160px !important; 237 | border-right: 1px solid #ddd; 238 | } 239 | 240 | #changelist table input { 241 | margin: 0; 242 | } 243 | 244 | #changelist table tbody tr.selected { 245 | background-color: #FFFFCC; 246 | } 247 | 248 | #changelist .actions { 249 | color: #999; 250 | padding: 3px; 251 | border-top: 1px solid #fff; 252 | border-bottom: 1px solid #ddd; 253 | background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x; 254 | } 255 | 256 | #changelist .actions.selected { 257 | background: #fffccf; 258 | border-top: 1px solid #fffee8; 259 | border-bottom: 1px solid #edecd6; 260 | } 261 | 262 | #changelist .actions span.all, 263 | #changelist .actions span.action-counter, 264 | #changelist .actions span.clear, 265 | #changelist .actions span.question { 266 | font-size: 11px; 267 | margin: 0 0.5em; 268 | display: none; 269 | } 270 | 271 | #changelist .actions:last-child { 272 | border-bottom: none; 273 | } 274 | 275 | #changelist .actions select { 276 | border: 1px solid #aaa; 277 | margin-left: 0.5em; 278 | padding: 1px 2px; 279 | } 280 | 281 | #changelist .actions label { 282 | font-size: 11px; 283 | margin-left: 0.5em; 284 | } 285 | 286 | #changelist #action-toggle { 287 | display: none; 288 | } 289 | 290 | #changelist .actions .button { 291 | font-size: 11px; 292 | padding: 1px 2px; 293 | } 294 | -------------------------------------------------------------------------------- /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 | word-wrap: break-word; 69 | } 70 | 71 | .aligned ul label { 72 | display: inline; 73 | float: none; 74 | width: auto; 75 | } 76 | 77 | .colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { 78 | width: 350px; 79 | } 80 | 81 | form .aligned p, form .aligned ul { 82 | margin-left: 7em; 83 | padding-left: 30px; 84 | } 85 | 86 | form .aligned table p { 87 | margin-left: 0; 88 | padding-left: 0; 89 | } 90 | 91 | form .aligned p.help { 92 | padding-left: 38px; 93 | } 94 | 95 | .aligned .vCheckboxLabel { 96 | float: none !important; 97 | display: inline; 98 | padding-left: 4px; 99 | } 100 | 101 | .colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField { 102 | width: 610px; 103 | } 104 | 105 | .checkbox-row p.help { 106 | margin-left: 0; 107 | padding-left: 0 !important; 108 | } 109 | 110 | fieldset .field-box { 111 | float: left; 112 | margin-right: 20px; 113 | } 114 | 115 | /* WIDE FIELDSETS */ 116 | 117 | .wide label { 118 | width: 15em !important; 119 | } 120 | 121 | form .wide p { 122 | margin-left: 15em; 123 | } 124 | 125 | form .wide p.help { 126 | padding-left: 38px; 127 | } 128 | 129 | .colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { 130 | width: 450px; 131 | } 132 | 133 | /* COLLAPSED FIELDSETS */ 134 | 135 | fieldset.collapsed * { 136 | display: none; 137 | } 138 | 139 | fieldset.collapsed h2, fieldset.collapsed { 140 | display: block !important; 141 | } 142 | 143 | fieldset.collapsed h2 { 144 | background-image: url(../img/nav-bg.gif); 145 | background-position: bottom left; 146 | color: #999; 147 | } 148 | 149 | fieldset.collapsed .collapse-toggle { 150 | background: transparent; 151 | display: inline !important; 152 | } 153 | 154 | /* MONOSPACE TEXTAREAS */ 155 | 156 | fieldset.monospace textarea { 157 | font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace; 158 | } 159 | 160 | /* SUBMIT ROW */ 161 | 162 | .submit-row { 163 | padding: 5px 7px; 164 | text-align: right; 165 | background: white url(../img/nav-bg.gif) 0 100% repeat-x; 166 | border: 1px solid #ccc; 167 | margin: 5px 0; 168 | overflow: hidden; 169 | } 170 | 171 | body.popup .submit-row { 172 | overflow: auto; 173 | } 174 | 175 | .submit-row input { 176 | margin: 0 0 0 5px; 177 | } 178 | 179 | .submit-row p { 180 | margin: 0.3em; 181 | } 182 | 183 | .submit-row p.deletelink-box { 184 | float: left; 185 | } 186 | 187 | .submit-row .deletelink { 188 | background: url(../img/icon_deletelink.gif) 0 50% no-repeat; 189 | padding-left: 14px; 190 | } 191 | 192 | /* CUSTOM FORM FIELDS */ 193 | 194 | .vSelectMultipleField { 195 | vertical-align: top !important; 196 | } 197 | 198 | .vCheckboxField { 199 | border: none; 200 | } 201 | 202 | .vDateField, .vTimeField { 203 | margin-right: 2px; 204 | } 205 | 206 | .vURLField { 207 | width: 30em; 208 | } 209 | 210 | .vLargeTextField, .vXMLLargeTextField { 211 | width: 48em; 212 | } 213 | 214 | .flatpages-flatpage #id_content { 215 | height: 40.2em; 216 | } 217 | 218 | .module table .vPositiveSmallIntegerField { 219 | width: 2.2em; 220 | } 221 | 222 | .vTextField { 223 | width: 20em; 224 | } 225 | 226 | .vIntegerField { 227 | width: 5em; 228 | } 229 | 230 | .vBigIntegerField { 231 | width: 10em; 232 | } 233 | 234 | .vForeignKeyRawIdAdminField { 235 | width: 5em; 236 | } 237 | 238 | /* INLINES */ 239 | 240 | .inline-group { 241 | padding: 0; 242 | border: 1px solid #ccc; 243 | margin: 10px 0; 244 | } 245 | 246 | .inline-group .aligned label { 247 | width: 8em; 248 | } 249 | 250 | .inline-related { 251 | position: relative; 252 | } 253 | 254 | .inline-related h3 { 255 | margin: 0; 256 | color: #666; 257 | padding: 3px 5px; 258 | font-size: 11px; 259 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 260 | border-bottom: 1px solid #ddd; 261 | } 262 | 263 | .inline-related h3 span.delete { 264 | float: right; 265 | } 266 | 267 | .inline-related h3 span.delete label { 268 | margin-left: 2px; 269 | font-size: 11px; 270 | } 271 | 272 | .inline-related fieldset { 273 | margin: 0; 274 | background: #fff; 275 | border: none; 276 | width: 100%; 277 | } 278 | 279 | .inline-related fieldset.module h3 { 280 | margin: 0; 281 | padding: 2px 5px 3px 5px; 282 | font-size: 11px; 283 | text-align: left; 284 | font-weight: bold; 285 | background: #bcd; 286 | color: #fff; 287 | } 288 | 289 | .inline-group .tabular fieldset.module { 290 | border: none; 291 | border-bottom: 1px solid #ddd; 292 | } 293 | 294 | .inline-related.tabular fieldset.module table { 295 | width: 100%; 296 | } 297 | 298 | .last-related fieldset { 299 | border: none; 300 | } 301 | 302 | .inline-group .tabular tr.has_original td { 303 | padding-top: 2em; 304 | } 305 | 306 | .inline-group .tabular tr td.original { 307 | padding: 2px 0 0 0; 308 | width: 0; 309 | _position: relative; 310 | } 311 | 312 | .inline-group .tabular th.original { 313 | width: 0px; 314 | padding: 0; 315 | } 316 | 317 | .inline-group .tabular td.original p { 318 | position: absolute; 319 | left: 0; 320 | height: 1.1em; 321 | padding: 2px 7px; 322 | overflow: hidden; 323 | font-size: 9px; 324 | font-weight: bold; 325 | color: #666; 326 | _width: 700px; 327 | } 328 | 329 | .inline-group ul.tools { 330 | padding: 0; 331 | margin: 0; 332 | list-style: none; 333 | } 334 | 335 | .inline-group ul.tools li { 336 | display: inline; 337 | padding: 0 5px; 338 | } 339 | 340 | .inline-group div.add-row, 341 | .inline-group .tabular tr.add-row td { 342 | color: #666; 343 | padding: 3px 5px; 344 | border-bottom: 1px solid #ddd; 345 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 346 | } 347 | 348 | .inline-group .tabular tr.add-row td { 349 | padding: 4px 5px 3px; 350 | border-bottom: none; 351 | } 352 | 353 | .inline-group ul.tools a.add, 354 | .inline-group div.add-row a, 355 | .inline-group .tabular tr.add-row td a { 356 | background: url(../img/icon_addlink.gif) 0 50% no-repeat; 357 | padding-left: 14px; 358 | font-size: 11px; 359 | outline: 0; /* Remove dotted border around link */ 360 | } 361 | 362 | .empty-form { 363 | display: none; 364 | } 365 | -------------------------------------------------------------------------------- /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 | function cancelEventPropagation(e) { 33 | if (!e) e = window.event; 34 | e.cancelBubble = true; 35 | if (e.stopPropagation) e.stopPropagation(); 36 | } 37 | 38 | // quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]); 39 | function quickElement() { 40 | var obj = document.createElement(arguments[0]); 41 | if (arguments[2] != '' && arguments[2] != null) { 42 | var textNode = document.createTextNode(arguments[2]); 43 | obj.appendChild(textNode); 44 | } 45 | var len = arguments.length; 46 | for (var i = 3; i < len; i += 2) { 47 | obj.setAttribute(arguments[i], arguments[i+1]); 48 | } 49 | arguments[1].appendChild(obj); 50 | return obj; 51 | } 52 | 53 | // "a" is reference to an object 54 | function removeChildren(a) { 55 | while (a.hasChildNodes()) a.removeChild(a.lastChild); 56 | } 57 | 58 | // ---------------------------------------------------------------------------- 59 | // Cross-browser xmlhttp object 60 | // from http://jibbering.com/2002/4/httprequest.html 61 | // ---------------------------------------------------------------------------- 62 | var xmlhttp; 63 | /*@cc_on @*/ 64 | /*@if (@_jscript_version >= 5) 65 | try { 66 | xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 67 | } catch (e) { 68 | try { 69 | xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 70 | } catch (E) { 71 | xmlhttp = false; 72 | } 73 | } 74 | @else 75 | xmlhttp = false; 76 | @end @*/ 77 | if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { 78 | xmlhttp = new XMLHttpRequest(); 79 | } 80 | 81 | // ---------------------------------------------------------------------------- 82 | // Find-position functions by PPK 83 | // See http://www.quirksmode.org/js/findpos.html 84 | // ---------------------------------------------------------------------------- 85 | function findPosX(obj) { 86 | var curleft = 0; 87 | if (obj.offsetParent) { 88 | while (obj.offsetParent) { 89 | curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft); 90 | obj = obj.offsetParent; 91 | } 92 | // IE offsetParent does not include the top-level 93 | if (isIE && obj.parentElement){ 94 | curleft += obj.offsetLeft - obj.scrollLeft; 95 | } 96 | } else if (obj.x) { 97 | curleft += obj.x; 98 | } 99 | return curleft; 100 | } 101 | 102 | function findPosY(obj) { 103 | var curtop = 0; 104 | if (obj.offsetParent) { 105 | while (obj.offsetParent) { 106 | curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop); 107 | obj = obj.offsetParent; 108 | } 109 | // IE offsetParent does not include the top-level 110 | if (isIE && obj.parentElement){ 111 | curtop += obj.offsetTop - obj.scrollTop; 112 | } 113 | } else if (obj.y) { 114 | curtop += obj.y; 115 | } 116 | return curtop; 117 | } 118 | 119 | //----------------------------------------------------------------------------- 120 | // Date object extensions 121 | // ---------------------------------------------------------------------------- 122 | 123 | Date.prototype.getTwelveHours = function() { 124 | hours = this.getHours(); 125 | if (hours == 0) { 126 | return 12; 127 | } 128 | else { 129 | return hours <= 12 ? hours : hours-12 130 | } 131 | } 132 | 133 | Date.prototype.getTwoDigitMonth = function() { 134 | return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1); 135 | } 136 | 137 | Date.prototype.getTwoDigitDate = function() { 138 | return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate(); 139 | } 140 | 141 | Date.prototype.getTwoDigitTwelveHour = function() { 142 | return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours(); 143 | } 144 | 145 | Date.prototype.getTwoDigitHour = function() { 146 | return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours(); 147 | } 148 | 149 | Date.prototype.getTwoDigitMinute = function() { 150 | return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); 151 | } 152 | 153 | Date.prototype.getTwoDigitSecond = function() { 154 | return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds(); 155 | } 156 | 157 | Date.prototype.getHourMinute = function() { 158 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute(); 159 | } 160 | 161 | Date.prototype.getHourMinuteSecond = function() { 162 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond(); 163 | } 164 | 165 | Date.prototype.strftime = function(format) { 166 | var fields = { 167 | c: this.toString(), 168 | d: this.getTwoDigitDate(), 169 | H: this.getTwoDigitHour(), 170 | I: this.getTwoDigitTwelveHour(), 171 | m: this.getTwoDigitMonth(), 172 | M: this.getTwoDigitMinute(), 173 | p: (this.getHours() >= 12) ? 'PM' : 'AM', 174 | S: this.getTwoDigitSecond(), 175 | w: '0' + this.getDay(), 176 | x: this.toLocaleDateString(), 177 | X: this.toLocaleTimeString(), 178 | y: ('' + this.getFullYear()).substr(2, 4), 179 | Y: '' + this.getFullYear(), 180 | '%' : '%' 181 | }; 182 | var result = '', i = 0; 183 | while (i < format.length) { 184 | if (format.charAt(i) === '%') { 185 | result = result + fields[format.charAt(i + 1)]; 186 | ++i; 187 | } 188 | else { 189 | result = result + format.charAt(i); 190 | } 191 | ++i; 192 | } 193 | return result; 194 | } 195 | 196 | // ---------------------------------------------------------------------------- 197 | // String object extensions 198 | // ---------------------------------------------------------------------------- 199 | String.prototype.pad_left = function(pad_length, pad_string) { 200 | var new_string = this; 201 | for (var i = 0; new_string.length < pad_length; i++) { 202 | new_string = pad_string + new_string; 203 | } 204 | return new_string; 205 | } 206 | 207 | // ---------------------------------------------------------------------------- 208 | // Get the computed style for and element 209 | // ---------------------------------------------------------------------------- 210 | function getStyle(oElm, strCssRule){ 211 | var strValue = ""; 212 | if(document.defaultView && document.defaultView.getComputedStyle){ 213 | strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); 214 | } 215 | else if(oElm.currentStyle){ 216 | strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ 217 | return p1.toUpperCase(); 218 | }); 219 | strValue = oElm.currentStyle[strCssRule]; 220 | } 221 | return strValue; 222 | } 223 | -------------------------------------------------------------------------------- /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_static_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_static_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_static_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_static_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/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, Travis Swicegood and Julien Phalip. 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 $this = $(this); 21 | var $parent = $this.parent(); 22 | var updateElementIndex = function(el, prefix, ndx) { 23 | var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))"); 24 | var replacement = prefix + "-" + ndx; 25 | if ($(el).prop("for")) { 26 | $(el).prop("for", $(el).prop("for").replace(id_regex, replacement)); 27 | } 28 | if (el.id) { 29 | el.id = el.id.replace(id_regex, replacement); 30 | } 31 | if (el.name) { 32 | el.name = el.name.replace(id_regex, replacement); 33 | } 34 | }; 35 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off"); 36 | var nextIndex = parseInt(totalForms.val(), 10); 37 | var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off"); 38 | // only show the add button if we are allowed to add more items, 39 | // note that max_num = None translates to a blank string. 40 | var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0; 41 | $this.each(function(i) { 42 | $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); 43 | }); 44 | if ($this.length && showAddButton) { 45 | var addButton; 46 | if ($this.prop("tagName") == "TR") { 47 | // If forms are laid out as table rows, insert the 48 | // "add" button in a new table row: 49 | var numCols = this.eq(-1).children().length; 50 | $parent.append('' + options.addText + ""); 51 | addButton = $parent.find("tr:last a"); 52 | } else { 53 | // Otherwise, insert it immediately after the last form: 54 | $this.filter(":last").after('"); 55 | addButton = $this.filter(":last").next().find("a"); 56 | } 57 | addButton.click(function(e) { 58 | e.preventDefault(); 59 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); 60 | var template = $("#" + options.prefix + "-empty"); 61 | var row = template.clone(true); 62 | row.removeClass(options.emptyCssClass) 63 | .addClass(options.formCssClass) 64 | .attr("id", options.prefix + "-" + nextIndex); 65 | if (row.is("tr")) { 66 | // If the forms are laid out in table rows, insert 67 | // the remove button into the last table cell: 68 | row.children(":last").append('"); 69 | } else if (row.is("ul") || row.is("ol")) { 70 | // If they're laid out as an ordered/unordered list, 71 | // insert an
    • after the last list item: 72 | row.append('
    • ' + options.deleteText + "
    • "); 73 | } else { 74 | // Otherwise, just insert the remove button as the 75 | // last child element of the form's container: 76 | row.children(":first").append('' + options.deleteText + ""); 77 | } 78 | row.find("*").each(function() { 79 | updateElementIndex(this, options.prefix, totalForms.val()); 80 | }); 81 | // Insert the new form when it has been fully edited 82 | row.insertBefore($(template)); 83 | // Update number of total forms 84 | $(totalForms).val(parseInt(totalForms.val(), 10) + 1); 85 | nextIndex += 1; 86 | // Hide add button in case we've hit the max, except we want to add infinitely 87 | if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) { 88 | addButton.parent().hide(); 89 | } 90 | // The delete button of each row triggers a bunch of other things 91 | row.find("a." + options.deleteCssClass).click(function(e) { 92 | e.preventDefault(); 93 | // Remove the parent form containing this button: 94 | var row = $(this).parents("." + options.formCssClass); 95 | row.remove(); 96 | nextIndex -= 1; 97 | // If a post-delete callback was provided, call it with the deleted form: 98 | if (options.removed) { 99 | options.removed(row); 100 | } 101 | // Update the TOTAL_FORMS form count. 102 | var forms = $("." + options.formCssClass); 103 | $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); 104 | // Show add button again once we drop below max 105 | if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) { 106 | addButton.parent().show(); 107 | } 108 | // Also, update names and ids for all remaining form controls 109 | // so they remain in sequence: 110 | for (var i=0, formCount=forms.length; i 3 | // 4 | 5 | var DateTimeShortcuts = { 6 | calendars: [], 7 | calendarInputs: [], 8 | clockInputs: [], 9 | dismissClockFunc: [], 10 | dismissCalendarFunc: [], 11 | calendarDivName1: 'calendarbox', // name of calendar
      that gets toggled 12 | calendarDivName2: 'calendarin', // name of
      that contains calendar 13 | calendarLinkName: 'calendarlink',// name of the link that is used to toggle 14 | clockDivName: 'clockbox', // name of clock
      that gets toggled 15 | clockLinkName: 'clocklink', // name of the link that is used to toggle 16 | shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts 17 | admin_media_prefix: '', 18 | init: function() { 19 | // Get admin_media_prefix by grabbing it off the window object. It's 20 | // set in the admin/base.html template, so if it's not there, someone's 21 | // overridden the template. In that case, we'll set a clearly-invalid 22 | // value in the hopes that someone will examine HTTP requests and see it. 23 | if (window.__admin_media_prefix__ != undefined) { 24 | DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__; 25 | } else { 26 | DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/'; 27 | } 28 | 29 | var inputs = document.getElementsByTagName('input'); 30 | for (i=0; i 66 | //

      Choose a time

      67 | // 73 | //

      Cancel

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

      164 | // 165 | // February 2003 166 | //

      167 | //
      168 | // 169 | //
      170 | //
      171 | // Yesterday | Today | Tomorrow 172 | //
      173 | //

      Cancel

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

      ' + key + '

      ' + 32 | '

      ' + y + ' at ' + x + '

      ' 33 | } 34 | , noData = "No Data Available." 35 | , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush') 36 | , transitionDuration = 250 37 | ; 38 | 39 | lines 40 | .clipEdge(true) 41 | ; 42 | lines2 43 | .interactive(false) 44 | ; 45 | xAxis 46 | .orient('bottom') 47 | .tickPadding(5) 48 | ; 49 | yAxis 50 | .orient('left') 51 | ; 52 | x2Axis 53 | .orient('bottom') 54 | .tickPadding(5) 55 | ; 56 | y2Axis 57 | .orient('left') 58 | ; 59 | //============================================================ 60 | 61 | 62 | //============================================================ 63 | // Private Variables 64 | //------------------------------------------------------------ 65 | 66 | var showTooltip = function(e, offsetElement) { 67 | var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ), 68 | top = e.pos[1] + ( offsetElement.offsetTop || 0), 69 | x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)), 70 | y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)), 71 | content = tooltip(e.series.key, x, y, e, chart); 72 | 73 | nv.tooltip.show([left, top], content, null, null, offsetElement); 74 | }; 75 | 76 | //============================================================ 77 | 78 | 79 | function chart(selection) { 80 | selection.each(function(data) { 81 | var container = d3.select(this), 82 | that = this; 83 | 84 | var availableWidth = (width || parseInt(container.style('width')) || 960) 85 | - margin.left - margin.right, 86 | availableHeight1 = (height || parseInt(container.style('height')) || 400) 87 | - margin.top - margin.bottom - height2, 88 | availableHeight2 = height2 - margin2.top - margin2.bottom; 89 | 90 | chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; 91 | chart.container = this; 92 | 93 | 94 | //------------------------------------------------------------ 95 | // Display No Data message if there's nothing to show. 96 | 97 | if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 98 | var noDataText = container.selectAll('.nv-noData').data([noData]); 99 | 100 | noDataText.enter().append('text') 101 | .attr('class', 'nvd3 nv-noData') 102 | .attr('dy', '-.7em') 103 | .style('text-anchor', 'middle'); 104 | 105 | noDataText 106 | .attr('x', margin.left + availableWidth / 2) 107 | .attr('y', margin.top + availableHeight1 / 2) 108 | .text(function(d) { return d }); 109 | 110 | return chart; 111 | } else { 112 | container.selectAll('.nv-noData').remove(); 113 | } 114 | 115 | //------------------------------------------------------------ 116 | 117 | 118 | //------------------------------------------------------------ 119 | // Setup Scales 120 | 121 | x = lines.xScale(); 122 | y = lines.yScale(); 123 | x2 = lines2.xScale(); 124 | y2 = lines2.yScale(); 125 | 126 | //------------------------------------------------------------ 127 | 128 | 129 | //------------------------------------------------------------ 130 | // Setup containers and skeleton of chart 131 | 132 | var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]); 133 | var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g'); 134 | var g = wrap.select('g'); 135 | 136 | gEnter.append('g').attr('class', 'nv-legendWrap'); 137 | 138 | var focusEnter = gEnter.append('g').attr('class', 'nv-focus'); 139 | focusEnter.append('g').attr('class', 'nv-x nv-axis'); 140 | focusEnter.append('g').attr('class', 'nv-y nv-axis'); 141 | focusEnter.append('g').attr('class', 'nv-linesWrap'); 142 | 143 | var contextEnter = gEnter.append('g').attr('class', 'nv-context'); 144 | contextEnter.append('g').attr('class', 'nv-x nv-axis'); 145 | contextEnter.append('g').attr('class', 'nv-y nv-axis'); 146 | contextEnter.append('g').attr('class', 'nv-linesWrap'); 147 | contextEnter.append('g').attr('class', 'nv-brushBackground'); 148 | contextEnter.append('g').attr('class', 'nv-x nv-brush'); 149 | 150 | //------------------------------------------------------------ 151 | 152 | 153 | //------------------------------------------------------------ 154 | // Legend 155 | 156 | if (showLegend) { 157 | legend.width(availableWidth); 158 | 159 | g.select('.nv-legendWrap') 160 | .datum(data) 161 | .call(legend); 162 | 163 | if ( margin.top != legend.height()) { 164 | margin.top = legend.height(); 165 | availableHeight1 = (height || parseInt(container.style('height')) || 400) 166 | - margin.top - margin.bottom - height2; 167 | } 168 | 169 | g.select('.nv-legendWrap') 170 | .attr('transform', 'translate(0,' + (-margin.top) +')') 171 | } 172 | 173 | //------------------------------------------------------------ 174 | 175 | 176 | wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 177 | 178 | 179 | //------------------------------------------------------------ 180 | // Main Chart Component(s) 181 | 182 | lines 183 | .width(availableWidth) 184 | .height(availableHeight1) 185 | .color( 186 | data 187 | .map(function(d,i) { 188 | return d.color || color(d, i); 189 | }) 190 | .filter(function(d,i) { 191 | return !data[i].disabled; 192 | }) 193 | ); 194 | 195 | lines2 196 | .defined(lines.defined()) 197 | .width(availableWidth) 198 | .height(availableHeight2) 199 | .color( 200 | data 201 | .map(function(d,i) { 202 | return d.color || color(d, i); 203 | }) 204 | .filter(function(d,i) { 205 | return !data[i].disabled; 206 | }) 207 | ); 208 | 209 | g.select('.nv-context') 210 | .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')') 211 | 212 | var contextLinesWrap = g.select('.nv-context .nv-linesWrap') 213 | .datum(data.filter(function(d) { return !d.disabled })) 214 | 215 | d3.transition(contextLinesWrap).call(lines2); 216 | 217 | //------------------------------------------------------------ 218 | 219 | 220 | /* 221 | var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') 222 | .datum(data.filter(function(d) { return !d.disabled })) 223 | 224 | d3.transition(focusLinesWrap).call(lines); 225 | */ 226 | 227 | 228 | //------------------------------------------------------------ 229 | // Setup Main (Focus) Axes 230 | 231 | xAxis 232 | .scale(x) 233 | .ticks( availableWidth / 100 ) 234 | .tickSize(-availableHeight1, 0); 235 | 236 | yAxis 237 | .scale(y) 238 | .ticks( availableHeight1 / 36 ) 239 | .tickSize( -availableWidth, 0); 240 | 241 | g.select('.nv-focus .nv-x.nv-axis') 242 | .attr('transform', 'translate(0,' + availableHeight1 + ')'); 243 | 244 | //------------------------------------------------------------ 245 | 246 | 247 | //------------------------------------------------------------ 248 | // Setup Brush 249 | 250 | brush 251 | .x(x2) 252 | .on('brush', function() { 253 | //When brushing, turn off transitions because chart needs to change immediately. 254 | var oldTransition = chart.transitionDuration(); 255 | chart.transitionDuration(0); 256 | onBrush(); 257 | chart.transitionDuration(oldTransition); 258 | }); 259 | 260 | if (brushExtent) brush.extent(brushExtent); 261 | 262 | var brushBG = g.select('.nv-brushBackground').selectAll('g') 263 | .data([brushExtent || brush.extent()]) 264 | 265 | var brushBGenter = brushBG.enter() 266 | .append('g'); 267 | 268 | brushBGenter.append('rect') 269 | .attr('class', 'left') 270 | .attr('x', 0) 271 | .attr('y', 0) 272 | .attr('height', availableHeight2); 273 | 274 | brushBGenter.append('rect') 275 | .attr('class', 'right') 276 | .attr('x', 0) 277 | .attr('y', 0) 278 | .attr('height', availableHeight2); 279 | 280 | var gBrush = g.select('.nv-x.nv-brush') 281 | .call(brush); 282 | gBrush.selectAll('rect') 283 | //.attr('y', -5) 284 | .attr('height', availableHeight2); 285 | gBrush.selectAll('.resize').append('path').attr('d', resizePath); 286 | 287 | onBrush(); 288 | 289 | //------------------------------------------------------------ 290 | 291 | 292 | //------------------------------------------------------------ 293 | // Setup Secondary (Context) Axes 294 | 295 | x2Axis 296 | .scale(x2) 297 | .ticks( availableWidth / 100 ) 298 | .tickSize(-availableHeight2, 0); 299 | 300 | g.select('.nv-context .nv-x.nv-axis') 301 | .attr('transform', 'translate(0,' + y2.range()[0] + ')'); 302 | d3.transition(g.select('.nv-context .nv-x.nv-axis')) 303 | .call(x2Axis); 304 | 305 | 306 | y2Axis 307 | .scale(y2) 308 | .ticks( availableHeight2 / 36 ) 309 | .tickSize( -availableWidth, 0); 310 | 311 | d3.transition(g.select('.nv-context .nv-y.nv-axis')) 312 | .call(y2Axis); 313 | 314 | g.select('.nv-context .nv-x.nv-axis') 315 | .attr('transform', 'translate(0,' + y2.range()[0] + ')'); 316 | 317 | //------------------------------------------------------------ 318 | 319 | 320 | //============================================================ 321 | // Event Handling/Dispatching (in chart's scope) 322 | //------------------------------------------------------------ 323 | 324 | legend.dispatch.on('stateChange', function(newState) { 325 | chart.update(); 326 | }); 327 | 328 | dispatch.on('tooltipShow', function(e) { 329 | if (tooltips) showTooltip(e, that.parentNode); 330 | }); 331 | 332 | //============================================================ 333 | 334 | 335 | //============================================================ 336 | // Functions 337 | //------------------------------------------------------------ 338 | 339 | // Taken from crossfilter (http://square.github.com/crossfilter/) 340 | function resizePath(d) { 341 | var e = +(d == 'e'), 342 | x = e ? 1 : -1, 343 | y = availableHeight2 / 3; 344 | return 'M' + (.5 * x) + ',' + y 345 | + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6) 346 | + 'V' + (2 * y - 6) 347 | + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y) 348 | + 'Z' 349 | + 'M' + (2.5 * x) + ',' + (y + 8) 350 | + 'V' + (2 * y - 8) 351 | + 'M' + (4.5 * x) + ',' + (y + 8) 352 | + 'V' + (2 * y - 8); 353 | } 354 | 355 | 356 | function updateBrushBG() { 357 | if (!brush.empty()) brush.extent(brushExtent); 358 | brushBG 359 | .data([brush.empty() ? x2.domain() : brushExtent]) 360 | .each(function(d,i) { 361 | var leftWidth = x2(d[0]) - x.range()[0], 362 | rightWidth = x.range()[1] - x2(d[1]); 363 | d3.select(this).select('.left') 364 | .attr('width', leftWidth < 0 ? 0 : leftWidth); 365 | 366 | d3.select(this).select('.right') 367 | .attr('x', x2(d[1])) 368 | .attr('width', rightWidth < 0 ? 0 : rightWidth); 369 | }); 370 | } 371 | 372 | 373 | function onBrush() { 374 | brushExtent = brush.empty() ? null : brush.extent(); 375 | var extent = brush.empty() ? x2.domain() : brush.extent(); 376 | 377 | //The brush extent cannot be less than one. If it is, don't update the line chart. 378 | if (Math.abs(extent[0] - extent[1]) <= 1) { 379 | return; 380 | } 381 | 382 | dispatch.brush({extent: extent, brush: brush}); 383 | 384 | 385 | updateBrushBG(); 386 | 387 | // Update Main (Focus) 388 | var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') 389 | .datum( 390 | data 391 | .filter(function(d) { return !d.disabled }) 392 | .map(function(d,i) { 393 | return { 394 | key: d.key, 395 | area: d.area, 396 | values: d.values.filter(function(d,i) { 397 | return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1]; 398 | }) 399 | } 400 | }) 401 | ); 402 | focusLinesWrap.transition().duration(transitionDuration).call(lines); 403 | 404 | 405 | // Update Main (Focus) Axes 406 | g.select('.nv-focus .nv-x.nv-axis').transition().duration(transitionDuration) 407 | .call(xAxis); 408 | g.select('.nv-focus .nv-y.nv-axis').transition().duration(transitionDuration) 409 | .call(yAxis); 410 | } 411 | 412 | //============================================================ 413 | 414 | 415 | }); 416 | 417 | return chart; 418 | } 419 | 420 | 421 | //============================================================ 422 | // Event Handling/Dispatching (out of chart's scope) 423 | //------------------------------------------------------------ 424 | 425 | lines.dispatch.on('elementMouseover.tooltip', function(e) { 426 | e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; 427 | dispatch.tooltipShow(e); 428 | }); 429 | 430 | lines.dispatch.on('elementMouseout.tooltip', function(e) { 431 | dispatch.tooltipHide(e); 432 | }); 433 | 434 | dispatch.on('tooltipHide', function() { 435 | if (tooltips) nv.tooltip.cleanup(); 436 | }); 437 | 438 | //============================================================ 439 | 440 | 441 | //============================================================ 442 | // Expose Public Variables 443 | //------------------------------------------------------------ 444 | 445 | // expose chart's sub-components 446 | chart.dispatch = dispatch; 447 | chart.legend = legend; 448 | chart.lines = lines; 449 | chart.lines2 = lines2; 450 | chart.xAxis = xAxis; 451 | chart.yAxis = yAxis; 452 | chart.x2Axis = x2Axis; 453 | chart.y2Axis = y2Axis; 454 | 455 | d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); 456 | 457 | chart.options = nv.utils.optionsFunc.bind(chart); 458 | 459 | chart.x = function(_) { 460 | if (!arguments.length) return lines.x; 461 | lines.x(_); 462 | lines2.x(_); 463 | return chart; 464 | }; 465 | 466 | chart.y = function(_) { 467 | if (!arguments.length) return lines.y; 468 | lines.y(_); 469 | lines2.y(_); 470 | return chart; 471 | }; 472 | 473 | chart.margin = function(_) { 474 | if (!arguments.length) return margin; 475 | margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 476 | margin.right = typeof _.right != 'undefined' ? _.right : margin.right; 477 | margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; 478 | margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 479 | return chart; 480 | }; 481 | 482 | chart.margin2 = function(_) { 483 | if (!arguments.length) return margin2; 484 | margin2 = _; 485 | return chart; 486 | }; 487 | 488 | chart.width = function(_) { 489 | if (!arguments.length) return width; 490 | width = _; 491 | return chart; 492 | }; 493 | 494 | chart.height = function(_) { 495 | if (!arguments.length) return height; 496 | height = _; 497 | return chart; 498 | }; 499 | 500 | chart.height2 = function(_) { 501 | if (!arguments.length) return height2; 502 | height2 = _; 503 | return chart; 504 | }; 505 | 506 | chart.color = function(_) { 507 | if (!arguments.length) return color; 508 | color =nv.utils.getColor(_); 509 | legend.color(color); 510 | return chart; 511 | }; 512 | 513 | chart.showLegend = function(_) { 514 | if (!arguments.length) return showLegend; 515 | showLegend = _; 516 | return chart; 517 | }; 518 | 519 | chart.tooltips = function(_) { 520 | if (!arguments.length) return tooltips; 521 | tooltips = _; 522 | return chart; 523 | }; 524 | 525 | chart.tooltipContent = function(_) { 526 | if (!arguments.length) return tooltip; 527 | tooltip = _; 528 | return chart; 529 | }; 530 | 531 | chart.interpolate = function(_) { 532 | if (!arguments.length) return lines.interpolate(); 533 | lines.interpolate(_); 534 | lines2.interpolate(_); 535 | return chart; 536 | }; 537 | 538 | chart.noData = function(_) { 539 | if (!arguments.length) return noData; 540 | noData = _; 541 | return chart; 542 | }; 543 | 544 | // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below 545 | chart.xTickFormat = function(_) { 546 | if (!arguments.length) return xAxis.tickFormat(); 547 | xAxis.tickFormat(_); 548 | x2Axis.tickFormat(_); 549 | return chart; 550 | }; 551 | 552 | chart.yTickFormat = function(_) { 553 | if (!arguments.length) return yAxis.tickFormat(); 554 | yAxis.tickFormat(_); 555 | y2Axis.tickFormat(_); 556 | return chart; 557 | }; 558 | 559 | chart.brushExtent = function(_) { 560 | if (!arguments.length) return brushExtent; 561 | brushExtent = _; 562 | return chart; 563 | }; 564 | 565 | chart.transitionDuration = function(_) { 566 | if (!arguments.length) return transitionDuration; 567 | transitionDuration = _; 568 | return chart; 569 | }; 570 | 571 | //============================================================ 572 | 573 | 574 | return chart; 575 | } --------------------------------------------------------------------------------