├── oabutton ├── __init__.py ├── apps │ ├── __init__.py │ ├── metadata │ │ ├── models.py │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── views.py │ │ └── tests.py │ ├── web │ │ ├── __init__.py │ │ ├── templatetags │ │ │ ├── __init__.py │ │ │ └── oafilters.py │ │ ├── models.py │ │ ├── urls.py │ │ ├── views.py │ │ ├── templates │ │ │ └── web │ │ │ │ ├── about.jade │ │ │ │ ├── layout.jade │ │ │ │ ├── start.jade │ │ │ │ └── index.jade │ │ └── tests.py │ └── bookmarklet │ │ ├── __init__.py │ │ ├── auth_extra.py │ │ ├── templatetags │ │ ├── __init__.py │ │ └── oafilters.py │ │ ├── urls.py │ │ ├── models.py │ │ ├── templates │ │ └── bookmarklet │ │ │ ├── page3.html │ │ │ ├── page1.html │ │ │ ├── page2.html │ │ │ ├── layout.html │ │ │ └── bookmarklet.html │ │ ├── tests.py │ │ └── views.py ├── static │ ├── public │ │ ├── img │ │ │ ├── bg.jpg │ │ │ ├── pmc.png │ │ │ ├── oalogo.png │ │ │ ├── aperson.png │ │ │ ├── favicon.ico │ │ │ ├── noalogo.png │ │ │ ├── scholar.png │ │ │ ├── header_wide.png │ │ │ ├── logo-medsin.jpg │ │ │ ├── photo-david.jpg │ │ │ ├── photo-joe.jpg │ │ │ ├── oabutton_logo.eps │ │ │ ├── oabutton_logo.png │ │ │ ├── oabutton_badge.png │ │ │ ├── oalogo_x_white.png │ │ │ ├── webicon-facebook.png │ │ │ ├── webicon-twitter.png │ │ │ ├── header_needaccess.png │ │ │ ├── iconmonstr-globe-3.png │ │ │ ├── iconmonstr-video-5.png │ │ │ ├── webicon-wordpress.png │ │ │ ├── glyphicons-halflings.png │ │ │ ├── iconmonstr-twitter-4.png │ │ │ ├── oabutton-logo-100x150.png │ │ │ ├── oabutton_logo_final200.png │ │ │ ├── glyphicons-halflings-white.png │ │ │ └── oabutton_logo_trans_bkgd.png │ │ ├── font │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ ├── less │ │ │ ├── styles.less │ │ │ ├── forms.less │ │ │ └── app.less │ │ ├── css │ │ │ ├── MarkerCluster.css │ │ │ ├── app.css │ │ │ ├── MarkerCluster.Default.css │ │ │ ├── leaflet.ie.css │ │ │ ├── styles.css │ │ │ ├── leaflet.css │ │ │ └── bootstrap-theme.min.css │ │ └── js │ │ │ ├── compiler.sh │ │ │ ├── success.js │ │ │ ├── lib │ │ │ ├── jquery.placeholder.js │ │ │ ├── jquery.color-2.1.2.min.js │ │ │ └── bootstrap-dialog.js │ │ │ ├── add.js │ │ │ └── index.js │ └── test │ │ ├── test.html │ │ ├── resources │ │ └── qunit.css │ │ └── test.js ├── json_util.py ├── settings_local.py.example ├── urls.py ├── wsgi.py ├── settings.py └── common │ └── __init__.py ├── runtime.txt ├── Procfile ├── setup.cfg ├── .gitignore ├── .travis.yml ├── oabutton.sublime-project ├── Makefile ├── manage.py ├── requirements.txt ├── fabfile.py ├── LICENSE.md ├── Vagrantfile ├── deploy └── gunicorn_start.sh ├── README.md └── INSTRUCTIONS.md /oabutton/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.4 2 | -------------------------------------------------------------------------------- /oabutton/apps/metadata/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/web/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/metadata/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn oabutton.wsgi 2 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/auth_extra.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/web/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oabutton/apps/web/models.py: -------------------------------------------------------------------------------- 1 | # Create your models here. 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E501 3 | exclude = oab-env 4 | -------------------------------------------------------------------------------- /oabutton/apps/web/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns 2 | 3 | urlpatterns = patterns('') 4 | -------------------------------------------------------------------------------- /oabutton/static/public/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/bg.jpg -------------------------------------------------------------------------------- /oabutton/static/public/img/pmc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/pmc.png -------------------------------------------------------------------------------- /oabutton/static/public/img/oalogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oalogo.png -------------------------------------------------------------------------------- /oabutton/static/public/img/aperson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/aperson.png -------------------------------------------------------------------------------- /oabutton/static/public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/favicon.ico -------------------------------------------------------------------------------- /oabutton/static/public/img/noalogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/noalogo.png -------------------------------------------------------------------------------- /oabutton/static/public/img/scholar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/scholar.png -------------------------------------------------------------------------------- /oabutton/static/public/img/header_wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/header_wide.png -------------------------------------------------------------------------------- /oabutton/static/public/img/logo-medsin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/logo-medsin.jpg -------------------------------------------------------------------------------- /oabutton/static/public/img/photo-david.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/photo-david.jpg -------------------------------------------------------------------------------- /oabutton/static/public/img/photo-joe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/photo-joe.jpg -------------------------------------------------------------------------------- /oabutton/static/public/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/font/FontAwesome.otf -------------------------------------------------------------------------------- /oabutton/static/public/img/oabutton_logo.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oabutton_logo.eps -------------------------------------------------------------------------------- /oabutton/static/public/img/oabutton_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oabutton_logo.png -------------------------------------------------------------------------------- /oabutton/static/public/img/oabutton_badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oabutton_badge.png -------------------------------------------------------------------------------- /oabutton/static/public/img/oalogo_x_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oalogo_x_white.png -------------------------------------------------------------------------------- /oabutton/static/public/img/webicon-facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/webicon-facebook.png -------------------------------------------------------------------------------- /oabutton/static/public/img/webicon-twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/webicon-twitter.png -------------------------------------------------------------------------------- /oabutton/static/public/img/header_needaccess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/header_needaccess.png -------------------------------------------------------------------------------- /oabutton/static/public/img/iconmonstr-globe-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/iconmonstr-globe-3.png -------------------------------------------------------------------------------- /oabutton/static/public/img/iconmonstr-video-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/iconmonstr-video-5.png -------------------------------------------------------------------------------- /oabutton/static/public/img/webicon-wordpress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/webicon-wordpress.png -------------------------------------------------------------------------------- /oabutton/static/public/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /oabutton/static/public/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /oabutton/static/public/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /oabutton/static/public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /oabutton/static/public/img/iconmonstr-twitter-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/iconmonstr-twitter-4.png -------------------------------------------------------------------------------- /oabutton/static/public/img/oabutton-logo-100x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oabutton-logo-100x150.png -------------------------------------------------------------------------------- /oabutton/static/public/img/oabutton_logo_final200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oabutton_logo_final200.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data 2 | oab-env 3 | build 4 | *.pyc 5 | *.sqlite3 6 | .vagrant 7 | *.sw? 8 | venv 9 | settings_local.py 10 | *.sublime-workspace 11 | compiler.jar 12 | -------------------------------------------------------------------------------- /oabutton/static/public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /oabutton/static/public/img/oabutton_logo_trans_bkgd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/img/oabutton_logo_trans_bkgd.png -------------------------------------------------------------------------------- /oabutton/static/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /oabutton/static/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /oabutton/static/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diracdeltas/OAButton/develop/oabutton/static/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | install: pip install -r requirements.txt --use-mirrors 5 | script: 6 | #- "flake8" 7 | - "python manage.py test web bookmarklet metadata" 8 | services: 9 | - mongodb 10 | -------------------------------------------------------------------------------- /oabutton/apps/metadata/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | from views import core_search 4 | 5 | urlpatterns = patterns('', 6 | url(r'^coresearch.json/(.*)$', core_search, name='core-search')) 7 | -------------------------------------------------------------------------------- /oabutton/apps/web/templatetags/oafilters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pyjade 4 | 5 | 6 | @pyjade.register_filter('label_with_classes') 7 | def label_with_classes(value, arg): 8 | return value.label_tag(attrs={'class': arg}) 9 | -------------------------------------------------------------------------------- /oabutton/static/public/less/styles.less: -------------------------------------------------------------------------------- 1 | /* Don't edit this CSS file. It's generated using lessc */ 2 | @import "../css/MarkerCluster.css"; 3 | @import "../css/MarkerCluster.Default.css"; 4 | 5 | @import "app.less"; 6 | @import "forms.less"; 7 | -------------------------------------------------------------------------------- /oabutton.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "." 6 | } 7 | ], 8 | "settings": 9 | { 10 | "tab_size": 4, 11 | "translate_tabs_to_spaces": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test lessc 2 | 3 | all: build 4 | 5 | build: lessc 6 | 7 | lessc: 8 | lessc oabutton/static/public/less/styles.less oabutton/static/public/css/styles.css 9 | 10 | 11 | test: 12 | python manage.py test web bookmarklet metadata 13 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/templatetags/oafilters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django import template 4 | register = template.Library() 5 | 6 | 7 | @register.filter(is_safe=True) 8 | def label_with_classes(value, arg): 9 | return value.label_tag(attrs={'class': arg}) 10 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oabutton.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /oabutton/json_util.py: -------------------------------------------------------------------------------- 1 | from json import JSONEncoder 2 | import datetime 3 | from bson.objectid import ObjectId 4 | 5 | 6 | class MyEncoder(JSONEncoder): 7 | 8 | def default(self, obj): 9 | if isinstance(obj, ObjectId): 10 | return str(obj) 11 | if isinstance(obj, datetime.datetime): 12 | return obj.isoformat() 13 | return JSONEncoder.default(self, obj) 14 | -------------------------------------------------------------------------------- /oabutton/static/public/css/MarkerCluster.css: -------------------------------------------------------------------------------- 1 | .leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow { 2 | -webkit-transition: -webkit-transform 0.2s ease-out, opacity 0.2s ease-in; 3 | -moz-transition: -moz-transform 0.2s ease-out, opacity 0.2s ease-in; 4 | -o-transition: -o-transform 0.2s ease-out, opacity 0.2s ease-in; 5 | transition: transform 0.2s ease-out, opacity 0.2s ease-in; 6 | } -------------------------------------------------------------------------------- /oabutton/settings_local.py.example: -------------------------------------------------------------------------------- 1 | DEBUG = False 2 | TEMPLATE_DEBUG = DEBUG 3 | 4 | # API key for CORE searches 5 | CORE_API_KEY = "you need this" 6 | 7 | # Hostname to be used for bookmarklet 8 | HOSTNAME = "and this" 9 | 10 | # Amazon SES Email configuration 11 | AMAZON_SMTPHOST = "YOUR_HOST" 12 | SESSMTPUSERNAME = "YOUR_SES_SMTP_USERNAME" 13 | SESSMTPPASSWORD = "YOUR_SES_SMTP_PASSWORD" 14 | 15 | OABUTTON_EMAIL = "oabutton@crankycoder.com" 16 | -------------------------------------------------------------------------------- /oabutton/static/public/js/compiler.sh: -------------------------------------------------------------------------------- 1 | echo "Expects compiler.jar (or symlink) in this folder" 2 | echo "See https://developers.google.com/closure/compiler/docs/gettingstarted_app" 3 | echo "--- compiling, please wait ... ---" 4 | cd lib 5 | java -jar ../compiler.jar \ 6 | --warning_level QUIET \ 7 | --js jquery.min.js \ 8 | --js jquery.color-2.1.2.min.js \ 9 | --js jquery.form.js \ 10 | --js jquery.placeholder.js \ 11 | --js bootstrap.min.js \ 12 | --js bootstrap-dialog.js \ 13 | --js leaflet.js \ 14 | --js leaflet.markercluster.js \ 15 | --js_output_file all.min.js 16 | -------------------------------------------------------------------------------- /oabutton/static/test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OAButton JavaScript tests 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.5.2 2 | Jinja2==2.7.1 3 | Markdown==2.3.1 4 | MarkupSafe==0.18 5 | Pygments==1.6 6 | Sphinx==1.2b1 7 | dj-static==0.0.5 8 | django-appconf==0.6 9 | django-compressor==1.3 10 | django-filter==0.7 11 | django-mongonaut==0.2.19 12 | djangorestframework==2.3.7 13 | djangotoolbox==0.9.2 14 | docutils==0.11 15 | flake8==2.0 16 | gunicorn==18.0 17 | httplib2==0.8 18 | httpretty==0.7.0 19 | ipython==1.0.0 20 | mccabe==0.2.1 21 | mock==1.0.1 22 | mongoengine==0.8.4 23 | nose==1.3.0 24 | ordereddict==1.1 25 | pep8==1.4.6 26 | plotly==0.4 27 | pyflakes==0.7.3 28 | pyjade==2.0.3 29 | pymongo==2.6.2 30 | python-dateutil==2.2 31 | requests==2.0.0 32 | six==1.4.1 33 | static==0.4 34 | urllib3==1.7.1 35 | wsgiref==0.1.2 36 | -------------------------------------------------------------------------------- /oabutton/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | # Uncomment the next two lines to enable the admin: 4 | from django.contrib import admin 5 | admin.autodiscover() 6 | 7 | from oabutton.apps.web import views as web 8 | 9 | urlpatterns = patterns('', 10 | url(r'^$', web.homepage, name="homepage"), 11 | 12 | # I just jammed this in here while i sort out all the URL 13 | # mappings. 14 | url(r'^api/', include('oabutton.apps.bookmarklet.urls', namespace='bookmarklet')), 15 | 16 | url(r'^metadata/', include( 17 | 'oabutton.apps.metadata.urls')), 18 | ) 19 | -------------------------------------------------------------------------------- /oabutton/static/public/css/app.css: -------------------------------------------------------------------------------- 1 | IMG#bookmarklet-logo { 2 | width: 100%; 3 | } 4 | 5 | IMG#bookmarklet-badge { 6 | display: none; 7 | } 8 | 9 | .bookmarklet-footer { 10 | margin-left: 15px; 11 | margin-right: 15px; 12 | margin-top: 10px; 13 | background: "grey"; 14 | font-family: "Raleway"; 15 | } 16 | 17 | .raleway-font { 18 | font-family: "Raleway"; 19 | } 20 | 21 | /* restyle anchor links into buttons on bookmarklet */ 22 | .likeabutton { 23 | appearance: button; 24 | -moz-appearance: button; 25 | -webkit-appearance: button; 26 | color: white; 27 | text-decoration: none; font: menu; color: ButtonText; 28 | display: inline-block; padding: 2px 8px; 29 | } 30 | 31 | #email-request { 32 | margin-top: 10px; 33 | } 34 | 35 | #core_results { 36 | margin-top: 10px; 37 | margin-bottom: 10px; 38 | } 39 | 40 | form button { 41 | margin-top: 5px; 42 | } 43 | -------------------------------------------------------------------------------- /oabutton/static/public/css/MarkerCluster.Default.css: -------------------------------------------------------------------------------- 1 | .marker-cluster-small { 2 | background-color: rgba(181, 226, 140, 0.6); 3 | } 4 | .marker-cluster-small div { 5 | background-color: rgba(110, 204, 57, 0.6); 6 | } 7 | 8 | .marker-cluster-medium { 9 | background-color: rgba(241, 211, 87, 0.6); 10 | } 11 | .marker-cluster-medium div { 12 | background-color: rgba(240, 194, 12, 0.6); 13 | } 14 | 15 | .marker-cluster-large { 16 | background-color: rgba(253, 156, 115, 0.6); 17 | } 18 | .marker-cluster-large div { 19 | background-color: rgba(241, 128, 23, 0.6); 20 | } 21 | 22 | .marker-cluster { 23 | background-clip: padding-box; 24 | border-radius: 20px; 25 | } 26 | .marker-cluster div { 27 | width: 30px; 28 | height: 30px; 29 | margin-left: 5px; 30 | margin-top: 5px; 31 | 32 | text-align: center; 33 | border-radius: 15px; 34 | font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; 35 | } 36 | .marker-cluster span { 37 | line-height: 30px; 38 | } -------------------------------------------------------------------------------- /fabfile.py: -------------------------------------------------------------------------------- 1 | """ 2 | The easy button for deployment 3 | 4 | Add your SSH key : 5 | 6 | $ ssh-add ~/.ssh/oabutton.pem 7 | Identity added: /Users/victorng/.ssh/oabutton.pem (/Users/victorng/.ssh/oabutton.pem) 8 | 9 | # Run prepare_deploy: 10 | 11 | $ fab prepare_deploy 12 | 13 | # Run deploy: 14 | 15 | $ fab -H ubuntu@staging.openaccessbutton.org deploy 16 | 17 | """ 18 | from fabric.api import local, settings, cd, run 19 | 20 | 21 | def prepare_deploy(): 22 | local("pip install -r requirements.txt") 23 | local("make test") 24 | local("git add -p && git commit") 25 | local("git push") 26 | 27 | 28 | def deploy(): 29 | code_dir = '/home/ubuntu/dev/OAButton/' 30 | with settings(warn_only=True): 31 | with cd(code_dir): 32 | run("git checkout develop") 33 | run("git pull") 34 | run("/home/ubuntu/.virtualenvs/oabutton/bin/pip install -r requirements.txt") 35 | run("sudo supervisorctl restart oabutton") 36 | -------------------------------------------------------------------------------- /oabutton/static/public/less/forms.less: -------------------------------------------------------------------------------- 1 | .form-bookmarklet { 2 | /* max-width: 300px; */ 3 | padding: 19px 29px 29px; 4 | margin: 0 auto 20px; 5 | background-color: #fff; 6 | border: 1px solid #e5e5e5; 7 | -webkit-border-radius: 5px; 8 | -moz-border-radius: 5px; 9 | border-radius: 5px; 10 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05); 11 | -moz-box-shadow: 0 1px 2px rgba(0,0,0,.05); 12 | box-shadow: 0 1px 2px rgba(0,0,0,.05); 13 | 14 | .form-bookmarklet-heading,.checkbox { 15 | margin-bottom: 10px; 16 | } 17 | 18 | input[type="text"], 19 | input[type="password"], 20 | select 21 | { 22 | font-size: 16px; 23 | height: auto; 24 | margin-bottom: 15px; 25 | padding: 7px 9px; 26 | width: 100%; 27 | } 28 | 29 | button 30 | { 31 | font-size: 16px; 32 | height: auto; 33 | margin-bottom: 15px; 34 | padding: 7px 9px; 35 | clear: both; 36 | width: 100%; 37 | } 38 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | --------------------- 3 | 4 | Copyright (c) 2013 Open Access Button 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | from views import form1, form2, form3 4 | from views import add_post 5 | from views import generate_bookmarklet 6 | from views import get_json 7 | from views import signin 8 | from views import xref_proxy 9 | 10 | urlpatterns = patterns('', 11 | # I think these 3 should be broken out to an API URL handler so we 12 | # can evolve it 13 | url(r'^$', get_json, name="get_json"), 14 | 15 | url(r'^form/page1/(?P.*)/$', form1, name="form1"), 16 | url(r'^form/page2/$', form2, name="form2"), 17 | url(r'^form/page3/$', form3, name="form3"), 18 | 19 | 20 | url(r'^post/$', add_post, name="add_post"), 21 | url(r'^signin/$', signin, name="signin"), 22 | url(r'^bookmarklet/(?P.*).js$', 23 | generate_bookmarklet, 24 | name="generate_bookmarklet"), 25 | url(r'^xref_proxy/(?P.*)', 26 | xref_proxy, 27 | name="xref_proxy"), 28 | ) 29 | -------------------------------------------------------------------------------- /oabutton/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for oabutton project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oabutton.settings") 19 | 20 | from django.core.wsgi import get_wsgi_application 21 | 22 | # This application object is used by any WSGI server configured to use this 23 | # file. This includes Django's development server, if the WSGI_APPLICATION 24 | # setting points here. 25 | 26 | from dj_static import Cling 27 | application = Cling(get_wsgi_application()) 28 | 29 | # Apply WSGI middleware here. 30 | # from helloworld.wsgi import HelloWorldApplication 31 | # application = HelloWorldApplication(application) 32 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | $PROVISION_SCRIPT = < 56 | {% endblock %} 57 | {% block body_attrs %}data-doi="{{doi|urlencode}}"{% endblock %} 58 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/templates/bookmarklet/page1.html: -------------------------------------------------------------------------------- 1 | {% extends "bookmarklet/layout.html" %} 2 | {% load oafilters %} 3 | {% block content %} 4 | 5 |
{% csrf_token %} 6 | 7 | {{ bookmarklet.non_field_errors }} 8 | {{ bookmarklet.accessed }} 9 | {{ bookmarklet.user_id }} 10 | 11 |
12 |

You've hit a paywall!?

13 |

That sucks.

14 |

Tell us why and we'll put you on the map.

15 |
16 | 17 | 18 |
19 | {{ bookmarklet.url|label_with_classes:"control-label"}} 20 |
21 | {{ bookmarklet.url }} 22 |
23 |
24 | 25 | 26 |
27 | {{ bookmarklet.location|label_with_classes:"control-label"}} 28 |
29 | {{ bookmarklet.location }} 30 | {{ bookmarklet.coords }} 31 |
32 |
33 |
34 | {{ bookmarklet.doi|label_with_classes:"control-label" }} 35 | {{ bookmarklet.doi.errors }} 36 |
37 | {{ bookmarklet.doi }} 38 |
39 |
40 |
41 | {{ bookmarklet.description|label_with_classes:"control-label" }} 42 | {{ bookmarklet.description.errors }} 43 | {{ bookmarklet.description }} 44 |
45 |
46 | {{ bookmarklet.story.errors }} 47 | {{ bookmarklet.story|label_with_classes:"control-label" }} 48 | {{ bookmarklet.story }} 49 |
50 |
51 |
52 | 53 |
54 |
55 | 56 |
57 | 58 | {% endblock %} 59 | 60 | {% block scripts %} 61 | 62 | {% endblock %} 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build 2 | Status](https://travis-ci.org/OAButton/OAButton.png?branch=develop)](https://travis-ci.org/OAButton/OAButton) 3 | 4 | ## Open Access Button Prototype 5 | 6 | A prototype version of the Open Access Button idea explained in [this blog](http://oabutton.wordpress.com/2013/07/06/our-project-short-version/). 7 | 8 | #### Demo 9 | 10 | [Have a look at the demo](http://oabutton.herokuapp.com/) - however **please do not share too widely as we're pre-launch!** 11 | 12 | #### Contributing 13 | 14 | If you want to contribute, we have some [instructions for setting up your local dev environment](https://github.com/OAButton/OAButton/blob/develop/INSTRUCTIONS.md). 15 | 16 | Our code is completely open source and we invite/need developers to get involved in this amazing project. Currently we're trying to focus on attaining [our minimum viable product](https://docs.google.com/document/d/1-5NV6PoUPiB8GmxRvIO0onDnv8bV69BeDGDco9uHxik/edit) before moving into our testing phase, provisionally on 1 October 2013. That should allow us to launch on 18 November 2013, in time for the [Berlin 11 Student & Early Stage Researcher Satellite Conference](http://berlin2013.sched.org/) on the occasion of the 10th anniversary of the [Berlin Declaration on Open Access to Knowledge in the Sciences and Humanities](http://openaccess.mpg.de/286432/Berlin-Declaration)! If you have suggestions, we are more than open to them, however, and post this date, we can think about broader development. 17 | 18 | As mentioned, we're working to a tight deadline and many people are counting on this tool launching in time, so please try to work on [high priority issues](https://github.com/OAButton/OAButton/issues). Also, we discuss the projects development more broadly on our [Google Group](http://groups.google.com/group/open-access-button). Where possible open ended questions should be made here, rather than in issues. 19 | 20 | #### Elsewhere on the internet 21 | 22 | * [Blog](http://oabutton.wordpress.com/) 23 | * [Twitter](https://twitter.com/OA_Button) 24 | * [Google Group](http://groups.google.com/group/open-access-button) 25 | * [Email](mailto:oabutton@gmail.com) 26 | 27 | Licensed under the [MIT license](https://github.com/OAButton/OAButton/blob/develop/LICENSE.md). Powered by [django](https://www.djangoproject.com/) and [MongoDB](http://www.mongodb.org). 28 | -------------------------------------------------------------------------------- /oabutton/apps/web/templates/web/layout.jade: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html(lang='en', itemscope, itemtype='http://schema.org/Organization') 3 | 4 | head 5 | meta(charset='utf-8') 6 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 7 | meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') 8 | meta(itemprop='name', content='Open Access Button') 9 | meta(itemprop='description', content='Paywalls deny people access to life-saving research every day. The Open Access Button will make the damage of paywalls visible to the world and get you access to the research you need.') 10 | meta(itemprop='image', content='/static/img/oabutton_logo.png') 11 | if title 12 | title Open Access Button - #{title} 13 | else 14 | title Open Access Button 15 | link(rel='shortcut icon', type='image/x-icon', href='/static/img/favicon.ico') 16 | 17 | link(rel='stylesheet', href='/static/css/bootstrap.min.css') 18 | link(rel='stylesheet', href='/static/css/leaflet.css') 19 | //if lte IE 8 20 | link(rel='stylesheet', href='/static/css/leaflet.ie.css') 21 | link(rel='stylesheet', type='text/css', media='all', href='/static/css/styles.css') 22 | 23 | script(src='//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js') 24 | script 25 | WebFont.load({ google: { families: ['Raleway'] }}); 26 | var events = {{ events|safe }}; 27 | 28 | //if lt IE 9 29 | script(src='/static/js/lib/html5shiv.js') 30 | script(src='/static/js/lib/respond.min.js') 31 | 32 | body(data-spy='scroll', data-target='.navbar') 33 | block body 34 | 35 | if DEBUG 36 | script(src='/static/js/lib/jquery.min.js') 37 | script(src='/static/js/lib/jquery.color-2.1.2.min.js') 38 | script(src='/static/js/lib/jquery.form.js') 39 | script(src='/static/js/lib/jquery.placeholder.js') 40 | script(src='/static/js/lib/bootstrap.min.js') 41 | script(src='/static/js/lib/bootstrap-dialog.js') 42 | script(src='/static/js/lib/leaflet.js') 43 | script(src='/static/js/lib/leaflet.markercluster.js') 44 | else 45 | script(src='/static/js/lib/all.min.js') 46 | 47 | // Application scripts 48 | script(src='/static/js/index.js') 49 | 50 | // Analytics 51 | script 52 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 53 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 54 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 55 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 56 | ga('create', 'UA-43774380-1', 'openaccessbutton.org'); 57 | ga('send', 'pageview'); 58 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/templates/bookmarklet/page2.html: -------------------------------------------------------------------------------- 1 | {% extends "bookmarklet/layout.html" %}{% block content %} 2 |
3 |

4 | Before we try and get you access, why not share your frustration - 5 | help make this problem visible! 6 |

7 | 8 |

9 | 10 |

11 | 12 |

13 | 14 |

15 |

16 |

17 |

18 |

19 |

20 |

21 |

22 |

23 |

24 | 25 |

26 |

27 |

28 | 29 |

30 | We're also on Facebook and Twitter! Join us! 31 |

32 |
33 | 34 |
{% csrf_token %} 35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 | 45 | 46 | 47 | 48 |
49 | {% endblock %} 50 | {% block scripts %} 51 | 52 | 59 | 66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/templates/bookmarklet/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Open Access Button 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |
24 | 31 |
32 |
33 |
34 | 35 | {% block footer %} 36 | 49 | {% endblock %} 50 | 51 | 52 | 62 | {% block scripts %} 63 | {% endblock %} 64 | 65 | 66 | -------------------------------------------------------------------------------- /oabutton/apps/web/templates/web/start.jade: -------------------------------------------------------------------------------- 1 | extends web/index 2 | - load oafilters 3 | block start 4 | form.form-bookmarklet#form-bookmarklet(action='/api/signin/', method='post') 5 | {% csrf_token %} 6 | .row 7 | .col-md-8.col-md-offset-2 8 | p Sign up below to get the 9 | i Open Access Button 10 | |, a safe, easy to use browser bookmarklet that you can use to show the global effects of research paywalls - and to help get access to the research you need. Every time you hit a paywall blocking your research, click the button. Fill out a short form, add your experience to the map along with thousands of others. Then use our tools to search for access to papers, and spread the word with social media. Every person who uses the 11 | i Open Access Button 12 | | brings us closer to changing the system. 13 | p 14 | a(data-toggle="modal", data-target="#video-modal", alt="Need access to research?", title="Learn more about the project in this video") 15 | | Watch the video 16 | | or 17 | a(href="http://oabutton.wordpress.com/2013/11/17/244/") read this 18 | | for a quick introduction, then sign up here: 19 | p 20 | {{ signin_form.email }} 21 | p 22 | {{ signin_form.name }} 23 | p 24 | {{ signin_form.profession }} 25 | .checkbox 26 | {{ signin_form.confirm_public}} 27 | {{ signin_form.confirm_public|label_with_classes:"confirm-label"}} 28 | .checkbox 29 | {{ signin_form.mailinglist}} 30 | {{ signin_form.mailinglist|label_with_classes:"mailinglist-label"}} 31 | p Your e-mail address 32 | u will not 33 | | be publicised or shared with third parties. 34 | p 35 | div.form-group 36 | {{ signin_form.privacy}} 37 | button.btn.btn-lg.btn-primary.btn-block(type='submit') 38 | | Get my personal Open Access Button 39 | 40 | #bookmarklet(style='display:none') 41 | .content 42 | i.wedge-up 43 | p 44 | | Drag me to your bookmark bar: 45 | a.btn.btn-lg.btn-primary 46 | i.icon-bookmark Open Access Button 47 | 48 | #help-bookmarklet 49 | .row 50 | .col-md-8.col-md-offset-2 51 | p 52 | b Thanks for signing up! 53 | | Don't forget to check your inbox for a verification email. 54 | p If you do not see a bookmark bar at the top of your browser, you can find instructions for activating yours here: 55 | a(href="https://support.google.com/chrome/answer/95745") Chrome 56 | |, 57 | a(href="http://support.mozilla.org/en-US/kb/bookmarks-toolbar-display-favorite-websites") Firefox 58 | |, 59 | a(href="http://support.apple.com/kb/ph4967") Safari 60 | |, 61 | a(href="http://www.cultofmac.com/36839/enabling-and-adding-to-the-bookmarks-bar-in-safari-for-ipad-how-to/") iPad 62 | |, 63 | a(href="http://windows.microsoft.com/en-us/windows-vista/show-or-hide-the-favorites-bar-in-internet-explorer-8") Internet Explorer 64 | |. For a 2-minute walkthrough on using the button (don't worry, it's easy!) - 65 | a(href="#start", data-dismiss="modal", data-toggle="modal", data-target="#video-modal") watch this video 66 | | or 67 | a(href="http://oabutton.wordpress.com/2013/11/17/244/") read here 68 | |. 69 | -------------------------------------------------------------------------------- /INSTRUCTIONS.md: -------------------------------------------------------------------------------- 1 | ## Getting started 2 | 3 | ### Requirements 4 | 5 | * [Python 2.6 or Python 2.7](http://www.python.org/getit/) 6 | * [pip](http://www.pip-installer.org/en/latest/installing.html) 7 | * [mongodb](http://docs.mongodb.org/manual/installation/) 8 | * [virtualenv](https://pypi.python.org/pypi/virtualenv) (optional but 9 | recommended) 10 | 11 | ### Installing 12 | 13 | Clone the [git repository][repo] and set up: 14 | ``` 15 | git clone https://github.com/OAButton/OAButton.git /path/to/repo 16 | cd /path/to/repo # Switch to the directory where 17 | # you've cloned the repo 18 | git checkout develop # Switch to the develop branch 19 | 20 | virtualenv oab-env # Set up a new virtualenv (optional) 21 | . oab-env/bin/activate # Activate the virtualenv (optional) 22 | 23 | pip install -r requirements.txt # Install dependencies 24 | 25 | flake8 --install-hook # Install the flake8 pre-commit hook 26 | # Checks your code for PEP8 compliance 27 | ``` 28 | 29 | ### Set your CORE API key 30 | 31 | ``` 32 | export CORE_API_KEY=blahblahblah 33 | ``` 34 | 35 | If you don't have a key, the app will still run, but you'll get 36 | warnings and communications with [CORE](http://core.kmi.open.ac.uk/) 37 | will fail. An API key isn't required to run the tests, as the API 38 | accesses are mocked out. 39 | 40 | ### Start mongodb 41 | 42 | If the mongodb daemon is not running yet, you can start it locally with 43 | ``` 44 | mongod --smallfiles -v 45 | ``` 46 | 47 | If you have installed MongoDB via your Debian/Ubuntu package mangager, do 48 | ``` 49 | sudo service mongodb start 50 | ``` 51 | 52 | ### Synchronise the database 53 | 54 | This will set up the database ready to use. You only have to do this once: 55 | ``` 56 | python manage.py syncdb 57 | ``` 58 | You will be prompted to create a superuser. Reply yes and follow the 59 | instructions. 60 | 61 | 62 | ### Start the webserver 63 | 64 | ``` 65 | python manage.py runserver 66 | ``` 67 | 68 | * Visit . Hooray! 69 | 70 | ### Development 71 | 72 | Use of virtualenv is highly recommended for development - it will 73 | ensure that all contributors are using a consistent set of packages. 74 | 75 | * You must be in your virtualenv before you start hacking on oabutton. 76 | * Remember to activate your virtualenv using (`source env/bin/activate`). 77 | * Check in settings.py that the line `from settings_local import *` 78 | is not commented out 79 | 80 | We are using LESSC to compile stylesheets from LESS to CSS. The compiler is 81 | run from a Makefile. Install Node.js then `npm install lessc -g`, then run 82 | `make` from the root folder to update CSS. 83 | 84 | ### Running the tests 85 | 86 | From the directory with the file manage.py in it 87 | ``` 88 | python manage.py test bookmarklet web 89 | ``` 90 | 91 | ### Heroku deployment 92 | 93 | * Ensure that settings.py imports from settings_heroku.py 94 | * Initial Postgresql Syncing of the database is done with: `heroku run python manage.py syncdb` 95 | * See: https://devcenter.heroku.com/articles/getting-started-with-django 96 | 97 | Required enviroment variables 98 | 99 | ## Getting started with a virtual machine 100 | 101 | If you're familiar with [Vagrant](http://vagrantup.com/) and virtual 102 | machines, there's a Vagrantfile included which will set up and 103 | provision a development VM for you. 104 | 105 | If you're not familiar, here's a lightning tutorial. After installing 106 | VirtualBox and Vagrant, and cloning the [git repository][repo]: 107 | 108 | 1. Open a command line and change to the directory where you cloned 109 | the repository 110 | 2. Start up the virtual machine: `vagrant up` 111 | * This will take a while the first time you do it, while it 112 | downloads the machine image and installs various prerequisites. 113 | 3. Log into the virtual machine via SSH: `vagrant ssh` 114 | * You should now have a commandline open in your virtual machine. 115 | 4. Change to the vagrant shared directory: `cd /vagrant` 116 | * The repository you cloned will automatically be mounted at 117 | `/vagrant`, so you can edit the files there using your favourite 118 | text editor *outside the VM* and all changes will be visible 119 | inside the VM as well. Just restart the server when you need 120 | to. 121 | 5. Activate the virtualenv environment: `source env/bin/activate` 122 | 6. Start the development server: `python manage.py runserver 123 | 192.168.33.10:8000` 124 | * Note the IP address on the end: if you omit this, 125 | 7. Point your web browser to 126 | 127 | When you've finished working, you can quit the webserver if it's still 128 | running (`CTRL-C`), log out of the virtual machine (`exit`) and then 129 | either: 130 | 131 | * Shut down the VM: `vagrant halt` 132 | * Suspend the VM to disk: `vagrant suspend` 133 | 134 | Then next time you're ready to work, follow the instructions above 135 | again: step 2 will take much less time as the VM is already installed 136 | and configured and just needs to boot. 137 | 138 | If you ever need to completely reset the VM, just delete it with 139 | `vagrant destroy` and start again. 140 | 141 | [repo]: http://github.com/OAButton/OAButton 142 | -------------------------------------------------------------------------------- /oabutton/static/public/js/success.js: -------------------------------------------------------------------------------- 1 | // This is just for successful submission to generate the xref links 2 | var oabSuccess = (function($) { 3 | 4 | function parseCrossRef(entry, doi) { 5 | var metadata = {'doi': doi}, 6 | item = entry["pam:message"]["pam:article"]; 7 | 8 | if (item["dc:title"]) { 9 | metadata["title"] = item["dc:title"]; 10 | } 11 | 12 | if (item["dc:creator"]) { 13 | if (!$.isArray(item["dc:creator"])) { 14 | item["dc:creator"] = [item["dc:creator"]]; 15 | } 16 | 17 | metadata["authors"] = item["dc:creator"].join(", "); 18 | } 19 | 20 | if (item["prism:publicationName"]) { 21 | metadata["publication"] = item["prism:publicationName"]; 22 | } 23 | 24 | if (item["prism:publicationDate"]) { 25 | // TODO: zero-pad or reformat the date, using Moment.js? 26 | metadata["date"] = item["prism:publicationDate"]; 27 | } 28 | 29 | return metadata; 30 | } 31 | 32 | function lookupCrossRef() { 33 | var doi = $('body').data('doi'); 34 | 35 | if (doi) { 36 | $.ajax({ 37 | url: 'http://data.crossref.org/' + encodeURIComponent(doi), 38 | dataType: "json", 39 | success: function(response) { 40 | if (response.feed.entry) { 41 | var metadata = parseCrossRef(response.feed.entry, doi); 42 | 43 | addPubMedCentralLink(metadata); 44 | addScholarDOILink(metadata); 45 | addScholarTitleLink(metadata); 46 | discoverCORELinks(metadata); 47 | } 48 | } 49 | }); 50 | } 51 | } 52 | 53 | function addPubMedCentralLink(metadata) { 54 | var doi = metadata["doi"]; 55 | 56 | if (doi) { 57 | $.ajax({ 58 | url: 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi', 59 | data: { 60 | db: "pmc", 61 | retmax: 1, 62 | term: doi + "[DOI]", 63 | tool: "oabutton" 64 | }, 65 | dataType: "xml", 66 | success: function(response) { 67 | if (response.getElementsByTagName('Count')[0].textContent === '0') { 68 | return; 69 | } 70 | 71 | var pmcid = response.getElementsByTagName('Id')[0].textContent; 72 | var url = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" + pmcid + "/"; 73 | var pmc_link = document.createElement("a"); 74 | pmc_link.setAttribute("class", "likeabutton btn btn-primary btn-block") 75 | pmc_link.setAttribute("href", url); 76 | pmc_link.setAttribute("target", "_blank"); 77 | pmc_link.innerHTML = "View on PubMedCentral"; 78 | 79 | 80 | $("#pmc_links").append(pmc_link); 81 | } 82 | }); 83 | } 84 | } 85 | 86 | function addScholarDOILink(metadata) { 87 | var doi = metadata["doi"]; 88 | 89 | if (doi) { 90 | var url = 'http://scholar.google.com/scholar?cluster=' + encodeURIComponent('http://dx.doi.org/' + doi); 91 | 92 | var sch_link = document.createElement("a"); 93 | sch_link.setAttribute("class", "btn btn-primary btn-block") 94 | sch_link.setAttribute("href", url); 95 | sch_link.setAttribute("target", "_blank"); 96 | sch_link.innerHTML = "Search Google Scholar by DOI"; 97 | 98 | $("#google_links").append(sch_link); 99 | } 100 | } 101 | 102 | function addScholarTitleLink(metadata) { 103 | var title = metadata["title"]; 104 | 105 | if (title) { 106 | var url = 'http://scholar.google.com/scholar?as_occt=title&as_q=' + encodeURIComponent(title); 107 | 108 | var sch_link = document.createElement("a"); 109 | sch_link.setAttribute("class", "btn btn-primary btn-block") 110 | sch_link.setAttribute("href", url); 111 | sch_link.setAttribute("target", "_blank"); 112 | sch_link.innerHTML = "Search Google Scholar by title"; 113 | 114 | $("#google_links").append(sch_link); 115 | } 116 | } 117 | 118 | function discoverCORELinks(metadata) { 119 | var title = metadata["title"]; 120 | 121 | if (title) { 122 | $.ajax({ 123 | url: "/metadata/coresearch.json/title:(" + encodeURIComponent(title) + ")", 124 | dataType: 'json', 125 | success: function(response) { 126 | var records = response.ListRecords; 127 | var $list = $('
    '); 128 | for (var i = 1; i < records.length; i++) { 129 | metadata = records[i]['record']['metadata']['oai_dc:dc']; 130 | $list.append('
  • ' 133 | + metadata['dc:creator'] 134 | + ' (' + metadata['dc:date'] + '); ' 135 | + metadata['dc:title'] 136 | + '
  • '); 137 | } 138 | 139 | $core_div = $('
    Matches from the CORE repository:
    '); 140 | $core_div.append($list); 141 | $("#core_links").append($core_div); 142 | } 143 | }); 144 | } 145 | } 146 | 147 | return { 148 | parseCrossRef: parseCrossRef, 149 | lookupCrossRef: lookupCrossRef, 150 | addScholarDOILink: addScholarDOILink, 151 | addScholarTitleLink: addScholarTitleLink, 152 | discoverCORELinks: discoverCORELinks, 153 | }; 154 | 155 | })(jQuery); 156 | 157 | 158 | $(document).ready(function() { 159 | oabSuccess.lookupCrossRef(); 160 | }); 161 | -------------------------------------------------------------------------------- /oabutton/apps/metadata/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | from django.test.client import Client 10 | import httpretty 11 | import os 12 | import json 13 | 14 | 15 | SAMPLE_ARTHRITIS = r'''{"ListRecords":[{"total_hits":5015},{"record":{"header":{"header:content":{"core:repositoryIdentifier":"143","identifier":"669636"},"header:attr":{"xmlns:core":"http:\/\/core.kmi.open.ac.uk\/api\/doc"}},"metadata":{"oai_dc:dc":{"oai_dc:ns":[{"xmlns:oai_dc":"http:\/\/www.openarchives.org\/OAI\/2.0\/oai_dc\/","xmlns:dc":"http:\/\/purl.org\/dc\/elements\/1.1\/"}],"dc:creator":"R Kudyar","dc:format":"application\/pdf","dc:source":"http:\/\/www.jkscience.org\/archive\/111\/18-RL-JACORD%20ARTHRITIS.pdf","dc:date":"2009","dc:identifier":"http:\/\/www.jkscience.org\/archive\/111\/18-RL-JACORD%20ARTHRITIS.pdf","dc:description":"A case of a patient with rheumatic valvulardisease who had comparable deformities of the handsand fingers and who fulfilled all of the criteriasuggested by Bywaters to describe Jaccoud's Arthritis is decribed here.","dc:title":"Jaccoud\u2019s Arthritis"}}}},{"record":{"header":{"header:content":{"core:repositoryIdentifier":"140","identifier":"60225"},"header:attr":{"xmlns:core":"http:\/\/core.kmi.open.ac.uk\/api\/doc"}},"metadata":{"oai_dc:dc":{"oai_dc:ns":[{"xmlns:oai_dc":"http:\/\/www.openarchives.org\/OAI\/2.0\/oai_dc\/","xmlns:dc":"http:\/\/purl.org\/dc\/elements\/1.1\/"}],"dc:creator":"Zai Liu, Guo Deng, Simon Foster and Andrej Tarkowski","dc:format":"application\/pdf","dc:source":"http:\/\/eprints.whiterose.ac.uk\/21\/1\/ar330.pdf","dc:date":"2001-09-17","dc:identifier":"http:\/\/eprints.whiterose.ac.uk\/21\/1\/ar330.pdf","dc:description":"Staphylococcus aureus is one of the most important pathogens in septic arthritis. To analyse the arthritogenic properties of staphylococcal peptidoglycan (PGN), highly purified PGN from S. aureus was intra-articularly injected into murine joints. The results demonstrate that PGN will trigger arthritis in a dose-dependent manner. A single injection of this compound leads to massive infiltration of predominantly macrophages and polymorphonuclear cells with occasional signs of cartilage and\/or bone destruction, lasting for at least 14 days. Further studies showed that this condition is mediated by the combined impact of acquired and innate immune systems. Our results indicate that PGN exerts a central role in joint inflammation triggered by S. aureus.","dc:title":"Staphylococcal peptidoglycans induce arthritis"}}}},{"record":{"header":{"header:content":{"core:repositoryIdentifier":"143","identifier":"5726004"},"header:attr":{"xmlns:core":"http:\/\/core.kmi.open.ac.uk\/api\/doc"}},"metadata":{"oai_dc:dc":{"oai_dc:ns":[{"xmlns:oai_dc":"http:\/\/www.openarchives.org\/OAI\/2.0\/oai_dc\/","xmlns:dc":"http:\/\/purl.org\/dc\/elements\/1.1\/"}],"dc:creator":"Karambin Mohammad Mehdi and Hashemian Hooman","dc:format":"application\/pdf","dc:source":"http:\/\/journals.tums.ac.ir\/PdfMed.aspx?pdf_med=\/upload_files\/pdf\/12751.pdf&manuscript_id=12751","dc:date":"2009","dc:identifier":"http:\/\/journals.tums.ac.ir\/PdfMed.aspx?pdf_med=\/upload_files\/pdf\/12751.pdf&manuscript_id=12751","dc:description":"To determine the rate of different types of arthritis in children. We prepared a retrospective descriptive study and included the whole 100 cases of arthritis referred to 17-Shahrivar Hospital, Rasht, Guilan during a 3 years period. Using their medical files, data including age, sex, season of admission, history of trauma, signs and symptoms, lab findings and duration of hospitalization were collected. SPSS 13.0 (statistical software) applied for statistical analysis. The most common age of involvement ranged 6-9 years. Septic arthritis, brucellosis, and rheumatoid fever were the most frequent causes of arthritis in our study. Fever and restricted range of motion had the highest rate among different signs and symptoms. Lab data demonstrated leukocytosis, positive CRP, and increased ESR among 74, 79.5, and 73 percent of our patients, respectively. According to the high prevalence of septic arthritis and the arthritis due to brucellosis and rheumatoid fever, it seems that mentioned diseases are still major problems in the issue of hygiene management.","dc:title":"Childhood Arthritis: Rate of Different Types"}}}}]}''' 16 | 17 | 18 | class SimpleTest(TestCase): 19 | 20 | def setUp(self): 21 | os.environ['CORE_API_KEY'] = 'Not really an API key' 22 | 23 | @httpretty.activate 24 | def test_core_search_success(self): 25 | """ 26 | Test that a successful request is proxied correctly 27 | """ 28 | httpretty.register_uri( 29 | httpretty.GET, 30 | "http://core.kmi.open.ac.uk/api/search/arthritis", 31 | body=SAMPLE_ARTHRITIS) 32 | 33 | c = Client() 34 | response = c.get('/metadata/coresearch.json/arthritis') 35 | 36 | self.assertEqual(response.status_code, 200) 37 | self.assertEqual(response.content, SAMPLE_ARTHRITIS) 38 | 39 | @httpretty.activate 40 | def test_core_search_failure(self): 41 | """ 42 | Test that a failed request is passed on 43 | """ 44 | httpretty.register_uri( 45 | httpretty.GET, 46 | "http://core.kmi.open.ac.uk/api/search/arthritis", 47 | body='ERROR', 48 | status=418) 49 | 50 | c = Client() 51 | response = c.get('/metadata/coresearch.json/arthritis') 52 | 53 | self.assertEqual(response.status_code, 502) 54 | 55 | response_info = json.loads(response.content) 56 | self.assertTrue('error' in response_info) 57 | self.assertEqual(response_info['error']['status'], 418) 58 | self.assertEqual(response_info['error']['content'], 'ERROR') 59 | -------------------------------------------------------------------------------- /oabutton/static/test/resources/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.12.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests li .runtime { 115 | float: right; 116 | font-size: smaller; 117 | } 118 | 119 | .qunit-assert-list { 120 | margin-top: 0.5em; 121 | padding: 0.5em; 122 | 123 | background-color: #fff; 124 | 125 | border-radius: 5px; 126 | -moz-border-radius: 5px; 127 | -webkit-border-radius: 5px; 128 | } 129 | 130 | .qunit-collapsed { 131 | display: none; 132 | } 133 | 134 | #qunit-tests table { 135 | border-collapse: collapse; 136 | margin-top: .2em; 137 | } 138 | 139 | #qunit-tests th { 140 | text-align: right; 141 | vertical-align: top; 142 | padding: 0 .5em 0 0; 143 | } 144 | 145 | #qunit-tests td { 146 | vertical-align: top; 147 | } 148 | 149 | #qunit-tests pre { 150 | margin: 0; 151 | white-space: pre-wrap; 152 | word-wrap: break-word; 153 | } 154 | 155 | #qunit-tests del { 156 | background-color: #e0f2be; 157 | color: #374e0c; 158 | text-decoration: none; 159 | } 160 | 161 | #qunit-tests ins { 162 | background-color: #ffcaca; 163 | color: #500; 164 | text-decoration: none; 165 | } 166 | 167 | /*** Test Counts */ 168 | 169 | #qunit-tests b.counts { color: black; } 170 | #qunit-tests b.passed { color: #5E740B; } 171 | #qunit-tests b.failed { color: #710909; } 172 | 173 | #qunit-tests li li { 174 | padding: 5px; 175 | background-color: #fff; 176 | border-bottom: none; 177 | list-style-position: inside; 178 | } 179 | 180 | /*** Passing Styles */ 181 | 182 | #qunit-tests li li.pass { 183 | color: #3c510c; 184 | background-color: #fff; 185 | border-left: 10px solid #C6E746; 186 | } 187 | 188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 189 | #qunit-tests .pass .test-name { color: #366097; } 190 | 191 | #qunit-tests .pass .test-actual, 192 | #qunit-tests .pass .test-expected { color: #999999; } 193 | 194 | #qunit-banner.qunit-pass { background-color: #C6E746; } 195 | 196 | /*** Failing Styles */ 197 | 198 | #qunit-tests li li.fail { 199 | color: #710909; 200 | background-color: #fff; 201 | border-left: 10px solid #EE5757; 202 | white-space: pre; 203 | } 204 | 205 | #qunit-tests > li:last-child { 206 | border-radius: 0 0 5px 5px; 207 | -moz-border-radius: 0 0 5px 5px; 208 | -webkit-border-bottom-right-radius: 5px; 209 | -webkit-border-bottom-left-radius: 5px; 210 | } 211 | 212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 213 | #qunit-tests .fail .test-name, 214 | #qunit-tests .fail .module-name { color: #000000; } 215 | 216 | #qunit-tests .fail .test-actual { color: #EE5757; } 217 | #qunit-tests .fail .test-expected { color: green; } 218 | 219 | #qunit-banner.qunit-fail { background-color: #EE5757; } 220 | 221 | 222 | /** Result */ 223 | 224 | #qunit-testresult { 225 | padding: 0.5em 0.5em 0.5em 2.5em; 226 | 227 | color: #2b81af; 228 | background-color: #D2E0E6; 229 | 230 | border-bottom: 1px solid white; 231 | } 232 | #qunit-testresult .module-name { 233 | font-weight: bold; 234 | } 235 | 236 | /** Fixture */ 237 | 238 | #qunit-fixture { 239 | position: absolute; 240 | top: -10000px; 241 | left: -10000px; 242 | width: 1000px; 243 | height: 1000px; 244 | } 245 | -------------------------------------------------------------------------------- /oabutton/static/public/js/lib/jquery.placeholder.js: -------------------------------------------------------------------------------- 1 | /*! http://mths.be/placeholder v2.0.7 by @mathias */ 2 | ;(function(window, document, $) { 3 | 4 | var isInputSupported = 'placeholder' in document.createElement('input'); 5 | var isTextareaSupported = 'placeholder' in document.createElement('textarea'); 6 | var prototype = $.fn; 7 | var valHooks = $.valHooks; 8 | var propHooks = $.propHooks; 9 | var hooks; 10 | var placeholder; 11 | 12 | if (isInputSupported && isTextareaSupported) { 13 | 14 | placeholder = prototype.placeholder = function() { 15 | return this; 16 | }; 17 | 18 | placeholder.input = placeholder.textarea = true; 19 | 20 | } else { 21 | 22 | placeholder = prototype.placeholder = function() { 23 | var $this = this; 24 | $this 25 | .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') 26 | .not('.placeholder') 27 | .bind({ 28 | 'focus.placeholder': clearPlaceholder, 29 | 'blur.placeholder': setPlaceholder 30 | }) 31 | .data('placeholder-enabled', true) 32 | .trigger('blur.placeholder'); 33 | return $this; 34 | }; 35 | 36 | placeholder.input = isInputSupported; 37 | placeholder.textarea = isTextareaSupported; 38 | 39 | hooks = { 40 | 'get': function(element) { 41 | var $element = $(element); 42 | 43 | var $passwordInput = $element.data('placeholder-password'); 44 | if ($passwordInput) { 45 | return $passwordInput[0].value; 46 | } 47 | 48 | return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; 49 | }, 50 | 'set': function(element, value) { 51 | var $element = $(element); 52 | 53 | var $passwordInput = $element.data('placeholder-password'); 54 | if ($passwordInput) { 55 | return $passwordInput[0].value = value; 56 | } 57 | 58 | if (!$element.data('placeholder-enabled')) { 59 | return element.value = value; 60 | } 61 | if (value == '') { 62 | element.value = value; 63 | // Issue #56: Setting the placeholder causes problems if the element continues to have focus. 64 | if (element != safeActiveElement()) { 65 | // We can't use `triggerHandler` here because of dummy text/password inputs :( 66 | setPlaceholder.call(element); 67 | } 68 | } else if ($element.hasClass('placeholder')) { 69 | clearPlaceholder.call(element, true, value) || (element.value = value); 70 | } else { 71 | element.value = value; 72 | } 73 | // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 74 | return $element; 75 | } 76 | }; 77 | 78 | if (!isInputSupported) { 79 | valHooks.input = hooks; 80 | propHooks.value = hooks; 81 | } 82 | if (!isTextareaSupported) { 83 | valHooks.textarea = hooks; 84 | propHooks.value = hooks; 85 | } 86 | 87 | $(function() { 88 | // Look for forms 89 | $(document).delegate('form', 'submit.placeholder', function() { 90 | // Clear the placeholder values so they don't get submitted 91 | var $inputs = $('.placeholder', this).each(clearPlaceholder); 92 | setTimeout(function() { 93 | $inputs.each(setPlaceholder); 94 | }, 10); 95 | }); 96 | }); 97 | 98 | // Clear placeholder values upon page reload 99 | $(window).bind('beforeunload.placeholder', function() { 100 | $('.placeholder').each(function() { 101 | this.value = ''; 102 | }); 103 | }); 104 | 105 | } 106 | 107 | function args(elem) { 108 | // Return an object of element attributes 109 | var newAttrs = {}; 110 | var rinlinejQuery = /^jQuery\d+$/; 111 | $.each(elem.attributes, function(i, attr) { 112 | if (attr.specified && !rinlinejQuery.test(attr.name)) { 113 | newAttrs[attr.name] = attr.value; 114 | } 115 | }); 116 | return newAttrs; 117 | } 118 | 119 | function clearPlaceholder(event, value) { 120 | var input = this; 121 | var $input = $(input); 122 | if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { 123 | if ($input.data('placeholder-password')) { 124 | $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); 125 | // If `clearPlaceholder` was called from `$.valHooks.input.set` 126 | if (event === true) { 127 | return $input[0].value = value; 128 | } 129 | $input.focus(); 130 | } else { 131 | input.value = ''; 132 | $input.removeClass('placeholder'); 133 | input == safeActiveElement() && input.select(); 134 | } 135 | } 136 | } 137 | 138 | function setPlaceholder() { 139 | var $replacement; 140 | var input = this; 141 | var $input = $(input); 142 | var id = this.id; 143 | if (input.value == '') { 144 | if (input.type == 'password') { 145 | if (!$input.data('placeholder-textinput')) { 146 | try { 147 | $replacement = $input.clone().attr({ 'type': 'text' }); 148 | } catch(e) { 149 | $replacement = $('').attr($.extend(args(this), { 'type': 'text' })); 150 | } 151 | $replacement 152 | .removeAttr('name') 153 | .data({ 154 | 'placeholder-password': $input, 155 | 'placeholder-id': id 156 | }) 157 | .bind('focus.placeholder', clearPlaceholder); 158 | $input 159 | .data({ 160 | 'placeholder-textinput': $replacement, 161 | 'placeholder-id': id 162 | }) 163 | .before($replacement); 164 | } 165 | $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); 166 | // Note: `$input[0] != input` now! 167 | } 168 | $input.addClass('placeholder'); 169 | $input[0].value = $input.attr('placeholder'); 170 | } else { 171 | $input.removeClass('placeholder'); 172 | } 173 | } 174 | 175 | function safeActiveElement() { 176 | // Avoid IE9 `document.activeElement` of death 177 | // https://github.com/mathiasbynens/jquery-placeholder/pull/99 178 | try { 179 | return document.activeElement; 180 | } catch (err) {} 181 | } 182 | 183 | }(this, document, jQuery)); 184 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/templates/bookmarklet/bookmarklet.html: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Detect a DOI for the article 3 | var detectDOI = function() { 4 | var nodes, node, childNode, matches, i, j; 5 | 6 | // match DOI: test on http://t.co/eIJciunBRJ 7 | var doi_re = /\b10\.\d{4,}(?:\.\d+)*\/\S+\b/; 8 | 9 | // look for meta[name=citation_doi][content] 10 | nodes = document.getElementsByTagName("meta"); 11 | for (i = 0; i < nodes.length; i++) { 12 | node = nodes[i]; 13 | 14 | if (node.getAttribute("name") == "citation_doi") { 15 | return node.getAttribute("content").replace(/^doi:/, ""); 16 | } 17 | } 18 | 19 | // look in all text nodes for a DOI 20 | nodes = document.getElementsByTagName("*"); 21 | for (i = 0; i < nodes.length; i++) { 22 | node = nodes[i]; 23 | 24 | if (!node.hasChildNodes()) { 25 | continue; 26 | } 27 | 28 | if (node.nodeName == "SCRIPT") { 29 | continue; 30 | } 31 | 32 | for (j = 0; j < node.childNodes.length; j++) { 33 | childNode = node.childNodes[j]; 34 | 35 | // only text nodes 36 | if (childNode.nodeType !== 3) { 37 | continue; 38 | } 39 | 40 | if (matches = doi_re.exec(childNode.nodeValue)) { 41 | return matches[0]; 42 | } 43 | } 44 | } 45 | 46 | return null; 47 | }; 48 | 49 | // Detect an email address for the corresponding author. 50 | var detectAuthorEmail = function() { 51 | var nodes, node, matches, i; 52 | 53 | // match email address in mailto link 54 | // from http://stackoverflow.com/a/201447/145899 55 | var mailto_re = /^mailto:(\S+@\S+\.\S+)/; 56 | 57 | // look for meta[name=citation_author_email][content] 58 | // test on http://dx.doi.org/10.1007/978-3-642-02879-3_7 59 | nodes = document.getElementsByTagName("meta"); 60 | for (i = 0; i < nodes.length; i++) { 61 | node = nodes[i]; 62 | 63 | if (node.getAttribute("name") == "citation_author_email") { 64 | return node.getAttribute("content"); 65 | } 66 | } 67 | 68 | // look for links that start with "mailto:". 69 | // can't guarantee this is the author - might be an editor or support email. 70 | // test on http://dx.doi.org/10.1371/journal.pone.0052814 71 | nodes = document.getElementsByTagName("a"); 72 | for (i = 0; i < nodes.length; i++) { 73 | node = nodes[i]; 74 | 75 | if (matches = mailto_re.exec(node.getAttribute("href"))) { 76 | return matches[1].replace(/\?.*/, ""); // remove any query string 77 | } 78 | } 79 | 80 | return null; 81 | }; 82 | 83 | // ascertain if the bookmarklet has already been called 84 | var exists = document.getElementById("OAButton"); 85 | var email = detectAuthorEmail(); 86 | console.log("Got email: " + email); 87 | 88 | if(exists == null) 89 | { 90 | 91 | // get the base URL 92 | var loader = document.body.lastChild; 93 | var base = loader.getAttribute("src").match(/^https?:\/\/[^/]+/)[0]; 94 | loader.parentNode.removeChild(loader); 95 | 96 | // build the iframe URL 97 | // Custom URL for user: [{{user_id}}] 98 | var url = base + "/api/form/page1/{{ user_id }}/?url=" + encodeURIComponent(window.location); 99 | var doi = detectDOI(); 100 | if(doi) { 101 | url += "&doi=" + encodeURIComponent(doi); 102 | } 103 | if (email) { 104 | url += "&email=" + encodeURIComponent(email); 105 | } 106 | 107 | // build the control div 108 | var div = document.createElement("div"); 109 | div.setAttribute("allowTransparency", "true"); 110 | div.setAttribute("id", "OAButton"); 111 | 112 | div.style.position = "fixed"; 113 | div.style.zIndex = "2147483640"; 114 | div.style.boxSizing = "border-box"; 115 | div.style.MozBoxSizing = "border-box"; 116 | div.style.padding = "15px"; 117 | div.style.background = "white"; 118 | div.style.height = "100%"; 119 | div.style.width = "350px"; 120 | div.style.top = "0"; 121 | div.style.right = "0"; 122 | div.style.overflow = "scroll"; 123 | div.style.overflowX = "hidden"; 124 | 125 | document.body.appendChild(div); 126 | 127 | // add the close button 128 | var closeButton = document.createElement("a"); 129 | closeButton.setAttribute("href", "javascript:document.getElementById('OAButton').setAttribute('style', 'display:none')"); 130 | closeButton.setAttribute("id", "closeButton"); 131 | closeButton.appendChild(document.createTextNode("X")); 132 | closeButton.style.zIndex = "2147483641"; 133 | closeButton.style.position = "relative"; 134 | closeButton.style.top = "0"; 135 | 136 | div.appendChild(closeButton); 137 | 138 | // add the iframe 139 | var iframe = document.createElement("iframe"); 140 | iframe.setAttribute("allowTransparency", "true"); 141 | iframe.setAttribute("src", url); 142 | 143 | iframe.style.position = "fixed"; 144 | iframe.style.zIndex = "2147483640"; 145 | iframe.style.boxSizing = "border-box"; 146 | iframe.style.MozBoxSizing = "border-box"; 147 | iframe.style.padding = "15px"; 148 | iframe.style.borderLeft = "2px #555 dashed"; 149 | iframe.style.background = "white"; 150 | iframe.style.height = "100%"; 151 | iframe.style.width = "350px"; 152 | iframe.style.bottom = "0"; 153 | iframe.style.right = "0"; 154 | 155 | div.appendChild(iframe); 156 | } else { 157 | var div = exists; 158 | div.setAttribute("allowTransparency", "true"); 159 | div.setAttribute("id", "OAButton"); 160 | 161 | div.style.position = "fixed"; 162 | div.style.zIndex = "2147483640"; 163 | div.style.boxSizing = "border-box"; 164 | div.style.MozBoxSizing = "border-box"; 165 | div.style.padding = "15px"; 166 | div.style.background = "white"; 167 | div.style.height = "100%"; 168 | div.style.width = "350px"; 169 | div.style.top = "0"; 170 | div.style.right = "0"; 171 | div.style.overflow = "scroll"; 172 | div.style.overflowX = "hidden"; 173 | div.style.display = "block"; 174 | 175 | } 176 | })(); 177 | 178 | -------------------------------------------------------------------------------- /oabutton/static/public/js/add.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | function getLocation() { 4 | if (navigator.geolocation) { 5 | navigator.geolocation.getCurrentPosition(showPosition, denyAccess); 6 | } 7 | } 8 | 9 | function showPosition(position) { 10 | rounded_lat = Math.round(position.coords.latitude * 10) / 10; 11 | rounded_long = Math.round(position.coords.longitude * 10) / 10; 12 | $('#id_coords').val([rounded_lat, rounded_long]); 13 | $('#id_location').attr({'placeholder': 'Detected from browser', 'required': false, 'readonly': 'readonly'}); 14 | } 15 | 16 | function denyAccess(error) { 17 | if (error.code == error.PERMISSION_DENIED) 18 | console.log("Geolocation denied"); 19 | } 20 | 21 | getLocation(); 22 | 23 | function pmc() { 24 | var doi = $('#id_doi').val(); 25 | 26 | if (doi) { 27 | $.ajax({ 28 | url: 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi', 29 | data: { 30 | db: "pmc", 31 | retmax: 1, 32 | term: doi + "[DOI]", 33 | tool: "oabutton" 34 | }, 35 | dataType: "xml", 36 | success: function(response) { 37 | if (response.getElementsByTagName('Count')[0].textContent === '0') { 38 | return; 39 | } 40 | 41 | var pmcid = response.getElementsByTagName('Id')[0].textContent; 42 | var url = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" + pmcid + "/"; 43 | $("#id_pmc").attr("href", url).show(); 44 | } 45 | }); 46 | } 47 | } 48 | 49 | pmc(); 50 | 51 | function parseCrossRef(item) { 52 | var description = {}; 53 | 54 | if (item.title) { 55 | description['Title'] = item.title; 56 | } 57 | 58 | if (item.author) { 59 | if (!$.isArray(item.author)) { 60 | item.author = [item.author]; 61 | } 62 | 63 | var authors = []; 64 | $.each(item.author, function(index, author) { 65 | var name = [author.given, author.family]; 66 | authors.push(name.join(" ")); 67 | }); 68 | 69 | description['Authors'] = authors.join(", "); 70 | } 71 | 72 | if (item["container-title"]) { 73 | description['Journal'] = item["container-title"]; 74 | } 75 | 76 | if (item.issued && item.issued["date-parts"]) { 77 | // TODO: zero-pad or reformat the date, using Moment.js? 78 | description['Date'] = item.issued["date-parts"][0].join("-"); 79 | } 80 | 81 | description = $.map(description, function(value, key) { 82 | return key + ": " + value; 83 | }); 84 | 85 | return description.join("\n"); 86 | } 87 | 88 | function lookup() { 89 | var doi = $('#id_doi').val(); 90 | 91 | if (doi) { 92 | $.ajax({ 93 | url: '/api/xref_proxy/' + encodeURIComponent(doi), 94 | dataType: "json", 95 | success: function(response) { 96 | if (response && response.URL) { 97 | $('#id_description').val(parseCrossRef(response)); 98 | } 99 | } 100 | }); 101 | } 102 | } 103 | 104 | lookup(); 105 | 106 | // save details in localstorage 107 | function rememberDetails() { 108 | // test for localstorage support (from Modernizr) 109 | // fails if third-party data is not allowed (in an iframe) 110 | try { 111 | localStorage.setItem('test', 'test'); 112 | localStorage.removeItem('test'); 113 | } catch(e) { 114 | return; 115 | } 116 | 117 | var rememberMeInput = $('#id_remember'), 118 | rememberNodes = $('[data-remember]'); 119 | 120 | var stored = localStorage.getItem('id_remember'); 121 | 122 | // can't always store booleans in localstorage, so "false" (string) = "don't store" 123 | var remember = stored === null || stored === true || stored.toString() === 'true'; 124 | 125 | if (remember) { 126 | rememberMeInput.prop('checked', true); 127 | } 128 | 129 | rememberMeInput.parent().show(); 130 | 131 | // store/load details 132 | rememberNodes 133 | // save the details 134 | .on('change', function(){ 135 | if (!remember) { 136 | return; 137 | } 138 | 139 | localStorage.setItem(this.getAttribute('id'), $(this).val()); 140 | }) 141 | // load the details 142 | .each(function() { 143 | $(this).val(localStorage.getItem(this.getAttribute('id'))); 144 | }); 145 | 146 | // when "remember me" is unchecked, delete stored details 147 | rememberMeInput.on('change', function() { 148 | remember = rememberMeInput.prop('checked'); 149 | localStorage.setItem('id_remember', remember); 150 | 151 | if (remember) { 152 | // save the current values 153 | rememberNodes.trigger('change'); 154 | } else { 155 | // remove the stored values 156 | rememberNodes.each(function() { 157 | localStorage.removeItem(this.getAttribute('id')); 158 | }); 159 | } 160 | }); 161 | } 162 | 163 | rememberDetails(); 164 | 165 | function geocode(form) { 166 | return $.ajax({ 167 | url: 'https://maps.googleapis.com/maps/api/geocode/json', 168 | data: { 169 | sensor: 'false', 170 | address: $('#id_location').val() 171 | }, 172 | success: function(response) { 173 | if (response.results.length == 0) { 174 | alert("You location could not be identified, please try again!"); 175 | } else { 176 | var location = response.results[0].geometry.location; 177 | $('#id_coords').val([location.lat, location.lng]); 178 | form.submit(); 179 | } 180 | }, 181 | error: function(jqXHR, textStatus, errorThrown) { 182 | console.log(jqXHR, textStatus, errorThrown); 183 | } 184 | }); 185 | } 186 | 187 | function onSubmit(event) { 188 | var form = $(this); 189 | 190 | // Do geocoding only if needed 191 | if (!$('#id_coords').val()) { 192 | event.preventDefault(); 193 | geocode(form); 194 | } 195 | } 196 | 197 | $('#id_accessed').val(new Date().toISOString()); 198 | $('form').on("submit", onSubmit); 199 | }); 200 | -------------------------------------------------------------------------------- /oabutton/static/public/js/lib/jquery.color-2.1.2.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Color v@2.1.2 http://github.com/jquery/jquery-color | jquery.org/license */ 2 | (function(a,b){function m(a,b,c){var d=h[b.type]||{};return a==null?c||!b.def?null:b.def:(a=d.floor?~~a:parseFloat(a),isNaN(a)?b.def:d.mod?(a+d.mod)%d.mod:0>a?0:d.max")[0],k,l=a.each;j.style.cssText="background-color:rgba(1,1,1,.5)",i.rgba=j.style.backgroundColor.indexOf("rgba")>-1,l(g,function(a,b){b.cache="_"+a,b.props.alpha={idx:3,type:"percent",def:1}}),f.fn=a.extend(f.prototype,{parse:function(c,d,e,h){if(c===b)return this._rgba=[null,null,null,null],this;if(c.jquery||c.nodeType)c=a(c).css(d),d=b;var i=this,j=a.type(c),o=this._rgba=[];d!==b&&(c=[c,d,e,h],j="array");if(j==="string")return this.parse(n(c)||k._default);if(j==="array")return l(g.rgba.props,function(a,b){o[b.idx]=m(c[b.idx],b)}),this;if(j==="object")return c instanceof f?l(g,function(a,b){c[b.cache]&&(i[b.cache]=c[b.cache].slice())}):l(g,function(b,d){var e=d.cache;l(d.props,function(a,b){if(!i[e]&&d.to){if(a==="alpha"||c[a]==null)return;i[e]=d.to(i._rgba)}i[e][b.idx]=m(c[a],b,!0)}),i[e]&&a.inArray(null,i[e].slice(0,3))<0&&(i[e][3]=1,d.from&&(i._rgba=d.from(i[e])))}),this},is:function(a){var b=f(a),c=!0,d=this;return l(g,function(a,e){var f,g=b[e.cache];return g&&(f=d[e.cache]||e.to&&e.to(d._rgba)||[],l(e.props,function(a,b){if(g[b.idx]!=null)return c=g[b.idx]===f[b.idx],c})),c}),c},_space:function(){var a=[],b=this;return l(g,function(c,d){b[d.cache]&&a.push(c)}),a.pop()},transition:function(a,b){var c=f(a),d=c._space(),e=g[d],i=this.alpha()===0?f("transparent"):this,j=i[e.cache]||e.to(i._rgba),k=j.slice();return c=c[e.cache],l(e.props,function(a,d){var e=d.idx,f=j[e],g=c[e],i=h[d.type]||{};if(g===null)return;f===null?k[e]=g:(i.mod&&(g-f>i.mod/2?f+=i.mod:f-g>i.mod/2&&(f-=i.mod)),k[e]=m((g-f)*b+f,d))}),this[d](k)},blend:function(b){if(this._rgba[3]===1)return this;var c=this._rgba.slice(),d=c.pop(),e=f(b)._rgba;return f(a.map(c,function(a,b){return(1-d)*e[b]+d*a}))},toRgbaString:function(){var b="rgba(",c=a.map(this._rgba,function(a,b){return a==null?b>2?1:0:a});return c[3]===1&&(c.pop(),b="rgb("),b+c.join()+")"},toHslaString:function(){var b="hsla(",c=a.map(this.hsla(),function(a,b){return a==null&&(a=b>2?1:0),b&&b<3&&(a=Math.round(a*100)+"%"),a});return c[3]===1&&(c.pop(),b="hsl("),b+c.join()+")"},toHexString:function(b){var c=this._rgba.slice(),d=c.pop();return b&&c.push(~~(d*255)),"#"+a.map(c,function(a){return a=(a||0).toString(16),a.length===1?"0"+a:a}).join("")},toString:function(){return this._rgba[3]===0?"transparent":this.toRgbaString()}}),f.fn.parse.prototype=f.fn,g.hsla.to=function(a){if(a[0]==null||a[1]==null||a[2]==null)return[null,null,null,a[3]];var b=a[0]/255,c=a[1]/255,d=a[2]/255,e=a[3],f=Math.max(b,c,d),g=Math.min(b,c,d),h=f-g,i=f+g,j=i*.5,k,l;return g===f?k=0:b===f?k=60*(c-d)/h+360:c===f?k=60*(d-b)/h+120:k=60*(b-c)/h+240,h===0?l=0:j<=.5?l=h/i:l=h/(2-i),[Math.round(k)%360,l,j,e==null?1:e]},g.hsla.from=function(a){if(a[0]==null||a[1]==null||a[2]==null)return[null,null,null,a[3]];var b=a[0]/360,c=a[1],d=a[2],e=a[3],f=d<=.5?d*(1+c):d+c-d*c,g=2*d-f;return[Math.round(o(g,f,b+1/3)*255),Math.round(o(g,f,b)*255),Math.round(o(g,f,b-1/3)*255),e]},l(g,function(c,e){var g=e.props,h=e.cache,i=e.to,j=e.from;f.fn[c]=function(c){i&&!this[h]&&(this[h]=i(this._rgba));if(c===b)return this[h].slice();var d,e=a.type(c),k=e==="array"||e==="object"?c:arguments,n=this[h].slice();return l(g,function(a,b){var c=k[e==="object"?a:b.idx];c==null&&(c=n[b.idx]),n[b.idx]=m(c,b)}),j?(d=f(j(n)),d[h]=n,d):f(n)},l(g,function(b,e){if(f.fn[b])return;f.fn[b]=function(f){var g=a.type(f),h=b==="alpha"?this._hsla?"hsla":"rgba":c,i=this[h](),j=i[e.idx],k;return g==="undefined"?j:(g==="function"&&(f=f.call(this,j),g=a.type(f)),f==null&&e.empty?this:(g==="string"&&(k=d.exec(f),k&&(f=j+parseFloat(k[2])*(k[1]==="+"?1:-1))),i[e.idx]=f,this[h](i)))}})}),f.hook=function(b){var c=b.split(" ");l(c,function(b,c){a.cssHooks[c]={set:function(b,d){var e,g,h="";if(d!=="transparent"&&(a.type(d)!=="string"||(e=n(d)))){d=f(e||d);if(!i.rgba&&d._rgba[3]!==1){g=c==="backgroundColor"?b.parentNode:b;while((h===""||h==="transparent")&&g&&g.style)try{h=a.css(g,"backgroundColor"),g=g.parentNode}catch(j){}d=d.blend(h&&h!=="transparent"?h:"_default")}d=d.toRgbaString()}try{b.style[c]=d}catch(j){}}},a.fx.step[c]=function(b){b.colorInit||(b.start=f(b.elem,c),b.end=f(b.end),b.colorInit=!0),a.cssHooks[c].set(b.elem,b.start.transition(b.end,b.pos))}})},f.hook(c),a.cssHooks.borderColor={expand:function(a){var b={};return l(["Top","Right","Bottom","Left"],function(c,d){b["border"+d+"Color"]=a}),b}},k=a.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}})(jQuery); -------------------------------------------------------------------------------- /oabutton/static/public/js/index.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | var oabutton = { 4 | renderMap : function () { 5 | if (typeof L == 'undefined') return; 6 | 7 | var map = L.map('map', { 8 | center: [20, 0], 9 | maxZoom: 18, 10 | minZoom: 2, 11 | scrollWheelZoom: false, 12 | touchZoom: false, 13 | zoom: 2 14 | }); 15 | L.tileLayer('http://{s}.tile.cloudmade.com/cee9bfb83d854a2f89a4a2445aa9f595/997/256/{z}/{x}/{y}.png', { 16 | attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade' 17 | }).addTo(map); 18 | 19 | var oaIcon = L.icon({ 20 | iconUrl: 'static/img/noalogo.png', 21 | iconSize: [12, 19], 22 | iconAnchor: [6, 9], 23 | popupAnchor: [0, -12] 24 | }); 25 | 26 | var markers = new L.MarkerClusterGroup(); 27 | for (var i = 0; i < events.length; i++) { 28 | var evt = events[i]; 29 | if(evt.coords.lat && evt.coords.lng) { 30 | bubble_content = '

    ' + evt.user_name; 31 | if (evt.user_profession != '') { 32 | bubble_content += ' (' + evt.user_profession + ')'; 33 | } 34 | bubble_content += '

    '; 35 | bubble_content += '

    ' + evt.story + '

    '; 36 | bubble_content += 'doi'; 37 | bubble_content += ' | '; 38 | bubble_content += 'url'; 39 | bubble_content += ' | '; 40 | bubble_content += evt.accessed || ''; 41 | 42 | var marker = new L.marker([evt.coords.lat, evt.coords.lng], { title: bubble_content, icon: oaIcon }); 43 | marker.bindPopup(bubble_content); 44 | 45 | markers.addLayer(marker); 46 | } 47 | } 48 | map.addLayer(markers); 49 | } 50 | } 51 | 52 | // Highlight some node. This ought to be moved to some common JS 53 | $.fn.animateHighlight = function(highlightColor, duration) { 54 | var highlightBg = highlightColor || "#FFFF9C"; 55 | var animateMs = duration || 1500; 56 | var originalBg = this.css("backgroundColor"); 57 | this.stop().css("background-color", 58 | highlightBg).animate({backgroundColor: 59 | originalBg}, animateMs); 60 | }; 61 | 62 | // Bind submission form 63 | $('#form-bookmarklet').ajaxForm({ 64 | target: '#divToUpdate', 65 | dataType: 'json', 66 | url: '/api/signin/', 67 | error: function(ctx) { 68 | var errors = JSON.parse(ctx.responseText).errors; 69 | 70 | if ('email' in errors) { 71 | $('#id_email').animateHighlight("#dd0000", 1000); 72 | } 73 | 74 | if ('name' in errors) { 75 | $('#id_name').animateHighlight("#dd0000", 1000); 76 | } 77 | 78 | if ('confirm_public' in errors) { 79 | $('label.confirm-label').animateHighlight("#dd0000", 1000); 80 | } 81 | }, 82 | success: function(responseJSON, statusText, xhr, formElem) { 83 | 84 | // Set URL from service response 85 | var bookmarklet = $('#bookmarklet .content'); 86 | $('.btn-primary', bookmarklet).attr('href', 87 | "javascript:document.getElementsByTagName('body')[0]" + 88 | ".appendChild(document.createElement('script'))" + 89 | ".setAttribute('src','"+responseJSON['url']+"');" 90 | ); 91 | 92 | // Show the new bookmarklet and instructions, roll up form 93 | $('#bookmarklet').show(); 94 | $('#form-bookmarklet').slideUp(); 95 | 96 | window.location = '#top'; 97 | $('#help-bookmarklet').fadeIn(); 98 | 99 | } // -success 100 | }); // -ajaxForm 101 | 102 | // Scrollspy effects (unused) 103 | //$('body').on('activate.bs.scrollspy', function (e) { 104 | //console.log(e); 105 | //}); 106 | 107 | // Show the map underlay (later as effect) 108 | $('#map .underlay').fadeIn(); 109 | 110 | // Stop video on close dialog 111 | $('#video-modal').on('hidden.bs.modal', function () { 112 | $('iframe').each(function() { 113 | this.contentWindow.postMessage( 114 | '{"event":"command","func":"pauseVideo","args":""}', '*' 115 | ); 116 | }); 117 | }); 118 | 119 | // Use JQuery Placeholder plugin on older browsers (ex. IE 9) 120 | $('#form-bookmarklet input[type="text"]').placeholder(); 121 | 122 | // Position the thumbnail hovers 123 | var midpoint = $('.thumbnails').width() / 2; 124 | $('.thumbnails li').each(function() { 125 | if ($(this).position().left >= midpoint) { 126 | $(this).addClass("right"); 127 | } 128 | }); 129 | 130 | // Tap as hover on touchscreens 131 | $('.thumbnails a.thumb').bind('touchstart', function(e){ 132 | $(this).parent().addClass('hover'); 133 | e.stopPropagation(); 134 | }).bind('touchend', function(e){ 135 | $(this).parent().parent().find('.hover').removeClass('hover'); 136 | e.stopPropagation(); 137 | }).css('visibility', 'visible'); // Avoids doubles while loading JS 138 | oabutton.renderMap(); 139 | 140 | // Page links should all be new window 141 | $('a[href^="http"]').attr('target', '_blank'); 142 | }); 143 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | from django.test.client import Client 10 | from nose.tools import eq_, ok_ 11 | from oabutton.apps.bookmarklet.models import Event, User 12 | import datetime 13 | import json 14 | import re 15 | 16 | 17 | class APITest(TestCase): 18 | def setUp(self): 19 | """ 20 | verify that the JSON emitted is compatible with the javascript 21 | map stuff 22 | """ 23 | # check that we have all the signin fields 24 | self.EMAIL = 'new_email@foo.com' 25 | self.POST_DATA = {u'email': self.EMAIL, 26 | 'name': 'some name', 27 | 'profession': 'Student', 28 | 'confirm_public': 'checked', 29 | 'mailinglist': 'checked'} 30 | 31 | for user in User.objects.filter(username=self.EMAIL): 32 | user.delete() 33 | 34 | c = Client() 35 | response = c.post('/api/signin/', self.POST_DATA) 36 | 37 | eq_(response.status_code, 200) 38 | self.user = User.objects.get(username=self.EMAIL) 39 | 40 | eq_(self.user.name, 'some name') 41 | eq_(self.user.email, self.EMAIL) 42 | eq_(self.user.profession, 'Student') 43 | ok_(self.user.mailinglist) 44 | 45 | def test_add_post(self): 46 | ''' 47 | We need to make sure all fields of the Event object are 48 | serialized back to MongoDB 49 | ''' 50 | POST_DATA = {u'story': [u'some access requirement'], 51 | u'doi': [u'10.1016/j.urology.2010.05.009.'], 52 | u'url': [u'http://www.ncbi.nlm.nih.gov/pubmed/20709373'], 53 | u'coords': [u'44,-79.5'], 54 | u'location': [u'Somewhere'], 55 | u'accessed': [u'Mon, 09 Sep 2013 14:54:42 GMT'], 56 | u'description': [u'some description'], 57 | u'user_id': self.user.id, } 58 | 59 | c = Client() 60 | response = c.post('/api/post/', POST_DATA) 61 | 62 | assert response.status_code == 302 63 | 64 | evt = Event.objects.get(id=c.session['data']['event_id']) 65 | 66 | expected = {'doi': u'10.1016/j.urology.2010.05.009.', 67 | 'url': u'http://www.ncbi.nlm.nih.gov/pubmed/20709373', 68 | 'coords': {u'lat': 44.0, u'lng': -79.5}, 69 | 'location': 'Somewhere', 70 | 'accessed': datetime.datetime(2013, 9, 9, 14, 54, 42), 71 | 'email': None} 72 | for k, v in expected.items(): 73 | assert getattr(evt, k) == v 74 | 75 | def test_event_json(self): 76 | """ 77 | verify that the JSON emitted is compatible with the javascript 78 | map stuff 79 | """ 80 | user = self.user 81 | 82 | POST_DATA = {u'story': [u'some access requirement'], 83 | u'doi': [u'10.1016/j.urology.2010.05.009.'], 84 | u'url': [u'http://www.ncbi.nlm.nih.gov/pubmed/20709373'], 85 | u'coords': [u'44,-79.5'], 86 | u'location': [u''], 87 | u'accessed': [u'Mon, 09 Sep 2013 14:54:42 GMT'], 88 | u'description': [u'some description'], 89 | u'user_id': user.id} 90 | 91 | c = Client() 92 | response = c.post('/api/post/', POST_DATA) 93 | 94 | assert response.status_code == 302 95 | 96 | json_data = Event.objects.filter(id=c.session['data']['event_id']).to_json() 97 | jdata = json.loads(json_data) 98 | eq_(len(jdata), 1) 99 | data = jdata[0] 100 | eq_(data['coords'], {'lat': 44.0, 'lng': -79.5}) 101 | eq_(data['doi'], '10.1016/j.urology.2010.05.009.') 102 | eq_(data['url'], 'http://www.ncbi.nlm.nih.gov/pubmed/20709373') 103 | 104 | actual_date = datetime.datetime.fromtimestamp(data['accessed']['$date'] / 1000) 105 | eq_(actual_date.year, 2013) 106 | eq_(actual_date.month, 9) 107 | eq_(actual_date.day, 9) 108 | 109 | eq_(data['user_name'], 'some name') 110 | eq_(data['user_profession'], 'Student') 111 | eq_(data['story'], 'some access requirement') 112 | 113 | # Check that we can resolve the original user oid 114 | evt = Event.objects.filter(id=c.session['data']['event_id'])[0] 115 | assert evt.user_id is not None 116 | 117 | def test_update_signon(self): 118 | """ 119 | verify that the JSON emitted is compatible with the javascript 120 | map stuff 121 | """ 122 | user = self.user 123 | 124 | c = Client() 125 | response = c.post('/api/signin/', self.POST_DATA) 126 | 127 | eq_(response.status_code, 200) 128 | eq_({'url': user.get_bookmarklet_url()}, json.loads(response.content)) 129 | 130 | # check that we have all the signin fields 131 | user = User.objects.filter(username=self.EMAIL)[0] 132 | eq_(user.name, 'some name') 133 | eq_(user.email, self.EMAIL) 134 | eq_(user.profession, 'Student') 135 | ok_(user.mailinglist) 136 | 137 | def test_search_doi_after_post(self): 138 | ''' 139 | Tests to make sure the response to submitting the form is rendered 140 | correctly. 141 | ''' 142 | POST_DATA = {'name': 'mock name', 143 | 'profession': 'mock profession', 144 | 'location': 'mock location', 145 | 'coords': '33.2,21.9', 146 | 'accessed': 'Mon, 09 Sep 2013 14:54:42 GMT', 147 | 'doi': 'some.doi', 148 | 'url': 'http://some.url/some_path', 149 | 'story': 'some_story', 150 | 'email': 'foo@blah.com', 151 | 'user_id': self.user.id, 152 | } 153 | 154 | c = Client() 155 | response = c.post('/api/post/', POST_DATA) 156 | 157 | response = c.post('/api/form/page3/') 158 | # Check that the DOI is passed through correctly 159 | data_doi_re = re.compile(']*data-doi="%s">' 160 | % POST_DATA['doi']) 161 | assert data_doi_re.search(response.content) is not None 162 | 163 | def test_data_xref_proxy(self): 164 | # TODO: Need a proper test for this that doesn't clobber the 165 | # network. Inject a fake requests mock object into xref_proxy 166 | # prior to execution 167 | pass 168 | """ 169 | c = Client() 170 | response = c.get('/api/xref_proxy/10.1103/PhysRevD.15.2752') 171 | assert json.loads(response.content) != None 172 | """ 173 | -------------------------------------------------------------------------------- /oabutton/static/public/js/lib/bootstrap-dialog.js: -------------------------------------------------------------------------------- 1 | /* ================================================ 2 | * Make use of Twitter Bootstrap's modal more monkey-friendly 3 | * Licensed under The MIT License. 4 | * ================================================ */ 5 | var BootstrapDialog = null; 6 | !function($){ 7 | "use strict"; 8 | 9 | BootstrapDialog = function(options){ 10 | this.defaultOptions = { 11 | 'title' : null, 12 | 'content' : null, 13 | 'draggableHandles' : '', 14 | 'autoDestroy' : true, 15 | 'buttons' : [], 16 | 'ready' : null, 17 | // Bootstrap's options 18 | 'backdrop' : 'static' 19 | }; 20 | this.dataHolder = {}; 21 | this.buttons = {}; 22 | this.init(options); 23 | }; 24 | 25 | BootstrapDialog.prototype = { 26 | constructor: BootstrapDialog, 27 | init: function(options){ 28 | this.options = $.extend(this.defaultOptions, options, true); 29 | }, 30 | initDialog: function(){ 31 | this.setDialog(this.createDialog()); 32 | this.setHeader(this.createHeader()); 33 | this.setBody(this.createBody()); 34 | this.setFooter(this.createFooter()); 35 | this.getDialog().append(this.getHeader()).append(this.getBody()).append(this.getFooter()); 36 | if (this.options.autoDestroy) { 37 | this.getDialog().on('hidden', function(event){ 38 | $(this).remove(); 39 | }); 40 | } 41 | }, 42 | initDraggable: function(){ 43 | var handles = this.options.draggableHandles.split(','); 44 | var handlesArray = []; 45 | for(var i=0; i 0) { 52 | this.getDialog().draggable({ 53 | handle: handlesArray.join(',') 54 | }); 55 | } 56 | }, 57 | initButtons: function(){ 58 | var $footer = this.getFooter(); 59 | var btns = this.getButtons(); 60 | for(var i=0; iButton'); 63 | if (btn.id){ 64 | this.buttons[btn.id] = $btn; 65 | } 66 | if (btn.label) { 67 | $btn.text(btn.label); 68 | } 69 | if (btn.cssClass) { 70 | $btn.addClass(btn.cssClass); 71 | } 72 | $btn.click({btn: btn, $btn: $btn, dialog: this}, function(event){ 73 | if(event.data.btn.onclick){ 74 | event.data.btn.onclick.call(event.target, event.data.dialog); 75 | } 76 | }); 77 | $footer.append($btn); 78 | } 79 | }, 80 | open: function(){ 81 | this.initDialog(); 82 | this.initDraggable(); 83 | this.initButtons(); 84 | this.ready(); 85 | this.getDialog().modal('show'); 86 | 87 | return this; 88 | }, 89 | close: function(){ 90 | this.getDialog().modal('hide'); 91 | }, 92 | destroy: function(){ 93 | this.getDialog().remove(); 94 | }, 95 | createDialog: function(){ 96 | var $dialog = $(document.createElement('div')); 97 | $dialog.addClass('modal fade hide'); 98 | $('body').append($dialog); 99 | $dialog.modal({ 100 | backdrop : this.options.backdrop, 101 | show : false 102 | }); 103 | 104 | return $dialog; 105 | }, 106 | setDialog: function($dialog){ 107 | this.$dialog = $dialog; 108 | 109 | return this; 110 | }, 111 | getDialog: function(){ 112 | return this.$dialog; 113 | }, 114 | createHeader: function(){ 115 | var $header = $(''); 116 | if (this.getTitle() == null) { 117 | $header.hide(); 118 | }else{ 119 | $header.append(this.createDynamicContent(this.getTitle())); 120 | } 121 | 122 | return $header; 123 | }, 124 | setHeader: function($header){ 125 | this.$header = $header; 126 | 127 | return this; 128 | }, 129 | getHeader: function(){ 130 | return this.$header; 131 | }, 132 | createBody: function(){ 133 | var $body = $(''); 134 | if (this.getContent() != null) { 135 | $body.append(this.createDynamicContent(this.getContent())); 136 | } 137 | 138 | return $body; 139 | }, 140 | setBody: function($body){ 141 | this.$body = $body; 142 | 143 | return this; 144 | }, 145 | getBody: function(){ 146 | return this.$body; 147 | }, 148 | createFooter: function(){ 149 | return $(''); 150 | }, 151 | setFooter: function($footer){ 152 | this.$footer = $footer; 153 | 154 | return this; 155 | }, 156 | getFooter: function(){ 157 | return this.$footer; 158 | }, 159 | setButton: function(id, $btn){ 160 | this.buttons[id] = $btn; 161 | 162 | return this; 163 | }, 164 | getButton: function(id){ 165 | return this.buttons[id]; 166 | }, 167 | createDynamicContent: function(rawContent){ 168 | var contentType = typeof rawContent; 169 | if (contentType != 'function') { 170 | return rawContent; 171 | } 172 | 173 | return rawContent(this); 174 | }, 175 | setData: function(key, value){ 176 | this.dataHolder[key] = value; 177 | 178 | return this; 179 | }, 180 | getData: function(key){ 181 | return this.dataHolder[key]; 182 | }, 183 | setTitle: function(title){ 184 | this.options.title = title; 185 | 186 | return this; 187 | }, 188 | getTitle: function(){ 189 | return this.options.title; 190 | }, 191 | setContent: function(content){ 192 | this.options.content = content; 193 | 194 | return this; 195 | }, 196 | getContent: function(){ 197 | return this.options.content; 198 | }, 199 | setButtons: function(buttons){ 200 | this.options.buttons = buttons; 201 | 202 | return this; 203 | }, 204 | getButtons: function(){ 205 | return this.options.buttons; 206 | }, 207 | setReady: function(readyCallback){ 208 | this.options.ready = readyCallback; 209 | 210 | return this; 211 | }, 212 | getReady: function(){ 213 | return this.options.ready; 214 | }, 215 | /** 216 | * Run this after the dialog is ready 217 | */ 218 | ready: function(){ 219 | if (typeof this.options.ready == 'function') { 220 | this.options.ready(this); 221 | } 222 | 223 | return this; 224 | } 225 | }; 226 | 227 | /* ================================================ 228 | * For lazy people 229 | * ================================================ */ 230 | BootstrapDialog.alert = function(message, callback){ 231 | new BootstrapDialog({ 232 | content: message, 233 | buttons: [{ 234 | label : 'OK', 235 | cssClass: 'btn-primary', 236 | onclick : function(dialog){ 237 | if (typeof callback == 'function') { 238 | callback(); 239 | } 240 | dialog.close(); 241 | } 242 | }] 243 | }).open(); 244 | }; 245 | 246 | BootstrapDialog.confirm = function(message, callback){ 247 | new BootstrapDialog({ 248 | content: message, 249 | buttons: [{ 250 | label : 'Cancel', 251 | onclick : function(dialog){ 252 | if (typeof callback == 'function') { 253 | callback(false); 254 | } 255 | dialog.close(); 256 | } 257 | }, { 258 | label : 'OK', 259 | cssClass: 'btn-primary', 260 | onclick : function(dialog){ 261 | if (typeof callback == 'function') { 262 | callback(true); 263 | } 264 | dialog.close(); 265 | } 266 | }] 267 | }).open(); 268 | }; 269 | 270 | }(window.jQuery); 271 | -------------------------------------------------------------------------------- /oabutton/apps/bookmarklet/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.core import serializers 3 | from django.core.context_processors import csrf 4 | from django.http import HttpResponse, HttpResponseServerError 5 | from django.shortcuts import redirect 6 | from django.shortcuts import render_to_response 7 | from django.views.decorators.csrf import csrf_protect 8 | from models import Event 9 | from oabutton.apps.bookmarklet.models import User 10 | from oabutton.common import SigninForm, Bookmarklet 11 | import dateutil 12 | import json 13 | import requests 14 | 15 | 16 | def show_map(req): 17 | # TODO: we need to make this smarter. Coallescing the lat/long 18 | # data on a nightly basis and folding that down into clustered 19 | # points would mean we throw less data down to the browser 20 | json_data = Event.objects.all().to_json() 21 | count = Event.objects.count() 22 | context = {'title': 'Map', 'events': json_data, 'count': count} 23 | return render_to_response(req, 'bookmarklet/site/map.html', context) 24 | 25 | 26 | def get_json(req): 27 | # Dump all data as JSON. This seems like a terrible idea when the 28 | # dataset gets large. 29 | json_data = serializers.serialize("json", Event.objects.all()) 30 | return HttpResponse(json_data, content_type="application/json") 31 | 32 | 33 | @csrf_protect 34 | def signin(request): 35 | """ 36 | One time signin to create a bookmarklet using HTTP POST. 37 | 38 | The only required field is the email address 39 | 40 | Create a new user and return the URL to the user bookmarklet 41 | """ 42 | if request.method == 'POST': 43 | # If the form has been submitted... 44 | form = SigninForm(request.POST) # A form bound to the POST data 45 | if form.is_valid(): # All validation rules pass 46 | # TODO: do stuff here 47 | manager = get_user_model()._default_manager 48 | data = dict(form.cleaned_data) 49 | data['username'] = data['email'] 50 | 51 | try: 52 | user = User.objects.get(username=data['email']) 53 | user.mailinglist = data['mailinglist'] 54 | user.name = data['name'] 55 | user.profession = data['profession'] 56 | user.usernmae = data['username'] 57 | user.save() 58 | except User.DoesNotExist: 59 | # Default the username to be email address 60 | user = manager.create_user(**data) 61 | 62 | return HttpResponse(json.dumps({'url': user.get_bookmarklet_url()}), content_type="application/json") 63 | return HttpResponseServerError(json.dumps({'errors': form._errors}), content_type="application/json") 64 | 65 | 66 | #def form(req, user_id): 67 | # """ 68 | # Show the bookmarklet form 69 | # """ 70 | # form = Bookmarklet(req.GET) 71 | # 72 | # if 'doi' in form.data: 73 | # form.fields['doi'].widget.attrs['readonly'] = 'readonly' 74 | # form.fields['doi'].widget.attrs['value'] = form.data['doi'] 75 | # 76 | # if 'url' in form.data: 77 | # form.fields['url'].widget.attrs['value'] = form.data['url'] 78 | # 79 | # form.fields['user_id'].widget.attrs['value'] = user_id 80 | # 81 | # c = {} 82 | # c.update(csrf(req)) 83 | # c.update({'bookmarklet': form, 'user_id': user_id}) 84 | # return render_to_response('bookmarklet/index.html', c) 85 | # 86 | 87 | def form1(req, user_id): 88 | """ 89 | Show the bookmarklet form 90 | """ 91 | form = Bookmarklet(req.GET) 92 | 93 | if 'doi' in form.data: 94 | form.fields['doi'].widget.attrs['readonly'] = 'readonly' 95 | form.fields['doi'].widget.attrs['value'] = form.data['doi'] 96 | 97 | if 'url' in form.data: 98 | form.fields['url'].widget.attrs['value'] = form.data['url'] 99 | 100 | form.fields['user_id'].widget.attrs['value'] = user_id 101 | 102 | c = {} 103 | req.session['user_id'] = user_id 104 | 105 | c.update(csrf(req)) 106 | c.update({'bookmarklet': form, 'user_id': user_id}) 107 | return render_to_response('bookmarklet/page1.html', c) 108 | 109 | 110 | def form2(req): 111 | """ 112 | Show the bookmarklet form. We just need the CSRF token here. 113 | """ 114 | data = req.session['data'] 115 | scholar_url = data['scholar_url'] 116 | doi = data['doi'] 117 | event = Event.objects.get(id=data['event_id']) 118 | 119 | c = {} 120 | c.update(csrf(req)) 121 | c.update({'scholar_url': scholar_url, 'doi': doi, 'url': event.url}) 122 | return render_to_response('bookmarklet/page2.html', c) 123 | 124 | 125 | def form3(req): 126 | """ 127 | Show the bookmarklet form 128 | """ 129 | 130 | if req.method != 'POST': 131 | return redirect('bookmarklet:form1', user_id=req.session['user_id']) 132 | 133 | data = req.session['data'] 134 | scholar_url = data['scholar_url'] 135 | doi = data['doi'] 136 | event = Event.objects.get(id=data['event_id']) 137 | 138 | c = {} 139 | c.update(csrf(req)) 140 | c.update({'scholar_url': scholar_url, 'doi': doi, 'url': event.url}) 141 | return render_to_response('bookmarklet/page3.html', c) 142 | 143 | 144 | def add_post(req): 145 | c = {} 146 | c.update(csrf(req)) 147 | 148 | if req.method == 'POST': 149 | # If the form has been submitted... 150 | form = Bookmarklet(req.POST) # A form bound to the POST data 151 | if form.is_valid(): # All validation rules pass 152 | evt_dict = dict(form.cleaned_data) 153 | lat, lng = evt_dict['coords'].split(',') 154 | evt_dict['coords'] = {'lat': float(lat), 'lng': float(lng)} 155 | if evt_dict['accessed'] != '': 156 | evt_dict['accessed'] = dateutil.parser.parse(evt_dict['accessed']) 157 | 158 | user = User.objects.get(id=evt_dict['user_id']) 159 | evt_dict['user_name'] = user.name 160 | evt_dict['user_profession'] = user.profession 161 | 162 | event = Event(**evt_dict) 163 | event.save() 164 | 165 | scholar_url = '' 166 | if 'doi' in evt_dict: 167 | # Some dumb DOIs end with '.' characters 168 | while evt_dict['doi'].endswith('.'): 169 | evt_dict['doi'] = evt_dict['doi'][:-1] 170 | doi = evt_dict['doi'] 171 | scholar_url = 'http://scholar.google.com/scholar?cluster=http://dx.doi.org/%s' % doi 172 | 173 | req.session['data'] = {'event_id': event.id, 174 | 'scholar_url': scholar_url, 175 | 'doi': doi} 176 | 177 | return redirect('bookmarklet:form2') 178 | else: 179 | return redirect('bookmarklet:form1', user_id=req.session['user_id']) 180 | 181 | 182 | def xref_proxy(req, doi): 183 | url = "http://data.crossref.org/%s" % doi 184 | headers = {'Accept': "application/vnd.citationstyles.csl+json"} 185 | r = requests.get(url, headers=headers) 186 | return HttpResponse(r.text, content_type="application/json") 187 | 188 | 189 | def generate_bookmarklet(req, user_id): 190 | return render_to_response('bookmarklet/bookmarklet.html', 191 | {'user_id': user_id}, 192 | content_type="application/javascript") 193 | -------------------------------------------------------------------------------- /oabutton/static/test/test.js: -------------------------------------------------------------------------------- 1 | module("success.js", { 2 | setup: function() { 3 | this.doi = "10.1038/nrrheum.2013.47"; 4 | this.metadata = { 5 | doi: this.doi, 6 | title: "Acute inflammatory arthritis: Monoarthritis risk stratification in Lyme disease", 7 | authors: "Robert T. Schoen", 8 | publication: "Nature Reviews Rheumatology", 9 | date: "2013-4-9", 10 | }; 11 | }, 12 | }); 13 | 14 | test( "parseCrossRef", function() { 15 | var entry = {"title":"Acute inflammatory arthritis: Monoarthritis risk stratification in Lyme disease","link":{"href":"http://dx.doi.org/10.1038%2Fnrrheum.2013.47"},"id":"http://dx.doi.org/10.1038/nrrheum.2013.47","updated":"2013-09-23T06:07:17-04:00","content":{"div":{"p":[{"b":"Acute inflammatory arthritis: Monoarthritis risk stratification in Lyme disease"},"\n\tNature Reviews Rheumatology. \n\t10.1038/nrrheum.2013.47","\n\tAuthors:\n\t\n\t\n\tRobert T. Schoen\n\t\n\t\n\t\n "],"xmlns":"http://www.w3.org/1999/xhtml"},"type":"xhtml"},"pam:message":{"pam:article":{"xhtml:head":{"xmlns:xhtml":"http://www.w3.org/1999/xhtml"},"dc:identifier":"10.1038/nrrheum.2013.47","dc:title":"Acute inflammatory arthritis: Monoarthritis risk stratification in Lyme disease","dc:publisher":"Nature Publishing Group","dc:isPartOf":"1759-4790","prism:alternateTitle":"Nature Reviews Rheumatology","dc:creator":"Robert T. Schoen","prism:publicationName":"Nature Reviews Rheumatology","prism:issn":"1759-4790","prism:eIssn":"1759-4804","prism:doi":"10.1038/nrrheum.2013.47","prism:publicationDate":"2013-4-9","prism:volume":"9","prism:startingPage":"261","prism:endingPage":"262","prism:url":"http://dx.doi.org/10.1038%2Fnrrheum.2013.47"},"xmlns:pam":"http://prismstandard.org/namespaces/pam/2.0/","xsi:schemaLocation":"http://prismstandard.org/namespaces/pam/2.0/ \n\t\t\t\t http://www.prismstandard.org/schemas/pam/2.1/pam.xsd"}}; 16 | 17 | var result = oabSuccess.parseCrossRef(entry, this.doi); 18 | 19 | deepEqual(result, this.metadata, "Parses data correctly"); 20 | }); 21 | 22 | test( "addScholarDOILink", function() { 23 | var $fixture = $('#qunit-fixture'); 24 | $fixture.append(''); 25 | 26 | oabSuccess.addScholarDOILink(this.metadata); 27 | 28 | var link = $("a", $fixture); 29 | equal(link.length, 1, "Only one link added"); 30 | ok(link.attr('href').indexOf('scholar.google.com') > -1, "Link goes to Google Scholar"); 31 | ok(link.attr('href').indexOf(encodeURIComponent(this.metadata.doi)) > -1, "Link href contains the DOI"); 32 | }); 33 | 34 | test( "addScholarTitleLink", function() { 35 | var $fixture = $('#qunit-fixture'); 36 | $fixture.append(''); 37 | 38 | oabSuccess.addScholarTitleLink(this.metadata); 39 | 40 | var link = $("a", $fixture); 41 | equal(link.length, 1, "Only one link added"); 42 | ok(link.attr('href').indexOf('scholar.google.com') > -1, "Link goes to Google Scholar"); 43 | ok(link.attr('href').indexOf(encodeURIComponent(this.metadata.title)) > -1, "Link href contains the DOI"); 44 | }); 45 | 46 | asyncTest( "discoverCORELinks", function() { 47 | expect(3); 48 | 49 | var $fixture = $('#qunit-fixture'); 50 | $fixture.append(''); 51 | 52 | $.mockjax({ 53 | url: "/metadata/coresearch.json/*", 54 | responseText: {"ListRecords":[{"total_hits":5015},{"record":{"header":{"header:content":{"core:repositoryIdentifier":"143","identifier":"669636"},"header:attr":{"xmlns:core":"http:\/\/core.kmi.open.ac.uk\/api\/doc"}},"metadata":{"oai_dc:dc":{"oai_dc:ns":[{"xmlns:oai_dc":"http:\/\/www.openarchives.org\/OAI\/2.0\/oai_dc\/","xmlns:dc":"http:\/\/purl.org\/dc\/elements\/1.1\/"}],"dc:creator":"R Kudyar","dc:format":"application\/pdf","dc:source":"http:\/\/www.jkscience.org\/archive\/111\/18-RL-JACORD%20ARTHRITIS.pdf","dc:date":"2009","dc:identifier":"http:\/\/www.jkscience.org\/archive\/111\/18-RL-JACORD%20ARTHRITIS.pdf","dc:description":"A case of a patient with rheumatic valvulardisease who had comparable deformities of the handsand fingers and who fulfilled all of the criteriasuggested by Bywaters to describe Jaccoud's Arthritis is decribed here.","dc:title":"Jaccoud\u2019s Arthritis"}}}},{"record":{"header":{"header:content":{"core:repositoryIdentifier":"140","identifier":"60225"},"header:attr":{"xmlns:core":"http:\/\/core.kmi.open.ac.uk\/api\/doc"}},"metadata":{"oai_dc:dc":{"oai_dc:ns":[{"xmlns:oai_dc":"http:\/\/www.openarchives.org\/OAI\/2.0\/oai_dc\/","xmlns:dc":"http:\/\/purl.org\/dc\/elements\/1.1\/"}],"dc:creator":"Zai Liu, Guo Deng, Simon Foster and Andrej Tarkowski","dc:format":"application\/pdf","dc:source":"http:\/\/eprints.whiterose.ac.uk\/21\/1\/ar330.pdf","dc:date":"2001-09-17","dc:identifier":"http:\/\/eprints.whiterose.ac.uk\/21\/1\/ar330.pdf","dc:description":"Staphylococcus aureus is one of the most important pathogens in septic arthritis. To analyse the arthritogenic properties of staphylococcal peptidoglycan (PGN), highly purified PGN from S. aureus was intra-articularly injected into murine joints. The results demonstrate that PGN will trigger arthritis in a dose-dependent manner. A single injection of this compound leads to massive infiltration of predominantly macrophages and polymorphonuclear cells with occasional signs of cartilage and\/or bone destruction, lasting for at least 14 days. Further studies showed that this condition is mediated by the combined impact of acquired and innate immune systems. Our results indicate that PGN exerts a central role in joint inflammation triggered by S. aureus.","dc:title":"Staphylococcal peptidoglycans induce arthritis"}}}},{"record":{"header":{"header:content":{"core:repositoryIdentifier":"143","identifier":"5726004"},"header:attr":{"xmlns:core":"http:\/\/core.kmi.open.ac.uk\/api\/doc"}},"metadata":{"oai_dc:dc":{"oai_dc:ns":[{"xmlns:oai_dc":"http:\/\/www.openarchives.org\/OAI\/2.0\/oai_dc\/","xmlns:dc":"http:\/\/purl.org\/dc\/elements\/1.1\/"}],"dc:creator":"Karambin Mohammad Mehdi and Hashemian Hooman","dc:format":"application\/pdf","dc:source":"http:\/\/journals.tums.ac.ir\/PdfMed.aspx?pdf_med=\/upload_files\/pdf\/12751.pdf&manuscript_id=12751","dc:date":"2009","dc:identifier":"http:\/\/journals.tums.ac.ir\/PdfMed.aspx?pdf_med=\/upload_files\/pdf\/12751.pdf&manuscript_id=12751","dc:description":"To determine the rate of different types of arthritis in children. We prepared a retrospective descriptive study and included the whole 100 cases of arthritis referred to 17-Shahrivar Hospital, Rasht, Guilan during a 3 years period. Using their medical files, data including age, sex, season of admission, history of trauma, signs and symptoms, lab findings and duration of hospitalization were collected. SPSS 13.0 (statistical software) applied for statistical analysis. The most common age of involvement ranged 6-9 years. Septic arthritis, brucellosis, and rheumatoid fever were the most frequent causes of arthritis in our study. Fever and restricted range of motion had the highest rate among different signs and symptoms. Lab data demonstrated leukocytosis, positive CRP, and increased ESR among 74, 79.5, and 73 percent of our patients, respectively. According to the high prevalence of septic arthritis and the arthritis due to brucellosis and rheumatoid fever, it seems that mentioned diseases are still major problems in the issue of hygiene management.","dc:title":"Childhood Arthritis: Rate of Different Types"}}}}]}, 55 | }); 56 | 57 | oabSuccess.discoverCORELinks(this.metadata); 58 | 59 | setTimeout(function() { 60 | var link = $("a", $fixture); 61 | equal(link.length, 3, "Three links added"); 62 | 63 | link = $("a:contains('Jaccoud\u2019s Arthritis')", $fixture); 64 | equal(link.length, 1, "Link with 'Jaccoud\u2019s Arthritis' added"); 65 | equal(link.attr('href'), "http:\/\/www.jkscience.org\/archive\/111\/18-RL-JACORD%20ARTHRITIS.pdf", 66 | "Link points to correct PDF"); 67 | 68 | start(); 69 | }, 500); 70 | }); 71 | -------------------------------------------------------------------------------- /oabutton/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for oabutton project. 2 | 3 | import sys 4 | from os.path import dirname, abspath, join 5 | from mongoengine import connect 6 | 7 | ROOT_PATH = dirname(dirname(abspath(__file__))) 8 | STATIC_PUBLIC = join(ROOT_PATH, 'oabutton/static/public') 9 | 10 | # Start override vars # 11 | DEBUG = False #(sys.argv[1] == 'runserver') 12 | TEMPLATE_DEBUG = DEBUG 13 | HOSTNAME='http://localhost:8000' 14 | # ENd override vars # 15 | 16 | try: 17 | from settings_local import * # NOQA 18 | except: 19 | print "Can't load settings_local - CORE won't work" 20 | 21 | 22 | ADMINS = ( 23 | ('Victor Ng', 'victor@crankycoder.com'), 24 | ) 25 | 26 | MANAGERS = ADMINS 27 | 28 | DATABASES = {'default': { 29 | 'ENGINE': 'django.db.backends.sqlite3', 30 | 'NAME': 'oabutton.sqlite3', # Path to database file. 31 | 'USER': '', # Not used with sqlite3. 32 | 'PASSWORD': '', # Not used with sqlite3. 33 | # Set to empty string for localhost. Not used with sqlite3. 34 | 'HOST': '', 35 | # Set to empty string for default. Not used with sqlite3. 36 | 'PORT': '', 37 | }} 38 | 39 | # Hosts/domain names that are valid for this site; required if DEBUG is False 40 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 41 | ALLOWED_HOSTS = ['*'] 42 | 43 | # Local time zone for this installation. Choices can be found here: 44 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 45 | # although not all choices may be available on all operating systems. 46 | # In a Windows environment this must be set to your system time zone. 47 | TIME_ZONE = 'America/Toronto' 48 | 49 | # Language code for this installation. All choices can be found here: 50 | # http://www.i18nguy.com/unicode/language-identifiers.html 51 | LANGUAGE_CODE = 'en-us' 52 | 53 | SITE_ID = 1 54 | 55 | # If you set this to False, Django will make some optimizations so as not 56 | # to load the internationalization machinery. 57 | USE_I18N = True 58 | 59 | # If you set this to False, Django will not format dates, numbers and 60 | # calendars according to the current locale. 61 | USE_L10N = True 62 | 63 | # If you set this to False, Django will not use timezone-aware datetimes. 64 | USE_TZ = True 65 | 66 | # Absolute filesystem path to the directory that will hold user-uploaded files. 67 | # Example: "/home/media/media.lawrence.com/media/" 68 | MEDIA_ROOT = '' 69 | 70 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 71 | # trailing slash. 72 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 73 | MEDIA_URL = '' 74 | 75 | # Absolute path to the directory static files should be collected to. 76 | # Don't put anything in this directory yourself; store your static files 77 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 78 | # Example: "/home/media/media.lawrence.com/static/" 79 | STATIC_ROOT = STATIC_PUBLIC 80 | 81 | # URL prefix for static files. 82 | # Example: "http://media.lawrence.com/static/" 83 | STATIC_URL = '/static/' 84 | 85 | # Additional locations of static files 86 | STATICFILES_DIRS = () 87 | 88 | # List of finder classes that know how to find static files in 89 | # various locations. 90 | STATICFILES_FINDERS = ( 91 | 'django.contrib.staticfiles.finders.FileSystemFinder', 92 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 93 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 94 | 'compressor.finders.CompressorFinder', 95 | ) 96 | 97 | # Make this unique, and don't share it with anybody. 98 | SECRET_KEY = 'vds2d@yy^4hi_et38)31kkq(pp@06275u&q1tnoz16wo&=127z' 99 | 100 | # List of callables that know how to import templates from various sources. 101 | TEMPLATE_LOADERS = ( 102 | ('pyjade.ext.django.Loader', ( 103 | 'django.template.loaders.filesystem.Loader', 104 | 'django.template.loaders.app_directories.Loader', 105 | )), 106 | ) 107 | 108 | # Production LESS compression via django-compressor 109 | COMPRESS_PRECOMPILERS = ( 110 | ('text/less', 'lessc {infile} {outfile}'), 111 | ) 112 | 113 | MIDDLEWARE_CLASSES = ( 114 | 'django.middleware.common.CommonMiddleware', 115 | 'django.contrib.sessions.middleware.SessionMiddleware', 116 | 'django.middleware.csrf.CsrfViewMiddleware', 117 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 118 | 'django.contrib.messages.middleware.MessageMiddleware', 119 | # Uncomment the next line for simple clickjacking protection: 120 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 121 | ) 122 | 123 | ROOT_URLCONF = 'oabutton.urls' 124 | 125 | # Python dotted path to the WSGI application used by Django's runserver. 126 | WSGI_APPLICATION = 'oabutton.wsgi.application' 127 | 128 | TEMPLATE_CONTEXT_PROCESSORS = ( 129 | "django.contrib.auth.context_processors.auth", 130 | "django.core.context_processors.debug", 131 | "django.core.context_processors.i18n", 132 | "django.core.context_processors.media", 133 | "django.core.context_processors.request", 134 | "django.core.context_processors.static", 135 | "django.core.context_processors.tz", 136 | "django.contrib.messages.context_processors.messages" 137 | ) 138 | 139 | TEMPLATE_DIRS = ( 140 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 141 | # Always use forward slashes, even on Windows. 142 | # Don't forget to use absolute paths, not relative paths. 143 | ) 144 | 145 | INSTALLED_APPS = ( 146 | 'django.contrib.auth', 147 | 'mongoengine.django.mongo_auth', 148 | 149 | 'django.contrib.contenttypes', 150 | 'django.contrib.sessions', 151 | 152 | 'django.contrib.messages', 153 | 'django.contrib.staticfiles', 154 | 155 | 'compressor', 156 | 157 | # The Django admin assumes you're running on a RDBMS and isn't 158 | # suitable for a pure MongoDB 159 | # Do *not* enable it. 160 | 'django.contrib.admin', 161 | 162 | # The bookmarklet app is really just the REST API 163 | 'oabutton.apps.bookmarklet', 164 | 165 | # The web app is the main website 166 | 'oabutton.apps.web', 167 | 168 | 'oabutton.apps.metadata', 169 | ) 170 | 171 | # A sample logging configuration. The only tangible logging 172 | # performed by this configuration is to send an email to 173 | # the site admins on every HTTP 500 error when DEBUG=False. 174 | # See http://docs.djangoproject.com/en/dev/topics/logging for 175 | # more details on how to customize your logging configuration. 176 | LOGGING = { 177 | 'version': 1, 178 | 'disable_existing_loggers': False, 179 | 'formatters': { 180 | 'verbose': { 181 | 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' 182 | }, 183 | 'simple': { 184 | 'format': '%(levelname)s %(message)s' 185 | }, 186 | }, 187 | 'filters': { 188 | 'require_debug_false': { 189 | '()': 'django.utils.log.RequireDebugFalse' 190 | } 191 | }, 192 | 'handlers': { 193 | 'console': { 194 | 'level': 'WARN', 195 | 'class': 'logging.StreamHandler', 196 | 'formatter': 'verbose' 197 | }, 198 | 'mail_admins': { 199 | 'level': 'ERROR', 200 | 'filters': ['require_debug_false'], 201 | 'class': 'django.utils.log.AdminEmailHandler' 202 | } 203 | }, 204 | 'loggers': { 205 | 'django.request': { 206 | 'handlers': ['mail_admins'], 207 | 'level': 'ERROR', 208 | 'propagate': True, 209 | }, 210 | 'oabutton.apps.metadata.views': { 211 | 'handlers': ['console'], 212 | 'level': 'WARN', 213 | }, 214 | } 215 | } 216 | 217 | # Honor the 'X-Forwarded-Proto' header for request.is_secure() 218 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 219 | 220 | # Bind MongoEngine 221 | connect('oabutton-server-dev', port=27017) 222 | 223 | 224 | # MongoEngine support requires overloading the session storage and the 225 | # authentication backends 226 | SESSION_ENGINE = 'mongoengine.django.sessions' 227 | AUTHENTICATION_BACKENDS = ('mongoengine.django.auth.MongoEngineBackend',) 228 | AUTH_USER_MODEL = 'mongo_auth.MongoUser' 229 | MONGOENGINE_USER_DOCUMENT = 'oabutton.apps.bookmarklet.models.User' 230 | 231 | -------------------------------------------------------------------------------- /oabutton/static/public/less/app.less: -------------------------------------------------------------------------------- 1 | /* Swatches */ 2 | 3 | @oayellow: #ffc425; 4 | @oaorange: #ef5b23; 5 | @oapurple: #7d355a; 6 | @oagreen1: #014d42; 7 | @oagreen2: #008471; 8 | @oagreen3: #37bba4; 9 | @oagreen4: #70c8bc; 10 | @oagreys1: #d6d3e2; 11 | @oagreys2: #a5bfba; 12 | 13 | /* Global styles */ 14 | 15 | body, h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { 16 | font-family: "Raleway", "Helvetica Neue", Helvetica,Arial,sans-serif; 17 | } 18 | 19 | /* Navigation and header area */ 20 | 21 | body { padding-top: 25px; } 22 | 23 | .anchor { padding-top: 50px; margin-top: -50px; } 24 | 25 | a[name] { 26 | padding-top: 75px; 27 | margin-top: -75px; 28 | display: inline-block; 29 | } 30 | 31 | .navbar-header .navbar-brand { 32 | font-size:0px; text-indent:-9999px; 33 | width:16px; height:25px; 34 | margin-top:12px; margin-left:12px; 35 | background:url(../img/oalogo_x_white.png) no-repeat top left; 36 | } 37 | 38 | header { 39 | background:url(../img/bg.jpg) no-repeat; 40 | background-position: 0px 50%; 41 | background-size:100% auto; 42 | text-align: center; 43 | 44 | .container { display:none; } 45 | 46 | a { 47 | display: block; 48 | width:100%; height:225px; max-width: 960px; 49 | font-size: 0px; text-indent: -9999px; 50 | background:url(../img/header_wide.png) no-repeat bottom center; 51 | background-size:80%; 52 | } 53 | 54 | .lead { 55 | position: absolute; 56 | text-align: center; 57 | font-size: 20px; 58 | font-weight: 600; 59 | width: 100%; 60 | left: 0px; 61 | margin-top: 15px; 62 | span { 63 | background-color: @oaorange; 64 | color: white; 65 | padding: 0.4em; 66 | opacity: 0.9; 67 | } 68 | } 69 | } 70 | 71 | #github_ribbon { 72 | position: absolute; 73 | top: 3em; 74 | right: 0; 75 | border: 0; 76 | z-index: 1; 77 | } 78 | 79 | #video-header { 80 | background: url(../img/iconmonstr-video-5.png) no-repeat left center; 81 | background-size: 1em; 82 | padding-left: 1.5em; 83 | } 84 | 85 | .jumbotron { margin-bottom: 0; } 86 | 87 | #start form { border: none; } 88 | 89 | /* Bookmarklet popup */ 90 | 91 | #bookmarklet { 92 | display: none; 93 | position: fixed; 94 | top: 15px; 95 | right: 100px; 96 | z-index: 9999; 97 | padding: 10px; 98 | margin: 0; 99 | background: white; 100 | border: 1px solid #ddd; 101 | border-radius: 10px; 102 | } 103 | 104 | #form-bookmarklet { 105 | .checkbox label { font-weight: 700; } 106 | } 107 | 108 | #help-bookmarklet { 109 | display: none; 110 | margin-top: 10px; 111 | margin-bottom: 30px; 112 | background: #eef; 113 | padding: 1em 1em 0.5em 1em; 114 | } 115 | 116 | .wedge-up { 117 | display: block; 118 | width: 15px; 119 | height: 15px; 120 | border-top: 15px solid transparent; 121 | border-left: 15px solid white; 122 | position: absolute; 123 | margin-top: -25px; 124 | } 125 | 126 | .modal-backdrop.in { 127 | opacity: .8; 128 | filter: alpha(opacity=80); 129 | } 130 | 131 | /* Activity map */ 132 | 133 | #world, #map { 134 | height: 500px; 135 | width: 100%; 136 | margin-top: -20px; 137 | } 138 | #world { 139 | .underlay { 140 | display: none; 141 | position: absolute; 142 | left: 0px; 143 | bottom: 0px; 144 | width: 84%; 145 | margin: 0 8%; 146 | padding: 1em 0; 147 | z-index: 99; 148 | background: @oayellow; 149 | 150 | .lead { 151 | background: #fff; 152 | text-align: center; 153 | margin-bottom: 10px; 154 | } 155 | 156 | p { padding: 0 1em; font-size: 110%; } 157 | } 158 | } 159 | 160 | #counter { 161 | font-weight: bold; 162 | font-size: 120%; 163 | } 164 | 165 | .thumbnail .caption { 166 | padding: 0px; 167 | 168 | p { font-size: 95%; } 169 | } 170 | 171 | /* Content areas */ 172 | 173 | h2 { 174 | color: #494949; 175 | font-size: 40px; 176 | text-align: center; 177 | padding-top: 0; 178 | margin-top: 0; 179 | margin-bottom: 15px; 180 | } 181 | 182 | h3 { 183 | color: #555; 184 | font-size: 19px; 185 | text-align: center; 186 | padding-top: 15px; 187 | margin-top: 0; 188 | margin-bottom: 1em; 189 | } 190 | 191 | h3.overline { 192 | border-top: 1px solid #aaa; 193 | } 194 | 195 | a[data-toggle] { 196 | cursor: pointer; 197 | } 198 | 199 | #intro, #contribute, #thanks { 200 | 201 | padding: 2em 3em; 202 | 203 | p { line-height: 1.7; } 204 | 205 | .row { 206 | border-left: 7px solid transparent; 207 | border-right: 7px solid transparent; 208 | padding: 0 1.5em; 209 | } 210 | .row.hilite-left { 211 | border-left: 7px solid @oaorange; 212 | } 213 | .row.hilite-right { 214 | border-right: 7px solid @oaorange; 215 | } 216 | } 217 | 218 | /* Container colors */ 219 | #thanks { background: #ffd; } 220 | #intro, #footer { background: #eee; } 221 | 222 | .buttons { 223 | margin-top: 2em; 224 | text-align: center; 225 | 226 | a { text-decoration: none !important; } 227 | 228 | button { 229 | font-size: 20px; 230 | padding: 4px 10px; 231 | border-color: #444; 232 | color: #000; 233 | margin: 0 20px; 234 | } 235 | } 236 | 237 | #contribute { 238 | background: #fff; 239 | } 240 | 241 | @thumbsize: 48; 242 | @captwidth: 400; 243 | @captheight: 156; 244 | @captmargin: @captwidth - @thumbsize; 245 | @thumbmargin: @captmargin + @thumbsize/2.4; 246 | #thanks .team { 247 | ul { text-align: left; list-style: none; } 248 | .thumbnails { 249 | 250 | li { display: inline-block; margin: 0 2px; } 251 | 252 | .wedge-up { border-left-color: @oaorange; margin-top:-19px; } 253 | 254 | .thumb { 255 | width: unit(@thumbsize, px); 256 | height: unit(@thumbsize, px); 257 | overflow: hidden; 258 | display: inline-block; 259 | img { width: 100%; } 260 | } 261 | 262 | li:hover, li.hover { .thumb { opacity: 0.1; } } 263 | 264 | li.right { 265 | .caption { margin-left:unit(-@captmargin, px); } 266 | .wedge-up { 267 | margin-left:unit(@thumbmargin, px); 268 | border-left: none; 269 | border-right: 15px solid @oaorange; 270 | } 271 | } 272 | 273 | .caption { 274 | position: absolute; z-index: 99; 275 | margin: 0; 276 | width: unit(@captwidth, px); 277 | height: unit(@captheight, px); 278 | max-width: 90%; 279 | padding: 4px; 280 | border: 2px solid @oaorange; 281 | background: white; 282 | font-size: 20px; 283 | 284 | .name { 285 | font-size: 23px; 286 | font-weight: bold; 287 | } 288 | 289 | p { 290 | margin-top: 4px; 291 | font-size: 15px; 292 | line-height: 1.2; 293 | } 294 | 295 | .large { 296 | height: unit(@thumbsize*3, px); 297 | max-width: unit(@thumbsize*3, px); 298 | float: left; margin-right: 5px; 299 | 300 | overflow: hidden; 301 | img { height: 144px; } 302 | } 303 | 304 | .links { 305 | position: absolute; 306 | left: unit(@thumbsize*3, px); 307 | bottom: 5px; 308 | } 309 | 310 | .web, .twitter { 311 | display: inline-block; 312 | width: auto; height: 24px; 313 | font-size: 18px; 314 | margin-left: 10px; 315 | padding-left: 27px; 316 | background-size: 24px 24px; 317 | background-repeat: no-repeat; 318 | background-position: left; 319 | } 320 | 321 | .web { background-image: url(../img/iconmonstr-globe-3.png); } 322 | .twitter { background-image: url(../img/iconmonstr-twitter-4.png); } 323 | } 324 | 325 | li .caption { display:none; } 326 | li:hover, li.hover { .caption { display: block; } } 327 | } 328 | } 329 | 330 | #thanks .orgs { 331 | li { width: 25%; float: left; } 332 | p { 333 | display: block; clear: both; 334 | font-size: 15px; 335 | padding-top:1em; text-align: center; 336 | } 337 | } 338 | 339 | #footer { 340 | padding-top: 2em; 341 | 342 | .icon { 343 | height: 32px; 344 | width: 32px; 345 | display: inline-block; 346 | background-repeat: no-repeat; 347 | background-position: 50% 50%; 348 | background-size: 100%; 349 | position: relative; 350 | font-size: 0px; 351 | text-indent: -9999px; 352 | margin: 0 25px 0 0; 353 | } 354 | .icon:hover { opacity: 0.5; } 355 | .twitter { background-image: url(../img/webicon-twitter.png); } 356 | .facebook { background-image: url(../img/webicon-facebook.png); } 357 | .wordpress { background-image: url(../img/webicon-wordpress.png); } 358 | } 359 | 360 | .license { 361 | font-size:90%; 362 | img { 363 | float: left; 364 | padding-top: 1.5em; 365 | padding-right: 1em; 366 | } 367 | } 368 | 369 | /* Responsive styles */ 370 | @media screen and (max-width: 768px) { 371 | body { padding-top: 0px; } 372 | header a { background-size:100%; } 373 | header .lead { font-size:15px; position:relative; } 374 | } 375 | @media screen and (max-width: 520px) { 376 | header a { 377 | height: 110px; position: absolute; 378 | background-image: url(../img/header_needaccess.png); 379 | } 380 | header .lead { visibility: hidden; } 381 | #github_ribbon { display: none; } 382 | } -------------------------------------------------------------------------------- /oabutton/apps/web/templates/web/index.jade: -------------------------------------------------------------------------------- 1 | extends web/layout 2 | block body 3 | #top 4 | a(name='top') 5 | 6 | // Github ribbon 7 | a(href='https://github.com/OAButton/OAButton') 8 | img#github_ribbon( 9 | src='https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png', 10 | alt='Fork me on GitHub') 11 | 12 | .navbar.navbar-inverse.navbar-fixed-top(role='navigation') 13 | .container 14 | .navbar-header 15 | button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse') 16 | span.icon-bar 17 | span.icon-bar 18 | span.icon-bar 19 | a.navbar-brand(href='#') Open Access Button 20 | .navbar-collapse.collapse 21 | ul.nav.navbar-nav 22 | li 23 | a(href='#top') Get the button 24 | li 25 | a(href='#world') See the map 26 | li 27 | a(href='#contribute') Contribute 28 | li 29 | a(href='#thanks') Meet the team 30 | li 31 | a(href='#contact') Contact and follow 32 | 33 | header.jumbotron.subhead 34 | .container 35 | h1 Need Access to Research? 36 | h2 Sign up below to get your Open Access Button 37 | a(href="#start", data-toggle="modal", data-target="#video-modal", alt="Need access to research?", title="Learn more about the project in this video") 38 | | Learn more about the project in this video 39 | .lead 40 | span 41 | | Tearing down barriers to accessing research, one click at a time 42 | 43 | .modal.fade(id="video-modal", tabindex="-1", role="dialog", aria-labelledby="video-header", aria-hidden="true") 44 | .modal-dialog 45 | .modal-content 46 | .modal-header 47 | button.close(type="button", data-dismiss="modal", aria-hidden="true") × 48 | h4.modal-title(id="video-header") 49 | | Introduction video 50 | .modal-body 51 | iframe(width="538", height="303", src="//www.youtube-nocookie.com/embed/3X_59G06RZM?rel=0", frameborder="0", allowfullscreen) 52 | .modal-footer 53 | button.btn.btn-default(type="button", data-dismiss="modal") Close 54 | 55 | #start 56 | .container.center 57 | block start 58 | 59 | #world 60 | a(name='world') 61 | .container 62 | #map 63 | .underlay 64 | .lead 65 | span(id='counter') 66 | {{count|force_escape}} 67 | span 68 | | Paywalls Hit 69 | p 70 | | This map displays people being denied access to the research they both need and paid for. Put yourself on the map using the Open Access Button to tell the world you are being denied access to knowledge. Using the Open Access Button will make the individual moments of injustice and frustration visible to the world. 71 | 72 | #intro 73 | a(name='intro') 74 | .container 75 | h2 What is this about? 76 | .row.hilite-left 77 | .col-md-12 78 | p 79 | | People are denied access to research hidden behind paywalls every day. This problem is invisible, but it slows innovation, kills curiosity and harms patients. This is an indictment of the current system. 80 | a(href="http://www.youtube.com/watch?v=L5rVH1KGBCY") Open Access 81 | | has given us the solution to this problem by allowing everyone to read and re-use research. 82 | | We created the 83 | i Open Access Button 84 | | to track the impact of paywalls and help you get access to the research you need. By using the button you’ll help show the impact of this problem, drive awareness of the issue, and help change the system. Furthermore, the 85 | i Open Access Button 86 | | has several ways of helping you get access to the research you need right now. 87 | 88 | #contribute 89 | a(name='contribute') 90 | .container 91 | h2 Get involved. 92 | .row.hilite-right 93 | .col-md-12 94 | p 95 | | The first step is using the Open Access Button (and you are using it, daily, on every paywall, right?). But don’t think that’s all there is to do! This project was made possible by developers, advocates, students and general clever folk from Open community and we need continued support to take this to the next level. Of course, the code, text, and data we’ve pulled together are openly available, so you can innovate and advocate with it. If you can, donate to the project to help us support and maintain service as well as helping us with our future development plans. Bottom line, we’d love to hear from you, so whether it’s a suggestion, virtual pat on the back, or, dare I say a bug you’ve noticed, get in touch. 96 | 97 | .row.buttons 98 | .col-md-12 99 | a(href="mailto:oabutton+bug@gmail.com") 100 | button.btn.btn-default.bug(type="button") 101 | | Report a bug 102 | a(href="https://www.charitycheckout.co.uk/1111824/OpenAccessButton") 103 | button.btn.btn-warning.donate(type="button") 104 | | Donate! 105 | a(href="mailto:oabutton+suggestion@gmail.com") 106 | button.btn.btn-success.suggest(type="button") 107 | | Suggestions 108 | 109 | #thanks 110 | a(name='thanks') 111 | .container 112 | .row.team 113 | .col-md-12 114 | h3.overline Meet the team of people and organisations who made this a reality 115 | ul.thumbnails 116 | each person in team_data 117 | li 118 | a.thumb(href="#{person['link']}", style="visibility:hidden") 119 | img(src="#{person['thumb']}") 120 | .caption 121 | i.wedge-up 122 | .large 123 | img(src="#{person['thumb']}") 124 | a.name(href="#{person['link']}") {{ person.name }} 125 | p {{ person.blurb }} 126 | .links 127 | if person.twitter 128 | a.twitter(href="http://twitter.com/#{person['twitter']}") Twitter 129 | .row.orgs 130 | .col-md-12 131 | h3 Many thanks to all of you who helped along the way 132 | ul 133 | each org in thanks_data 134 | li 135 | a(href="#{org['link']}") 136 | | {{ org.name }} 137 | p 138 | b + 139 | | Everyone who joined the 140 | a(href="https://www.thunderclap.it/en/projects/5675-open-access-button-launch‎") Thunderclap 141 | |, sent messages of support and suggestions! 142 | 143 | 144 | #footer 145 | a(name='contact') 146 | .container 147 | .row 148 | .col-md-3.col-sm-3 149 | p 150 | b INFO 151 | p 152 | a(href="http://blog.openaccessbutton.org") 153 | | Project blog 154 | p 155 | a(href="https://groups.google.com/d/forum/open-access-button-general") 156 | | Join the community 157 | p 158 | a(href="http://oabutton.wordpress.com/2013/11/17/disclaimer/") 159 | | Terms of use 160 | .col-md-3.col-sm-3 161 | p 162 | b ABOUT 163 | p 164 | a(href="mailto:oabutton+press@gmail.com") 165 | | Media inquiries 166 | p 167 | a(href="https://github.com/oabutton") 168 | | Get the code 169 | p 170 | a(href="mailto:oabutton+data@gmail.com") 171 | | Get the data 172 | .col-md-6.col-sm-6 173 | p 174 | a.icon.twitter(href="https://twitter.com/oa_button") Twitter 175 | a.icon.facebook(href="https://www.facebook.com/openaccessbutton") Facebook 176 | a.icon.wordpress(href="http://oabutton.wordpress.com/") Wordpress 177 | 178 | p.license 179 | a(rel="license", href="http://creativecommons.org/licenses/by/3.0/") 180 | img(alt="Creative Commons License", style="border-width:0", src="http://i.creativecommons.org/l/by/3.0/88x31.png") 181 | br 182 | | The 183 | span(xmlns:dct="http://purl.org/dc/terms/", property="dct:title") Open Access Button 184 | | is licensed under a 185 | a(rel="license", href="http://creativecommons.org/licenses/by/3.0/") Creative Commons Attribution 3.0 Unported License 186 | |. 187 | -------------------------------------------------------------------------------- /oabutton/static/public/css/styles.css: -------------------------------------------------------------------------------- 1 | @import "../css/MarkerCluster.css"; 2 | 3 | @import "../css/MarkerCluster.Default.css"; 4 | /* Don't edit this CSS file. It's generated using lessc */ 5 | /* Swatches */ 6 | /* Global styles */ 7 | body, 8 | h1, 9 | h2, 10 | h3, 11 | h4, 12 | h5, 13 | h6, 14 | .h1, 15 | .h2, 16 | .h3, 17 | .h4, 18 | .h5, 19 | .h6 { 20 | font-family: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif; 21 | } 22 | /* Navigation and header area */ 23 | body { 24 | padding-top: 25px; 25 | } 26 | .anchor { 27 | padding-top: 50px; 28 | margin-top: -50px; 29 | } 30 | a[name] { 31 | padding-top: 75px; 32 | margin-top: -75px; 33 | display: inline-block; 34 | } 35 | .navbar-header .navbar-brand { 36 | font-size: 0px; 37 | text-indent: -9999px; 38 | width: 16px; 39 | height: 25px; 40 | margin-top: 12px; 41 | margin-left: 12px; 42 | background: url(../img/oalogo_x_white.png) no-repeat top left; 43 | } 44 | header { 45 | background: url(../img/bg.jpg) no-repeat; 46 | background-position: 0px 50%; 47 | background-size: 100% auto; 48 | text-align: center; 49 | } 50 | header .container { 51 | display: none; 52 | } 53 | header a { 54 | display: block; 55 | width: 100%; 56 | height: 225px; 57 | max-width: 960px; 58 | font-size: 0px; 59 | text-indent: -9999px; 60 | background: url(../img/header_wide.png) no-repeat bottom center; 61 | background-size: 80%; 62 | } 63 | header .lead { 64 | position: absolute; 65 | text-align: center; 66 | font-size: 20px; 67 | font-weight: 600; 68 | width: 100%; 69 | left: 0px; 70 | margin-top: 15px; 71 | } 72 | header .lead span { 73 | background-color: #ef5b23; 74 | color: white; 75 | padding: 0.4em; 76 | opacity: 0.9; 77 | } 78 | #github_ribbon { 79 | position: absolute; 80 | top: 3em; 81 | right: 0; 82 | border: 0; 83 | z-index: 1; 84 | } 85 | #video-header { 86 | background: url(../img/iconmonstr-video-5.png) no-repeat left center; 87 | background-size: 1em; 88 | padding-left: 1.5em; 89 | } 90 | .jumbotron { 91 | margin-bottom: 0; 92 | } 93 | #start form { 94 | border: none; 95 | } 96 | /* Bookmarklet popup */ 97 | #bookmarklet { 98 | display: none; 99 | position: fixed; 100 | top: 15px; 101 | right: 100px; 102 | z-index: 9999; 103 | padding: 10px; 104 | margin: 0; 105 | background: white; 106 | border: 1px solid #ddd; 107 | border-radius: 10px; 108 | } 109 | #form-bookmarklet .checkbox label { 110 | font-weight: 700; 111 | } 112 | #help-bookmarklet { 113 | display: none; 114 | margin-top: 10px; 115 | margin-bottom: 30px; 116 | background: #eef; 117 | padding: 1em 1em 0.5em 1em; 118 | } 119 | .wedge-up { 120 | display: block; 121 | width: 15px; 122 | height: 15px; 123 | border-top: 15px solid transparent; 124 | border-left: 15px solid white; 125 | position: absolute; 126 | margin-top: -25px; 127 | } 128 | .modal-backdrop.in { 129 | opacity: .8; 130 | filter: alpha(opacity=80); 131 | } 132 | /* Activity map */ 133 | #world, 134 | #map { 135 | height: 500px; 136 | width: 100%; 137 | margin-top: -20px; 138 | } 139 | #world .underlay { 140 | display: none; 141 | position: absolute; 142 | left: 0px; 143 | bottom: 0px; 144 | width: 84%; 145 | margin: 0 8%; 146 | padding: 1em 0; 147 | z-index: 99; 148 | background: #ffc425; 149 | } 150 | #world .underlay .lead { 151 | background: #fff; 152 | text-align: center; 153 | margin-bottom: 10px; 154 | } 155 | #world .underlay p { 156 | padding: 0 1em; 157 | font-size: 110%; 158 | } 159 | #counter { 160 | font-weight: bold; 161 | font-size: 120%; 162 | } 163 | .thumbnail .caption { 164 | padding: 0px; 165 | } 166 | .thumbnail .caption p { 167 | font-size: 95%; 168 | } 169 | /* Content areas */ 170 | h2 { 171 | color: #494949; 172 | font-size: 40px; 173 | text-align: center; 174 | padding-top: 0; 175 | margin-top: 0; 176 | margin-bottom: 15px; 177 | } 178 | h3 { 179 | color: #555; 180 | font-size: 19px; 181 | text-align: center; 182 | padding-top: 15px; 183 | margin-top: 0; 184 | margin-bottom: 1em; 185 | } 186 | h3.overline { 187 | border-top: 1px solid #aaa; 188 | } 189 | a[data-toggle] { 190 | cursor: pointer; 191 | } 192 | #intro, 193 | #contribute, 194 | #thanks { 195 | padding: 2em 3em; 196 | } 197 | #intro p, 198 | #contribute p, 199 | #thanks p { 200 | line-height: 1.7; 201 | } 202 | #intro .row, 203 | #contribute .row, 204 | #thanks .row { 205 | border-left: 7px solid transparent; 206 | border-right: 7px solid transparent; 207 | padding: 0 1.5em; 208 | } 209 | #intro .row.hilite-left, 210 | #contribute .row.hilite-left, 211 | #thanks .row.hilite-left { 212 | border-left: 7px solid #ef5b23; 213 | } 214 | #intro .row.hilite-right, 215 | #contribute .row.hilite-right, 216 | #thanks .row.hilite-right { 217 | border-right: 7px solid #ef5b23; 218 | } 219 | /* Container colors */ 220 | #thanks { 221 | background: #ffd; 222 | } 223 | #intro, 224 | #footer { 225 | background: #eee; 226 | } 227 | .buttons { 228 | margin-top: 2em; 229 | text-align: center; 230 | } 231 | .buttons a { 232 | text-decoration: none !important; 233 | } 234 | .buttons button { 235 | font-size: 20px; 236 | padding: 4px 10px; 237 | border-color: #444; 238 | color: #000; 239 | margin: 0 20px; 240 | } 241 | #contribute { 242 | background: #fff; 243 | } 244 | #thanks .team ul { 245 | text-align: left; 246 | list-style: none; 247 | } 248 | #thanks .team .thumbnails li { 249 | display: inline-block; 250 | margin: 0 2px; 251 | } 252 | #thanks .team .thumbnails .wedge-up { 253 | border-left-color: #ef5b23; 254 | margin-top: -19px; 255 | } 256 | #thanks .team .thumbnails .thumb { 257 | width: 48px; 258 | height: 48px; 259 | overflow: hidden; 260 | display: inline-block; 261 | } 262 | #thanks .team .thumbnails .thumb img { 263 | width: 100%; 264 | } 265 | #thanks .team .thumbnails li:hover .thumb, 266 | #thanks .team .thumbnails li.hover .thumb { 267 | opacity: 0.1; 268 | } 269 | #thanks .team .thumbnails li.right .caption { 270 | margin-left: -352px; 271 | } 272 | #thanks .team .thumbnails li.right .wedge-up { 273 | margin-left: 372px; 274 | border-left: none; 275 | border-right: 15px solid #ef5b23; 276 | } 277 | #thanks .team .thumbnails .caption { 278 | position: absolute; 279 | z-index: 99; 280 | margin: 0; 281 | width: 400px; 282 | height: 156px; 283 | max-width: 90%; 284 | padding: 4px; 285 | border: 2px solid #ef5b23; 286 | background: white; 287 | font-size: 20px; 288 | } 289 | #thanks .team .thumbnails .caption .name { 290 | font-size: 23px; 291 | font-weight: bold; 292 | } 293 | #thanks .team .thumbnails .caption p { 294 | margin-top: 4px; 295 | font-size: 15px; 296 | line-height: 1.2; 297 | } 298 | #thanks .team .thumbnails .caption .large { 299 | height: 144px; 300 | max-width: 144px; 301 | float: left; 302 | margin-right: 5px; 303 | overflow: hidden; 304 | } 305 | #thanks .team .thumbnails .caption .large img { 306 | height: 144px; 307 | } 308 | #thanks .team .thumbnails .caption .links { 309 | position: absolute; 310 | left: 144px; 311 | bottom: 5px; 312 | } 313 | #thanks .team .thumbnails .caption .web, 314 | #thanks .team .thumbnails .caption .twitter { 315 | display: inline-block; 316 | width: auto; 317 | height: 24px; 318 | font-size: 18px; 319 | margin-left: 10px; 320 | padding-left: 27px; 321 | background-size: 24px 24px; 322 | background-repeat: no-repeat; 323 | background-position: left; 324 | } 325 | #thanks .team .thumbnails .caption .web { 326 | background-image: url(../img/iconmonstr-globe-3.png); 327 | } 328 | #thanks .team .thumbnails .caption .twitter { 329 | background-image: url(../img/iconmonstr-twitter-4.png); 330 | } 331 | #thanks .team .thumbnails li .caption { 332 | display: none; 333 | } 334 | #thanks .team .thumbnails li:hover .caption, 335 | #thanks .team .thumbnails li.hover .caption { 336 | display: block; 337 | } 338 | #thanks .orgs li { 339 | width: 25%; 340 | float: left; 341 | } 342 | #thanks .orgs p { 343 | display: block; 344 | clear: both; 345 | font-size: 15px; 346 | padding-top: 1em; 347 | text-align: center; 348 | } 349 | #footer { 350 | padding-top: 2em; 351 | } 352 | #footer .icon { 353 | height: 32px; 354 | width: 32px; 355 | display: inline-block; 356 | background-repeat: no-repeat; 357 | background-position: 50% 50%; 358 | background-size: 100%; 359 | position: relative; 360 | font-size: 0px; 361 | text-indent: -9999px; 362 | margin: 0 25px 0 0; 363 | } 364 | #footer .icon:hover { 365 | opacity: 0.5; 366 | } 367 | #footer .twitter { 368 | background-image: url(../img/webicon-twitter.png); 369 | } 370 | #footer .facebook { 371 | background-image: url(../img/webicon-facebook.png); 372 | } 373 | #footer .wordpress { 374 | background-image: url(../img/webicon-wordpress.png); 375 | } 376 | .license { 377 | font-size: 90%; 378 | } 379 | .license img { 380 | float: left; 381 | padding-top: 1.5em; 382 | padding-right: 1em; 383 | } 384 | /* Responsive styles */ 385 | @media screen and (max-width: 768px) { 386 | body { 387 | padding-top: 0px; 388 | } 389 | header a { 390 | background-size: 100%; 391 | } 392 | header .lead { 393 | font-size: 15px; 394 | position: relative; 395 | } 396 | } 397 | @media screen and (max-width: 520px) { 398 | header a { 399 | height: 110px; 400 | position: absolute; 401 | background-image: url(../img/header_needaccess.png); 402 | } 403 | header .lead { 404 | visibility: hidden; 405 | } 406 | #github_ribbon { 407 | display: none; 408 | } 409 | } 410 | .form-bookmarklet { 411 | /* max-width: 300px; */ 412 | 413 | padding: 19px 29px 29px; 414 | margin: 0 auto 20px; 415 | background-color: #fff; 416 | border: 1px solid #e5e5e5; 417 | -webkit-border-radius: 5px; 418 | -moz-border-radius: 5px; 419 | border-radius: 5px; 420 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 421 | -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 422 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 423 | } 424 | .form-bookmarklet .form-bookmarklet-heading, 425 | .form-bookmarklet .checkbox { 426 | margin-bottom: 10px; 427 | } 428 | .form-bookmarklet input[type="text"], 429 | .form-bookmarklet input[type="password"], 430 | .form-bookmarklet select { 431 | font-size: 16px; 432 | height: auto; 433 | margin-bottom: 15px; 434 | padding: 7px 9px; 435 | width: 100%; 436 | } 437 | .form-bookmarklet button { 438 | font-size: 16px; 439 | height: auto; 440 | margin-bottom: 15px; 441 | padding: 7px 9px; 442 | clear: both; 443 | width: 100%; 444 | } 445 | -------------------------------------------------------------------------------- /oabutton/common/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: latin-1 2 | from django import forms 3 | 4 | 5 | class SigninForm(forms.Form): 6 | PROFESSION_CHOICES = (('Student', 'Student'), 7 | ('Doctor', 'Doctor'), 8 | ('Patient', 'Patient'), 9 | ('Advocate', 'Advocate'), 10 | ('Academic', 'Academic'), 11 | ('Researcher', 'Researcher'), 12 | ('Librarian', 'Librarian'), 13 | ('Other', 'Other'), 14 | ('Undisclosed', 'Prefer not to say')) 15 | 16 | email = forms.EmailField(required=True, 17 | label='Email address', 18 | widget=forms.TextInput(attrs={'placeholder': 'Email address (required)'})) 19 | 20 | name = forms.CharField(required=True, 21 | label="Name", 22 | widget=forms.TextInput(attrs={'placeholder': 'Name (required, preferably full name)'})) 23 | 24 | profession = forms.ChoiceField(required=True, 25 | label="Profession", 26 | choices=PROFESSION_CHOICES) 27 | 28 | confirm_public = forms.BooleanField(label="I understand that information obtained by this button will be publicly accessible", required=True) 29 | 30 | mailinglist = forms.BooleanField(label="I would like to be added to the Open Access Button Mailing List", required=False, initial=True) 31 | 32 | 33 | class Bookmarklet(forms.Form): 34 | user_id = forms.CharField(widget=forms.HiddenInput, required=True) 35 | accessed = forms.CharField(widget=forms.HiddenInput, required=False) 36 | coords = forms.CharField(widget=forms.HiddenInput, required=False) 37 | 38 | location = forms.CharField(required=False, 39 | label="Location", 40 | widget=forms.TextInput(attrs={ 41 | 'placeholder': "e.g. London, United Kingdom", 42 | 'class': "form-control input-block-level", 43 | 'required': True})) 44 | 45 | doi = forms.CharField(required=False, label="Digital Object Identifier (DOI)", 46 | widget=forms.TextInput(attrs={'class': "form-control input-block-level"}, 47 | )) 48 | 49 | url = forms.URLField(required=True, label='Article URL', 50 | widget=forms.TextInput(attrs={'placeholder': 51 | "http://www.publisher.com/journal?id=XXXX", 'class': "form-control input-block-level"})) 52 | 53 | story = forms.CharField(required=False, label="Why do you need access?", 54 | widget=forms.Textarea(attrs={'rows': "4", 55 | 'placeholder': "e.g. I'm trying to save lives, dammit!", 56 | 'class': "form-control input-block-level"})) 57 | 58 | description = forms.CharField(required=False, label="Description", 59 | widget=forms.Textarea(attrs={'placeholder': 'Title, Authors, Journal', 'data-remember': "data-remember", 60 | 'rows': '4', 'class': "form-control input-block-level"})) 61 | 62 | teamdata = [{"name": "Joseph McArthur", 63 | "link": "http://twitter.com/Mcarthur_Joe", 64 | "thumb": "/static/img/photo-joe.jpg", 65 | "twitter": "Mcarthur_Joe", 66 | "blurb": "Founder, project lead and apparently also a pharmacology student" 67 | }, { 68 | "name": "David Carroll", 69 | "link": "http://twitter.com/davidecarroll", 70 | "thumb": "/static/img/photo-david.jpg", 71 | "twitter": "davidecarroll", 72 | "blurb": "Founder, medical student and twitter addict" 73 | }, { 74 | "name": "Nicholas Ng", 75 | "link": "http://twitter.com/nicholascwng", 76 | "thumb": "https://secure.gravatar.com/avatar/9637895e310caf25237e89155157b2a7?s=144", 77 | "twitter": "nicholascwng", 78 | "blurb": "Awesome developer from the BMJ Hackday who was instrumental in getting us 3rd place" 79 | }, { 80 | "name": "Andy Lulham", 81 | "link": "http://twitter.com/andylolz", 82 | "thumb": "https://1.gravatar.com/avatar/bbb9eb1af3b427f8259df33f6e8844aa?s=144", 83 | "twitter": "andylolz", 84 | "blurb": "Lead developer and central to the project from the first second at the BMJ hackday" 85 | }, { 86 | "name": "Florian Rathgeber", 87 | "link": "http://twitter.com/frathgeber", 88 | "thumb": "https://lh5.googleusercontent.com/-TQyLDuEMj1E/Umm1CqZohdI/AAAAAAAAZAA/PdCXgUG5d0Y/s144-no", 89 | "twitter": "frathgeber", 90 | "blurb": "Joined the project at the BMJ hackday and helped throughout providing solutions to our many problems" 91 | }, { 92 | "name": "Jez Cope", 93 | "link": "#", 94 | "thumb": "https://2.gravatar.com/avatar/6c1c67fa04add9cada803f13ae2ff050?s=144", 95 | "blurb": "Lead Developer who designed and implemented a way for us to link papers behind paywalls to available copies" 96 | }, { 97 | "name": "Alf Eaton", 98 | "link": "http://twitter.com/invisiblecomma", 99 | "thumb": "https://lh3.googleusercontent.com/-EHMuXvuZH1k/AAAAAAAAAAI/AAAAAAAAFeE/Z93Ix4IW-BY/s120-c/photo.jpg", 100 | "twitter": "invisiblecomma", 101 | "blurb": "Developer at PeerJ who lended a hand at several points" 102 | }, { 103 | "name": "Ayesha Garrett", 104 | "link": "http://londonlime.net/", 105 | "thumb": "http://behance.vo.llnwd.net/profiles17/614780/851b0bf48a4eb7968873cfa622662c58.jpg", 106 | "twitter": "londonlime", 107 | "blurb": "Helped design much of the imaging around the project" 108 | }, { 109 | "name": "Victor Ng", 110 | "link": "#", 111 | "thumb": "http://www.gravatar.com/avatar/c5ab90719801dfa47055338ba47f5580.png?s=144", 112 | "blurb": "One of our best developers, instrumental in bring it to completion. Elsevier's biggest fan" 113 | }, { 114 | "name": "Oleg Lavrovsky", 115 | "link": "http://utou.ch", 116 | "thumb": "https://pbs.twimg.com/profile_images/378800000404422761/3bde338af1f5fcdbe24dda38ed36b9d1.jpeg", 117 | "twitter": "loleg", 118 | "blurb": "Lead designer and web developer, also creator of an awesome spin off, the Open Data Button" 119 | }, { 120 | "name": "Tom Pollard", 121 | "link": "https://twitter.com/tompollard", 122 | "thumb": "http://www.ucl.ac.uk/mssl/space-medicine/people/TomPollardPhoto", 123 | "twitter": "tompollard", 124 | "blurb": "Developer. PhD student in University College London. Co-founder of Ubiquity Press" 125 | }, { 126 | "name": "Martin Paul Eve", 127 | "link": "#", 128 | "thumb": "https://0.gravatar.com/avatar/3645eb2857da226a58ca23a582a0b859?s=144", 129 | "blurb": "Developer. Lecturer in English Literature. Director of Open Library of Humanities" 130 | }, { 131 | "name": "Emanuil Tolev", 132 | "link": "#", 133 | "thumb": "https://pbs.twimg.com/profile_images/378800000606651539/5651bffd1905807bcdd4b619d0a7e13d.jpeg", 134 | "blurb": "Developer. Computer Science Student. Associate at Cottage Labs" 135 | }, { 136 | "name": "Cameron Stocks", 137 | "link": "http://twitter.com/cam_stocks", 138 | "thumb": "https://pbs.twimg.com/profile_images/3666850209/e8c8b7281daccb2fcccae1f538c6d82a_bigger.jpeg", 139 | "twitter": "cam_stocks", 140 | "blurb": "National Director of Medsin, the UK’s student global health network, and founder" 141 | }, { 142 | "name": "Nicole Allen", 143 | "link": "https://twitter.com/txtbks", 144 | "thumb": "https://pbs.twimg.com/profile_images/3151008497/5c42187852b2ef7e5492a6f6e792a21b.jpeg", 145 | "twitter": "txtbks", 146 | "blurb": "Open Educational Resources Program Director at SPARC who helped with training and project planning" 147 | }, { 148 | "name": "Nicholas Shockey", 149 | "link": "#", 150 | "thumb": "http://conf11.freeculture.org/files/2011/01/Nick-Shockey1.jpg", 151 | "blurb": "Director of the Right to Research Coalition and of Student Advocacy at SPARC, who provided invaluable input at all stages" 152 | }, { 153 | "name": "Medsin", 154 | "link": "http://www.medsin.org/", 155 | "thumb": "/static/img/logo-medsin.jpg", 156 | "twitter": "medsin", 157 | "blurb": "The Open Access Button is a Medsin programme" 158 | }, { 159 | "name": "Right to Research Coalition", 160 | "link": "http://www.righttoresearch.org/", 161 | "thumb": "http://www.righttoresearch.org/bm~pix/r2r-logo.png", 162 | "twitter": "R2RC", 163 | "blurb": "An invaluable source of support, ideas and money" 164 | } 165 | ] 166 | 167 | thanksdata = [{ 168 | "link": "http://opensciencefederation.com/", 169 | "name": "Open Science Federation" 170 | },{ 171 | "link": "http://originalcontentlondon.com/", 172 | "name": "Original Content London" 173 | },{ 174 | "link": "https://twitter.com/McDawg", 175 | "name": "Graham Steel" 176 | },{ 177 | "link": "http://okfn.org/", 178 | "name": "Open Knowledge Foundation" 179 | },{ 180 | "link": "http://rewiredstate.org/", 181 | "name": "Rewired State" 182 | },{ 183 | "link": "http://www.bmj.com/", 184 | "name": "BMJ" 185 | },{ 186 | "link": "http://www.plos.org/", 187 | "name": "PLOS" 188 | },{ 189 | "link": "https://twitter.com/Protohedgehog", 190 | "name": "Jon Tennant" 191 | },{ 192 | "link": "https://twitter.com/petermurrayrust", 193 | "name": "Peter Murray-Rust" 194 | },{ 195 | "link": "http://sparc.arl.org/", 196 | "name": "SPARC" 197 | },{ 198 | "link": "https://twitter.com/CameronNeylon", 199 | "name": "Cameron Neylon" 200 | },{ 201 | "link": "https://twitter.com/evomri", 202 | "name": "Daniel Mietchen" 203 | }] 204 | -------------------------------------------------------------------------------- /oabutton/static/public/css/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-map-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-pane, 8 | .leaflet-tile-container, 9 | .leaflet-overlay-pane, 10 | .leaflet-shadow-pane, 11 | .leaflet-marker-pane, 12 | .leaflet-popup-pane, 13 | .leaflet-overlay-pane svg, 14 | .leaflet-zoom-box, 15 | .leaflet-image-layer, 16 | .leaflet-layer { 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | } 21 | .leaflet-container { 22 | overflow: hidden; 23 | -ms-touch-action: none; 24 | } 25 | .leaflet-tile, 26 | .leaflet-marker-icon, 27 | .leaflet-marker-shadow { 28 | -webkit-user-select: none; 29 | -moz-user-select: none; 30 | user-select: none; 31 | -webkit-user-drag: none; 32 | } 33 | .leaflet-marker-icon, 34 | .leaflet-marker-shadow { 35 | display: block; 36 | } 37 | /* map is broken in FF if you have max-width: 100% on tiles */ 38 | .leaflet-container img { 39 | max-width: none !important; 40 | } 41 | /* stupid Android 2 doesn't understand "max-width: none" properly */ 42 | .leaflet-container img.leaflet-image-layer { 43 | max-width: 15000px !important; 44 | } 45 | .leaflet-tile { 46 | filter: inherit; 47 | visibility: hidden; 48 | } 49 | .leaflet-tile-loaded { 50 | visibility: inherit; 51 | } 52 | .leaflet-zoom-box { 53 | width: 0; 54 | height: 0; 55 | } 56 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 57 | .leaflet-overlay-pane svg { 58 | -moz-user-select: none; 59 | } 60 | 61 | .leaflet-tile-pane { z-index: 2; } 62 | .leaflet-objects-pane { z-index: 3; } 63 | .leaflet-overlay-pane { z-index: 4; } 64 | .leaflet-shadow-pane { z-index: 5; } 65 | .leaflet-marker-pane { z-index: 6; } 66 | .leaflet-popup-pane { z-index: 7; } 67 | 68 | 69 | /* control positioning */ 70 | 71 | .leaflet-control { 72 | position: relative; 73 | z-index: 7; 74 | pointer-events: auto; 75 | } 76 | .leaflet-top, 77 | .leaflet-bottom { 78 | position: absolute; 79 | z-index: 1000; 80 | pointer-events: none; 81 | } 82 | .leaflet-top { 83 | top: 0; 84 | } 85 | .leaflet-right { 86 | right: 0; 87 | } 88 | .leaflet-bottom { 89 | bottom: 0; 90 | } 91 | .leaflet-left { 92 | left: 0; 93 | } 94 | .leaflet-control { 95 | float: left; 96 | clear: both; 97 | } 98 | .leaflet-right .leaflet-control { 99 | float: right; 100 | } 101 | .leaflet-top .leaflet-control { 102 | margin-top: 10px; 103 | } 104 | .leaflet-bottom .leaflet-control { 105 | margin-bottom: 10px; 106 | } 107 | .leaflet-left .leaflet-control { 108 | margin-left: 10px; 109 | } 110 | .leaflet-right .leaflet-control { 111 | margin-right: 10px; 112 | } 113 | 114 | 115 | /* zoom and fade animations */ 116 | 117 | .leaflet-fade-anim .leaflet-tile, 118 | .leaflet-fade-anim .leaflet-popup { 119 | opacity: 0; 120 | -webkit-transition: opacity 0.2s linear; 121 | -moz-transition: opacity 0.2s linear; 122 | -o-transition: opacity 0.2s linear; 123 | transition: opacity 0.2s linear; 124 | } 125 | .leaflet-fade-anim .leaflet-tile-loaded, 126 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 127 | opacity: 1; 128 | } 129 | 130 | .leaflet-zoom-anim .leaflet-zoom-animated { 131 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 132 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 133 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); 134 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 135 | } 136 | .leaflet-zoom-anim .leaflet-tile, 137 | .leaflet-pan-anim .leaflet-tile, 138 | .leaflet-touching .leaflet-zoom-animated { 139 | -webkit-transition: none; 140 | -moz-transition: none; 141 | -o-transition: none; 142 | transition: none; 143 | } 144 | 145 | .leaflet-zoom-anim .leaflet-zoom-hide { 146 | visibility: hidden; 147 | } 148 | 149 | 150 | /* cursors */ 151 | 152 | .leaflet-clickable { 153 | cursor: pointer; 154 | } 155 | .leaflet-container { 156 | cursor: -webkit-grab; 157 | cursor: -moz-grab; 158 | } 159 | .leaflet-popup-pane, 160 | .leaflet-control { 161 | cursor: auto; 162 | } 163 | .leaflet-dragging, 164 | .leaflet-dragging .leaflet-clickable, 165 | .leaflet-dragging .leaflet-container { 166 | cursor: move; 167 | cursor: -webkit-grabbing; 168 | cursor: -moz-grabbing; 169 | } 170 | 171 | 172 | /* visual tweaks */ 173 | 174 | .leaflet-container { 175 | background: #ddd; 176 | outline: 0; 177 | } 178 | .leaflet-container a { 179 | color: #0078A8; 180 | } 181 | .leaflet-container a.leaflet-active { 182 | outline: 2px solid orange; 183 | } 184 | .leaflet-zoom-box { 185 | border: 2px dotted #05f; 186 | background: white; 187 | opacity: 0.5; 188 | } 189 | 190 | 191 | /* general typography */ 192 | .leaflet-container { 193 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 194 | } 195 | 196 | 197 | /* general toolbar styles */ 198 | 199 | .leaflet-bar { 200 | box-shadow: 0 1px 7px rgba(0,0,0,0.65); 201 | -webkit-border-radius: 4px; 202 | border-radius: 4px; 203 | } 204 | .leaflet-bar a, .leaflet-bar a:hover { 205 | background-color: #fff; 206 | border-bottom: 1px solid #ccc; 207 | width: 26px; 208 | height: 26px; 209 | line-height: 26px; 210 | display: block; 211 | text-align: center; 212 | text-decoration: none; 213 | color: black; 214 | } 215 | .leaflet-bar a, 216 | .leaflet-control-layers-toggle { 217 | background-position: 50% 50%; 218 | background-repeat: no-repeat; 219 | display: block; 220 | } 221 | .leaflet-bar a:hover { 222 | background-color: #f4f4f4; 223 | } 224 | .leaflet-bar a:first-child { 225 | -webkit-border-top-left-radius: 4px; 226 | border-top-left-radius: 4px; 227 | -webkit-border-top-right-radius: 4px; 228 | border-top-right-radius: 4px; 229 | } 230 | .leaflet-bar a:last-child { 231 | -webkit-border-bottom-left-radius: 4px; 232 | border-bottom-left-radius: 4px; 233 | -webkit-border-bottom-right-radius: 4px; 234 | border-bottom-right-radius: 4px; 235 | border-bottom: none; 236 | } 237 | .leaflet-bar a.leaflet-disabled { 238 | cursor: default; 239 | background-color: #f4f4f4; 240 | color: #bbb; 241 | } 242 | 243 | .leaflet-touch .leaflet-bar { 244 | -webkit-border-radius: 10px; 245 | border-radius: 10px; 246 | } 247 | .leaflet-touch .leaflet-bar a { 248 | width: 30px; 249 | height: 30px; 250 | } 251 | .leaflet-touch .leaflet-bar a:first-child { 252 | -webkit-border-top-left-radius: 7px; 253 | border-top-left-radius: 7px; 254 | -webkit-border-top-right-radius: 7px; 255 | border-top-right-radius: 7px; 256 | } 257 | .leaflet-touch .leaflet-bar a:last-child { 258 | -webkit-border-bottom-left-radius: 7px; 259 | border-bottom-left-radius: 7px; 260 | -webkit-border-bottom-right-radius: 7px; 261 | border-bottom-right-radius: 7px; 262 | border-bottom: none; 263 | } 264 | 265 | 266 | /* zoom control */ 267 | 268 | .leaflet-control-zoom-in { 269 | font: bold 18px 'Lucida Console', Monaco, monospace; 270 | } 271 | .leaflet-control-zoom-out { 272 | font: bold 22px 'Lucida Console', Monaco, monospace; 273 | } 274 | 275 | .leaflet-touch .leaflet-control-zoom-in { 276 | font-size: 22px; 277 | line-height: 30px; 278 | } 279 | .leaflet-touch .leaflet-control-zoom-out { 280 | font-size: 28px; 281 | line-height: 30px; 282 | } 283 | 284 | 285 | /* layers control */ 286 | 287 | .leaflet-control-layers { 288 | box-shadow: 0 1px 7px rgba(0,0,0,0.4); 289 | background: #f8f8f9; 290 | -webkit-border-radius: 5px; 291 | border-radius: 5px; 292 | } 293 | .leaflet-control-layers-toggle { 294 | background-image: url(images/layers.png); 295 | width: 36px; 296 | height: 36px; 297 | } 298 | .leaflet-retina .leaflet-control-layers-toggle { 299 | background-image: url(images/layers-2x.png); 300 | background-size: 26px 26px; 301 | } 302 | .leaflet-touch .leaflet-control-layers-toggle { 303 | width: 44px; 304 | height: 44px; 305 | } 306 | .leaflet-control-layers .leaflet-control-layers-list, 307 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 308 | display: none; 309 | } 310 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 311 | display: block; 312 | position: relative; 313 | } 314 | .leaflet-control-layers-expanded { 315 | padding: 6px 10px 6px 6px; 316 | color: #333; 317 | background: #fff; 318 | } 319 | .leaflet-control-layers-selector { 320 | margin-top: 2px; 321 | position: relative; 322 | top: 1px; 323 | } 324 | .leaflet-control-layers label { 325 | display: block; 326 | } 327 | .leaflet-control-layers-separator { 328 | height: 0; 329 | border-top: 1px solid #ddd; 330 | margin: 5px -10px 5px -6px; 331 | } 332 | 333 | 334 | /* attribution and scale controls */ 335 | 336 | .leaflet-container .leaflet-control-attribution { 337 | background-color: rgba(255, 255, 255, 0.7); 338 | box-shadow: 0 0 5px #bbb; 339 | margin: 0; 340 | } 341 | .leaflet-control-attribution, 342 | .leaflet-control-scale-line { 343 | padding: 0 5px; 344 | color: #333; 345 | } 346 | .leaflet-container .leaflet-control-attribution, 347 | .leaflet-container .leaflet-control-scale { 348 | font-size: 11px; 349 | } 350 | .leaflet-left .leaflet-control-scale { 351 | margin-left: 5px; 352 | } 353 | .leaflet-bottom .leaflet-control-scale { 354 | margin-bottom: 5px; 355 | } 356 | .leaflet-control-scale-line { 357 | border: 2px solid #777; 358 | border-top: none; 359 | color: black; 360 | line-height: 1.1; 361 | padding: 2px 5px 1px; 362 | font-size: 11px; 363 | text-shadow: 1px 1px 1px #fff; 364 | background-color: rgba(255, 255, 255, 0.5); 365 | box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2); 366 | white-space: nowrap; 367 | overflow: hidden; 368 | } 369 | .leaflet-control-scale-line:not(:first-child) { 370 | border-top: 2px solid #777; 371 | border-bottom: none; 372 | margin-top: -2px; 373 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 374 | } 375 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 376 | border-bottom: 2px solid #777; 377 | } 378 | 379 | .leaflet-touch .leaflet-control-attribution, 380 | .leaflet-touch .leaflet-control-layers, 381 | .leaflet-touch .leaflet-bar { 382 | box-shadow: none; 383 | } 384 | .leaflet-touch .leaflet-control-layers, 385 | .leaflet-touch .leaflet-bar { 386 | border: 4px solid rgba(0,0,0,0.3); 387 | } 388 | 389 | 390 | /* popup */ 391 | 392 | .leaflet-popup { 393 | position: absolute; 394 | text-align: center; 395 | } 396 | .leaflet-popup-content-wrapper { 397 | padding: 1px; 398 | text-align: left; 399 | -webkit-border-radius: 12px; 400 | border-radius: 12px; 401 | } 402 | .leaflet-popup-content { 403 | margin: 13px 19px; 404 | line-height: 1.4; 405 | } 406 | .leaflet-popup-content p { 407 | margin: 18px 0; 408 | } 409 | .leaflet-popup-tip-container { 410 | margin: 0 auto; 411 | width: 40px; 412 | height: 20px; 413 | position: relative; 414 | overflow: hidden; 415 | } 416 | .leaflet-popup-tip { 417 | width: 17px; 418 | height: 17px; 419 | padding: 1px; 420 | 421 | margin: -10px auto 0; 422 | 423 | -webkit-transform: rotate(45deg); 424 | -moz-transform: rotate(45deg); 425 | -ms-transform: rotate(45deg); 426 | -o-transform: rotate(45deg); 427 | transform: rotate(45deg); 428 | } 429 | .leaflet-popup-content-wrapper, .leaflet-popup-tip { 430 | background: white; 431 | 432 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 433 | } 434 | .leaflet-container a.leaflet-popup-close-button { 435 | position: absolute; 436 | top: 0; 437 | right: 0; 438 | padding: 4px 4px 0 0; 439 | text-align: center; 440 | width: 18px; 441 | height: 14px; 442 | font: 16px/14px Tahoma, Verdana, sans-serif; 443 | color: #c3c3c3; 444 | text-decoration: none; 445 | font-weight: bold; 446 | background: transparent; 447 | } 448 | .leaflet-container a.leaflet-popup-close-button:hover { 449 | color: #999; 450 | } 451 | .leaflet-popup-scrolled { 452 | overflow: auto; 453 | border-bottom: 1px solid #ddd; 454 | border-top: 1px solid #ddd; 455 | } 456 | 457 | 458 | /* div icon */ 459 | 460 | .leaflet-div-icon { 461 | background: #fff; 462 | border: 1px solid #666; 463 | } 464 | .leaflet-editing-icon { 465 | -webkit-border-radius: 2px; 466 | border-radius: 2px; 467 | } 468 | -------------------------------------------------------------------------------- /oabutton/static/public/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,0%,#e6e6e6,100%);background-image:-moz-linear-gradient(top,#fff 0,#e6e6e6 100%);background-image:linear-gradient(to bottom,#fff 0,#e6e6e6 100%);background-repeat:repeat-x;border-color:#e0e0e0;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0)}.btn-default:active,.btn-default.active{background-color:#e6e6e6;border-color:#e0e0e0}.btn-primary{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;border-color:#2d6ca2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.btn-primary:active,.btn-primary.active{background-color:#3071a9;border-color:#2d6ca2}.btn-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;border-color:#419641;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.btn-success:active,.btn-success.active{background-color:#449d44;border-color:#419641}.btn-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;border-color:#eb9316;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.btn-warning:active,.btn-warning.active{background-color:#ec971f;border-color:#eb9316}.btn-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;border-color:#c12e2a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.btn-danger:active,.btn-danger.active{background-color:#c9302c;border-color:#c12e2a}.btn-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;border-color:#2aabd2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.btn-info:active,.btn-info.active{background-color:#31b0d5;border-color:#2aabd2}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#f8f8f8));background-image:-webkit-linear-gradient(top,#fff,0%,#f8f8f8,100%);background-image:-moz-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar .navbar-nav>.active>a{background-color:#f8f8f8}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-gradient(linear,left 0,left 100%,from(#3c3c3c),to(#222));background-image:-webkit-linear-gradient(top,#3c3c3c,0%,#222,100%);background-image:-moz-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0)}.navbar-inverse .navbar-nav>.active>a{background-color:#222}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#c8e5bc));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#c8e5bc,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#b9def0,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#f8efc0));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#f8efc0,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#e7c3c3));background-image:-webkit-linear-gradient(top,#f2dede,0%,#e7c3c3,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#ebebeb,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3278b3));background-image:-webkit-linear-gradient(top,#428bca,0%,#3278b3,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5,0%,#e8e8e8,100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#d0e9c6));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#d0e9c6,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#c4e3f3));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#c4e3f3,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#faf2cc));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#faf2cc,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#ebcccc));background-image:-webkit-linear-gradient(top,#f2dede,0%,#ebcccc,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-gradient(linear,left 0,left 100%,from(#e8e8e8),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#e8e8e8,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} --------------------------------------------------------------------------------